diff --git a/BUILD.gn b/BUILD.gn
index 6eefc1b..549429e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -101,7 +101,6 @@
       "//chrome",
       "//chrome/installer/zucchini:zucchini",
       "//chrome/installer/zucchini:zucchini_unittests",
-      "//chrome/profiling:unit_tests",
       "//chrome/test:browser_tests",
       "//chrome/test:interactive_ui_tests",
       "//chrome/test:sync_integration_tests",
@@ -714,6 +713,10 @@
       deps += [ "//tools/perf/contrib/vr_benchmarks:vr_perf_tests" ]
     }
   }
+
+  if (is_fuchsia) {
+    deps += [ "//headless" ]
+  }
 }
 
 # TODO(GYP_GONE): This target exists for compatibility with GYP, specifically
diff --git a/DEPS b/DEPS
index dbf21c3..2bb9e459 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,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': '32fa5104148722dbd6aac1ce8d38dcf47737f669',
+  'skia_revision': '13544dffb9f700c1c387a90ae78a8b2c5b9ed061',
   # 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': 'f1317ecc526a2026e55c9bd6c532c3046defdeaa',
+  'v8_revision': '07ee877db98940f66f9625ca1c6215c19450aa0b',
   # 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.
@@ -64,7 +64,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': 'd27998f6526272a5b8732106aa9b75f724434aca',
+  'pdfium_revision': 'd3002340caad8d688d0e4928a36e90d354a797bd',
   # 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.
@@ -96,7 +96,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': '0eeb5baed7a4417ac083221805842019185ccec0',
+  'catapult_revision': '5db51352aa5cca18e236d28ce85ba8b8a818fe12',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -362,7 +362,7 @@
       Var('chromium_git') + '/external/github.com/swisspol/GCDWebServer.git' + '@' + '43555c66627f6ed44817855a0f6d465f559d30e0',
 
     'src/ios/third_party/material_components_ios/src':
-      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'c2a357fb67321e9660190c55b49836c51fe04821',
+      Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'a0466df249c593ba65007440d7a84e4665ac2631',
 
     'src/ios/third_party/material_font_disk_loader_ios/src':
       Var('chromium_git') + '/external/github.com/material-foundation/material-font-disk-loader-ios.git' + '@' + '8e30188777b016182658fbaa0a4a020a48183224',
diff --git a/WATCHLISTS b/WATCHLISTS
index 5ee2978..19924ac 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -772,6 +772,9 @@
     'courgette': {
       'filepath': 'courgette/',
     },
+    'cq': {
+      'filepath': 'infra/config/cq.cfg',
+    },
     'cr_elements': {
       'filepath': 'ui/webui/resources/cr_element',
     },
@@ -920,9 +923,6 @@
     'gn': {
       'filepath': 'tools/gn',
     },
-    'goma': {
-      'filepath': 'infra/config/cq.cfg',
-    },
     'gpu': {
       'filepath': 'gpu/',
     },
@@ -1985,6 +1985,11 @@
                           'twellington+watch@chromium.org'],
     'courgette': ['huangs+watch@chromium.org',
                   'wfh+watch@chromium.org'],
+    'cq': ['cq-config-changes@chromium.org',
+           'shinyak+cc@chromium.org',
+           'tikuta+cc@chromium.org',
+           'ukai+cc@chromium.org',
+           'yyanagisawa+cc@chromium.org'],
     'cr_elements': ['michaelpg+watch-elements@chromium.org',
                     'stevenjb+watch-md-settings@chromium.org'],
     'custom_tabs': ['lizeb+watch-custom-tabs@chromium.org'],
@@ -2037,10 +2042,6 @@
     'gn': ['agrieve+watch@chromium.org',
            'dpranke@chromium.org',
            'tfarina@chromium.org'],
-    'goma': ['shinyak+cc@chromium.com',
-             'tikuta+cc@chromium.com',
-             'ukai+cc@chromium.com',
-             'yyanagisawa+cc@chromium.com'],
     'gpu': ['piman+watch@chromium.org'],
     'history_ui': ['pam+watch@chromium.org'],
     'hotword': ['rlp+watch@chromium.org'],
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index 7f05440..6418442 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -149,7 +149,7 @@
   }
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableWebViewFinch)) {
+          switches::kEnableWebViewVariations)) {
     AwMetricsServiceClient::GetOrCreateGUID();
   }
 
diff --git a/android_webview/browser/aw_contents_statics.cc b/android_webview/browser/aw_contents_statics.cc
index 43b71bd..be551515 100644
--- a/android_webview/browser/aw_contents_statics.cc
+++ b/android_webview/browser/aw_contents_statics.cc
@@ -13,6 +13,7 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
+#include "components/security_interstitials/core/urls.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/url_constants.h"
@@ -52,6 +53,15 @@
 }  // namespace
 
 // static
+ScopedJavaLocalRef<jstring> GetSafeBrowsingPrivacyPolicyUrl(
+    JNIEnv* env,
+    const JavaParamRef<jclass>&) {
+  // TODO(ntfschr): append the locale to this URL
+  return base::android::ConvertUTF8ToJavaString(
+      env, security_interstitials::kSafeBrowsingPrivacyPolicyUrl);
+}
+
+// static
 void ClearClientCertPreferences(JNIEnv* env,
                                 const JavaParamRef<jclass>&,
                                 const JavaParamRef<jobject>& callback) {
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index b8b55aa..1a7e8e7 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -125,10 +125,10 @@
   request_context_ = request_context;
   channel_ = GetChannelFromPackageName();
 
-  // If Finch is enabled for WebView the GUID will already have been read at
-  // startup
+  // If variations are enabled for WebView the GUID will already have been read
+  // at startup
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableWebViewFinch)) {
+          switches::kEnableWebViewVariations)) {
     InitializeWithGUID();
   } else {
     content::BrowserThread::PostTaskAndReply(
@@ -141,7 +141,8 @@
 
 void AwMetricsServiceClient::InitializeWithGUID() {
   // The guid must have already been initialized at this point, either
-  // synchronously or asynchronously depending on the kEnableWebViewFinch flag
+  // synchronously or asynchronously depending on the kEnableWebViewVariations
+  // flag
   DCHECK_EQ(g_client_id_guid.Get().length(), GUID_SIZE);
   pref_service_->SetString(metrics::prefs::kMetricsClientID,
                            g_client_id_guid.Get());
diff --git a/android_webview/common/aw_switches.cc b/android_webview/common/aw_switches.cc
index 555513c9f..a8d9e825 100644
--- a/android_webview/common/aw_switches.cc
+++ b/android_webview/common/aw_switches.cc
@@ -6,7 +6,7 @@
 
 namespace switches {
 
-const char kEnableWebViewFinch[] = "enable-webview-finch";
+const char kEnableWebViewVariations[] = "enable-webview-variations";
 const char kSyncOnDrawHardware[] = "sync-on-draw-hardware";
 const char kWebViewSandboxedRenderer[] = "webview-sandboxed-renderer";
 
diff --git a/android_webview/common/aw_switches.h b/android_webview/common/aw_switches.h
index c41a274..341712b 100644
--- a/android_webview/common/aw_switches.h
+++ b/android_webview/common/aw_switches.h
@@ -7,7 +7,7 @@
 
 namespace switches {
 
-extern const char kEnableWebViewFinch[];
+extern const char kEnableWebViewVariations[];
 extern const char kSyncOnDrawHardware[];
 extern const char kWebViewSandboxedRenderer[];
 extern const char kWebViewEnableSafeBrowsingSupport[];
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 6a5c5cc..c7eb4e9 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
@@ -583,6 +583,16 @@
                         AwContentsStatics.setSafeBrowsingWhitelist(urls, callback);
                     }
 
+                    /**
+                     * Returns a URL pointing to the privacy policy for Safe Browsing reporting.
+                     *
+                     * @return the url pointing to a privacy policy document which can be displayed
+                     * to users.
+                     */
+                    // TODO(ntfschr): add @Override once next android SDK rolls
+                    public Uri getSafeBrowsingPrivacyPolicyUrl() {
+                        return AwContentsStatics.getSafeBrowsingPrivacyPolicyUrl();
+                    }
                 };
             }
         }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
index e4e2605..f03750db 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
@@ -6,6 +6,7 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.net.Uri;
 import android.webkit.ValueCallback;
 
 import org.chromium.base.ThreadUtils;
@@ -154,6 +155,10 @@
         }
     }
 
+    public static Uri getSafeBrowsingPrivacyPolicyUrl() {
+        return Uri.parse(nativeGetSafeBrowsingPrivacyPolicyUrl());
+    }
+
     public static void setCheckClearTextPermitted(boolean permitted) {
         nativeSetCheckClearTextPermitted(permitted);
     }
@@ -176,6 +181,7 @@
     //--------------------------------------------------------------------------------------------
     //  Native methods
     //--------------------------------------------------------------------------------------------
+    private static native String nativeGetSafeBrowsingPrivacyPolicyUrl();
     private static native void nativeClearClientCertPreferences(Runnable callback);
     private static native String nativeGetUnreachableWebDataUrl();
     private static native String nativeGetProductVersion();
diff --git a/android_webview/javatests/AndroidManifest.xml b/android_webview/javatests/AndroidManifest.xml
index ed0ae66f..07a3566 100644
--- a/android_webview/javatests/AndroidManifest.xml
+++ b/android_webview/javatests/AndroidManifest.xml
@@ -8,6 +8,10 @@
     <instrumentation android:name="org.chromium.android_webview.test.AwInstrumentationTestRunner"
         android:targetPackage="org.chromium.android_webview.shell"
         android:label="Tests for org.chromium.android_webview"/>
+    <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
+        android:targetPackage="org.chromium.android_webview.shell"
+        chromium-junit4="true"
+        android:label="Tests for org.chromium.android_webview"/>
     <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
     <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
     <uses-permission android:name="android.permission.READ_LOGS"/>
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
index fdbc2148..187b398 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
@@ -85,9 +85,9 @@
                 getJavaScriptResultBodyTextContent(mAwContents, mContentsClient));
         assertEquals(LocaleUtils.getDefaultLocaleString(), acceptLanguages[0]);
 
-        String[] acceptLanguagesJs = getAcceptLanguages(
-                JSUtils.executeJavaScriptAndWaitForResult(
-                        this, mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
+        String[] acceptLanguagesJs =
+                getAcceptLanguages(JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(),
+                        mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
                         "navigator.languages.join(',')"));
         assertEquals(acceptLanguagesJs.length, acceptLanguages.length);
         for (int i = 0; i < acceptLanguagesJs.length; ++i) {
@@ -128,9 +128,10 @@
         assertEquals(
                 LocaleUtils.getDefaultLocaleListString(), TextUtils.join(",", acceptLanguages));
 
-        String[] acceptLanguagesJs = getAcceptLanguages(JSUtils.executeJavaScriptAndWaitForResult(
-                this, mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
-                "navigator.languages.join(',')"));
+        String[] acceptLanguagesJs =
+                getAcceptLanguages(JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(),
+                        mAwContents, mContentsClient.getOnEvaluateJavaScriptResultHelper(),
+                        "navigator.languages.join(',')"));
         assertEquals(acceptLanguagesJs.length, acceptLanguages.length);
         for (int i = 0; i < acceptLanguagesJs.length; ++i) {
             assertEquals(acceptLanguagesJs[i], acceptLanguages[i]);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
index 4ff65e9..84f74a24 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
@@ -126,7 +126,7 @@
     }
 
     @Override
-    protected TestDependencyFactory createTestDependencyFactory() {
+    public TestDependencyFactory createTestDependencyFactory() {
         return new TestDependencyFactory() {
             @Override
             public AwScrollOffsetManager createScrollOffsetManager(
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
index ad613bf..16b780d 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
@@ -70,7 +70,7 @@
     }
 
     @Override
-    protected TestDependencyFactory createTestDependencyFactory() {
+    public TestDependencyFactory createTestDependencyFactory() {
         return new TestDependencyFactory() {
             @Override
             public AwLayoutSizer createLayoutSizer() {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
new file mode 100644
index 0000000..d3bd9a5
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -0,0 +1,412 @@
+// 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.
+
+package org.chromium.android_webview.test;
+
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import org.chromium.android_webview.AwBrowserContext;
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwContentsClient;
+import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.test.AwTestBase.PopupInfo;
+import org.chromium.android_webview.test.AwTestBase.TestDependencyFactory;
+import org.chromium.android_webview.test.AwTestCommon.AwTestCommonCallback;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.InMemorySharedPreferences;
+import org.chromium.net.test.util.TestWebServer;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/** Custom ActivityTestRunner for WebView instrumentation tests */
+public class AwActivityTestRule
+        extends ActivityTestRule<AwTestRunnerActivity> implements AwTestCommonCallback {
+    public static final long WAIT_TIMEOUT_MS = scaleTimeout(15000);
+
+    public static final int CHECK_INTERVAL = 100;
+
+    private Description mCurrentTestDescription;
+
+    private final AwTestCommon mTestCommon;
+
+    public AwActivityTestRule() {
+        super(AwTestRunnerActivity.class);
+        mTestCommon = new AwTestCommon(this);
+    }
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        mCurrentTestDescription = description;
+        return super.apply(new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                setUp();
+                base.evaluate();
+            }
+        }, description);
+    }
+
+    public void setUp() throws Exception {
+        mTestCommon.setUp();
+    }
+
+    @Override
+    public AwBrowserContext createAwBrowserContextOnUiThread(
+            InMemorySharedPreferences prefs, Context appContext) {
+        return new AwBrowserContext(prefs, appContext);
+    }
+
+    @Override
+    public TestDependencyFactory createTestDependencyFactory() {
+        return new TestDependencyFactory();
+    }
+
+    /**
+     * Override this to return false if the test doesn't want to create an
+     * AwBrowserContext automatically.
+     */
+    @Override
+    public boolean needsAwBrowserContextCreated() {
+        return true;
+    }
+
+    /**
+     * Override this to return false if the test doesn't want the browser
+     * startup sequence to be run automatically.
+     *
+     * @return Whether the instrumentation test requires the browser process to
+     *         already be started.
+     */
+    @Override
+    public boolean needsBrowserProcessStarted() {
+        return true;
+    }
+
+    public void createAwBrowserContext() {
+        mTestCommon.createAwBrowserContext();
+    }
+
+    public void startBrowserProcess() throws Exception {
+        mTestCommon.startBrowserProcess();
+    }
+
+    /**
+     * Runs a {@link Callable} on the main thread, blocking until it is
+     * complete, and returns the result. Calls
+     * {@link Instrumentation#waitForIdleSync()} first to help avoid certain
+     * race conditions.
+     *
+     * @param <R> Type of result to return
+     */
+    public <R> R runTestOnUiThreadAndGetResult(Callable<R> callable) throws Exception {
+        return mTestCommon.runTestOnUiThreadAndGetResult(callable);
+    }
+
+    public void enableJavaScriptOnUiThread(final AwContents awContents) {
+        mTestCommon.enableJavaScriptOnUiThread(awContents);
+    }
+
+    public void setNetworkAvailableOnUiThread(
+            final AwContents awContents, final boolean networkUp) {
+        mTestCommon.setNetworkAvailableOnUiThread(awContents, networkUp);
+    }
+
+    /**
+     * Loads url on the UI thread and blocks until onPageFinished is called.
+     */
+    public void loadUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String url) throws Exception {
+        mTestCommon.loadUrlSync(awContents, onPageFinishedHelper, url);
+    }
+
+    public void loadUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String url, final Map<String, String> extraHeaders) throws Exception {
+        mTestCommon.loadUrlSync(awContents, onPageFinishedHelper, url, extraHeaders);
+    }
+
+    public void loadUrlSyncAndExpectError(final AwContents awContents,
+            CallbackHelper onPageFinishedHelper, CallbackHelper onReceivedErrorHelper,
+            final String url) throws Exception {
+        mTestCommon.loadUrlSyncAndExpectError(
+                awContents, onPageFinishedHelper, onReceivedErrorHelper, url);
+    }
+
+    /**
+     * Loads url on the UI thread but does not block.
+     */
+    public void loadUrlAsync(final AwContents awContents, final String url) throws Exception {
+        mTestCommon.loadUrlAsync(awContents, url);
+    }
+
+    public void loadUrlAsync(
+            final AwContents awContents, final String url, final Map<String, String> extraHeaders) {
+        mTestCommon.loadUrlAsync(awContents, url, extraHeaders);
+    }
+
+    /**
+     * Posts url on the UI thread and blocks until onPageFinished is called.
+     */
+    public void postUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String url, byte[] postData) throws Exception {
+        mTestCommon.postUrlSync(awContents, onPageFinishedHelper, url, postData);
+    }
+
+    /**
+     * Loads url on the UI thread but does not block.
+     */
+    public void postUrlAsync(final AwContents awContents, final String url, byte[] postData)
+            throws Exception {
+        mTestCommon.postUrlAsync(awContents, url, postData);
+    }
+
+    /**
+     * Loads data on the UI thread and blocks until onPageFinished is called.
+     */
+    public void loadDataSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String data, final String mimeType, final boolean isBase64Encoded)
+            throws Exception {
+        mTestCommon.loadDataSync(awContents, onPageFinishedHelper, data, mimeType, isBase64Encoded);
+    }
+
+    public void loadDataSyncWithCharset(final AwContents awContents,
+            CallbackHelper onPageFinishedHelper, final String data, final String mimeType,
+            final boolean isBase64Encoded, final String charset) throws Exception {
+        mTestCommon.loadDataSyncWithCharset(
+                awContents, onPageFinishedHelper, data, mimeType, isBase64Encoded, charset);
+    }
+
+    /**
+     * Loads data on the UI thread but does not block.
+     */
+    public void loadDataAsync(final AwContents awContents, final String data, final String mimeType,
+            final boolean isBase64Encoded) throws Exception {
+        mTestCommon.loadDataAsync(awContents, data, mimeType, isBase64Encoded);
+    }
+
+    public void loadDataWithBaseUrlSync(final AwContents awContents,
+            CallbackHelper onPageFinishedHelper, final String data, final String mimeType,
+            final boolean isBase64Encoded, final String baseUrl, final String historyUrl)
+            throws Throwable {
+        mTestCommon.loadDataWithBaseUrlSync(awContents, onPageFinishedHelper, data, mimeType,
+                isBase64Encoded, baseUrl, historyUrl);
+    }
+
+    public void loadDataWithBaseUrlAsync(final AwContents awContents, final String data,
+            final String mimeType, final boolean isBase64Encoded, final String baseUrl,
+            final String historyUrl) throws Throwable {
+        mTestCommon.loadDataWithBaseUrlAsync(
+                awContents, data, mimeType, isBase64Encoded, baseUrl, historyUrl);
+    }
+
+    /**
+     * Reloads the current page synchronously.
+     */
+    public void reloadSync(final AwContents awContents, CallbackHelper onPageFinishedHelper)
+            throws Exception {
+        mTestCommon.reloadSync(awContents, onPageFinishedHelper);
+    }
+
+    /**
+     * Stops loading on the UI thread.
+     */
+    public void stopLoading(final AwContents awContents) {
+        mTestCommon.stopLoading(awContents);
+    }
+
+    public void waitForVisualStateCallback(final AwContents awContents) throws Exception {
+        mTestCommon.waitForVisualStateCallback(awContents);
+    }
+
+    public void insertVisualStateCallbackOnUIThread(final AwContents awContents,
+            final long requestId, final AwContents.VisualStateCallback callback) {
+        mTestCommon.insertVisualStateCallbackOnUIThread(awContents, requestId, callback);
+    }
+
+    // Waits for the pixel at the center of AwContents to color up into expectedColor.
+    // Note that this is a stricter condition that waiting for a visual state callback,
+    // as visual state callback only indicates that *something* has appeared in WebView.
+    public void waitForPixelColorAtCenterOfView(final AwContents awContents,
+            final AwTestContainerView testContainerView, final int expectedColor) throws Exception {
+        mTestCommon.waitForPixelColorAtCenterOfView(awContents, testContainerView, expectedColor);
+    }
+
+    public AwTestContainerView createAwTestContainerView(final AwContentsClient awContentsClient) {
+        return mTestCommon.createAwTestContainerView(awContentsClient);
+    }
+
+    public AwTestContainerView createAwTestContainerView(final AwContentsClient awContentsClient,
+            boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
+        return mTestCommon.createAwTestContainerView(
+                awContentsClient, supportsLegacyQuirks, testDependencyFactory);
+    }
+
+    public AwBrowserContext getAwBrowserContext() {
+        return mTestCommon.getAwBrowserContext();
+    }
+
+    public AwTestContainerView createDetachedAwTestContainerView(
+            final AwContentsClient awContentsClient) {
+        return mTestCommon.createDetachedAwTestContainerView(awContentsClient);
+    }
+
+    public AwTestContainerView createDetachedAwTestContainerView(
+            final AwContentsClient awContentsClient, boolean supportsLegacyQuirks,
+            TestDependencyFactory testDependencyFactory) {
+        return mTestCommon.createDetachedAwTestContainerView(
+                awContentsClient, supportsLegacyQuirks, testDependencyFactory);
+    }
+
+    protected boolean isHardwareAcceleratedTest() {
+        return mTestCommon.isHardwareAcceleratedTest();
+    }
+
+    public AwTestContainerView createAwTestContainerViewOnMainSync(final AwContentsClient client)
+            throws Exception {
+        return mTestCommon.createAwTestContainerViewOnMainSync(client);
+    }
+
+    public AwTestContainerView createAwTestContainerViewOnMainSync(
+            final AwContentsClient client, final boolean supportsLegacyQuirks) {
+        return mTestCommon.createAwTestContainerViewOnMainSync(client, supportsLegacyQuirks);
+    }
+
+    public AwTestContainerView createAwTestContainerViewOnMainSync(final AwContentsClient client,
+            final boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
+        return mTestCommon.createAwTestContainerViewOnMainSync(
+                client, supportsLegacyQuirks, testDependencyFactory);
+    }
+
+    public void destroyAwContentsOnMainSync(final AwContents awContents) {
+        mTestCommon.destroyAwContentsOnMainSync(awContents);
+    }
+
+    public String getTitleOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.getTitleOnUiThread(awContents);
+    }
+
+    public AwSettings getAwSettingsOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.getAwSettingsOnUiThread(awContents);
+    }
+
+    /**
+     * Verify double quotes in both sides of the raw string. Strip the double quotes and
+     * returns rest of the string.
+     */
+    protected String maybeStripDoubleQuotes(String raw) {
+        return mTestCommon.maybeStripDoubleQuotes(raw);
+    }
+
+    /**
+     * Executes the given snippet of JavaScript code within the given ContentView. Returns the
+     * result of its execution in JSON format.
+     */
+    public String executeJavaScriptAndWaitForResult(final AwContents awContents,
+            TestAwContentsClient viewClient, final String code) throws Exception {
+        return mTestCommon.executeJavaScriptAndWaitForResult(awContents, viewClient, code);
+    }
+
+    /**
+     * Executes JavaScript code within the given ContentView to get the text content in
+     * document body. Returns the result string without double quotes.
+     */
+    public String getJavaScriptResultBodyTextContent(
+            final AwContents awContents, final TestAwContentsClient viewClient) throws Exception {
+        return mTestCommon.getJavaScriptResultBodyTextContent(awContents, viewClient);
+    }
+
+    /**
+     * Wrapper around CriteriaHelper.pollInstrumentationThread. This uses AwTestBase-specifc
+     * timeouts and treats timeouts and exceptions as test failures automatically.
+     */
+    public static void pollInstrumentationThread(final Callable<Boolean> callable)
+            throws Exception {
+        AwTestCommon.pollInstrumentationThread(callable);
+    }
+
+    /**
+     * Wrapper around {@link AwTestBase#poll()} but runs the callable on the UI thread.
+     */
+    public void pollUiThread(final Callable<Boolean> callable) throws Exception {
+        mTestCommon.pollUiThread(callable);
+    }
+
+    /**
+     * Clears the resource cache. Note that the cache is per-application, so this will clear the
+     * cache for all WebViews used.
+     */
+    public void clearCacheOnUiThread(final AwContents awContents, final boolean includeDiskFiles)
+            throws Exception {
+        mTestCommon.clearCacheOnUiThread(awContents, includeDiskFiles);
+    }
+
+    /**
+     * Returns pure page scale.
+     */
+    public float getScaleOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.getScaleOnUiThread(awContents);
+    }
+
+    /**
+     * Returns page scale multiplied by the screen density.
+     */
+    public float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.getPixelScaleOnUiThread(awContents);
+    }
+
+    /**
+     * Returns whether a user can zoom the page in.
+     */
+    public boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.canZoomInOnUiThread(awContents);
+    }
+
+    /**
+     * Returns whether a user can zoom the page out.
+     */
+    public boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.canZoomOutOnUiThread(awContents);
+    }
+
+    public void killRenderProcessOnUiThreadAsync(final AwContents awContents) throws Exception {
+        mTestCommon.killRenderProcessOnUiThreadAsync(awContents);
+    }
+
+    /**
+     * Loads the main html then triggers the popup window.
+     */
+    public void triggerPopup(final AwContents parentAwContents,
+            TestAwContentsClient parentAwContentsClient, TestWebServer testWebServer,
+            String mainHtml, String popupHtml, String popupPath, String triggerScript)
+            throws Exception {
+        mTestCommon.triggerPopup(parentAwContents, parentAwContentsClient, testWebServer, mainHtml,
+                popupHtml, popupPath, triggerScript);
+    }
+
+    /**
+     * Supplies the popup window with AwContents then waits for the popup window to finish loading.
+     */
+    public PopupInfo connectPendingPopup(final AwContents parentAwContents) throws Exception {
+        return mTestCommon.connectPendingPopup(parentAwContents);
+    }
+
+    @Override
+    public boolean testMethodHasAnnotation(Class<? extends Annotation> clazz) {
+        return mCurrentTestDescription.getAnnotation(clazz) != null ? true : false;
+    }
+
+    @Override
+    public Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
index 5e3004e..f56533f1 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -214,7 +214,7 @@
                 mShouldInterceptRequestHelper.getRequestsForUrl(pageWithFormUrl).method);
 
         callCount = mShouldInterceptRequestHelper.getCallCount();
-        JSUtils.clickOnLinkUsingJs(this, mAwContents,
+        JSUtils.clickOnLinkUsingJs(getInstrumentation(), mAwContents,
                 mContentsClient.getOnEvaluateJavaScriptResultHelper(), "link");
         mShouldInterceptRequestHelper.waitForCallback(callCount);
         assertEquals("POST",
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
index c02be0dc..28090bd1 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
@@ -78,7 +78,7 @@
 
     private void clickOnLinkUsingJs() throws Throwable {
         enableJavaScriptOnUiThread(mAwContents);
-        JSUtils.clickOnLinkUsingJs(this, mAwContents,
+        JSUtils.clickOnLinkUsingJs(getInstrumentation(), mAwContents,
                 mContentsClient.getOnEvaluateJavaScriptResultHelper(), "link");
     }
 
@@ -926,8 +926,9 @@
 
             loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), urlWithLink);
             // Executing JS code that tries to navigate somewhere should not create an intent.
-            assertEquals("\"" + testUrl + "\"", JSUtils.executeJavaScriptAndWaitForResult(
-                            this, mAwContents, new OnEvaluateJavaScriptResultHelper(),
+            assertEquals("\"" + testUrl + "\"",
+                    JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), mAwContents,
+                            new OnEvaluateJavaScriptResultHelper(),
                             "document.location.href='" + testUrl + "'"));
             assertNull(getActivity().getLastSentIntent());
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
index cb5c93d..e0c4d29 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
@@ -42,7 +42,7 @@
     }
 
     @Override
-    protected TestDependencyFactory createTestDependencyFactory() {
+    public TestDependencyFactory createTestDependencyFactory() {
         if (mOverridenFactory == null) {
             return new TestDependencyFactory();
         } else {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index ce37246..a787e7c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.android_webview.test;
 
+import static org.chromium.android_webview.test.AwTestCommon.WAIT_TIMEOUT_MS;
+
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -11,14 +13,19 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
-import android.test.UiThreadTest;
 import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.View;
 import android.webkit.JavascriptInterface;
 
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.AwSwitches;
@@ -52,117 +59,141 @@
 /**
  * AwContents tests.
  */
-public class AwContentsTest extends AwTestBase {
+@RunWith(AwJUnit4ClassRunner.class)
+public class AwContentsTest {
+    @Rule
+    public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
 
     private TestAwContentsClient mContentsClient = new TestAwContentsClient();
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
-    @UiThreadTest
     public void testCreateDestroy() throws Throwable {
         // NOTE this test runs on UI thread, so we cannot call any async methods.
-        createAwTestContainerView(mContentsClient).getAwContents().destroy();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivityTestRule.createAwTestContainerView(mContentsClient)
+                        .getAwContents()
+                        .destroy();
+            }
+        });
     }
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testCreateLoadPageDestroy() throws Throwable {
         AwTestContainerView awTestContainerView =
-                createAwTestContainerViewOnMainSync(mContentsClient);
-        loadDataSync(awTestContainerView.getAwContents(), mContentsClient.getOnPageFinishedHelper(),
-                CommonResources.ABOUT_HTML, "text/html", false);
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
+        mActivityTestRule.loadDataSync(awTestContainerView.getAwContents(),
+                mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML, "text/html",
+                false);
 
-        destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
+        mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
         // It should be safe to call destroy multiple times.
-        destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
+        mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
     }
 
+    @Test
     @LargeTest
     @Feature({"AndroidWebView"})
     public void testCreateLoadDestroyManyTimes() throws Throwable {
         for (int i = 0; i < 10; ++i) {
-            AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+            AwTestContainerView testView =
+                    mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
             AwContents awContents = testView.getAwContents();
 
-            loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+            mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
-            destroyAwContentsOnMainSync(awContents);
+            mActivityTestRule.destroyAwContentsOnMainSync(awContents);
         }
     }
 
+    @Test
     @LargeTest
     @Feature({"AndroidWebView"})
     public void testCreateLoadDestroyManyAtOnce() throws Throwable {
         AwTestContainerView views[] = new AwTestContainerView[10];
 
         for (int i = 0; i < views.length; ++i) {
-            views[i] = createAwTestContainerViewOnMainSync(mContentsClient);
-            loadUrlSync(views[i].getAwContents(), mContentsClient.getOnPageFinishedHelper(),
+            views[i] = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
+            mActivityTestRule.loadUrlSync(views[i].getAwContents(),
+                    mContentsClient.getOnPageFinishedHelper(),
                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
         }
 
         for (int i = 0; i < views.length; ++i) {
-            destroyAwContentsOnMainSync(views[i].getAwContents());
+            mActivityTestRule.destroyAwContentsOnMainSync(views[i].getAwContents());
             views[i] = null;
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
-    @UiThreadTest
     public void testWebViewApisFailGracefullyAfterDestruction() throws Throwable {
-        AwContents awContents =
-                createAwTestContainerView(mContentsClient).getAwContents();
-        awContents.destroy();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                AwContents awContents = mActivityTestRule.createAwTestContainerView(mContentsClient)
+                                                .getAwContents();
+                awContents.destroy();
 
-        // The documentation for WebView#destroy() reads "This method should be called
-        // after this WebView has been removed from the view system. No other methods
-        // may be called on this WebView after destroy".
-        // However, some apps do not respect that restriction so we need to ensure that
-        // we fail gracefully and do not crash when APIs are invoked after destruction.
-        // Due to the large number of APIs we only test a representative selection here.
-        awContents.clearHistory();
-        awContents.loadUrl("http://www.google.com");
-        awContents.findAllAsync("search");
-        assertNull(awContents.getUrl());
-        assertFalse(awContents.canGoBack());
-        awContents.disableJavascriptInterfacesInspection();
-        awContents.invokeZoomPicker();
-        awContents.onResume();
-        awContents.stopLoading();
-        awContents.onWindowVisibilityChanged(View.VISIBLE);
-        awContents.requestFocus();
-        awContents.isMultiTouchZoomSupported();
-        awContents.setOverScrollMode(View.OVER_SCROLL_NEVER);
-        awContents.pauseTimers();
-        awContents.onContainerViewScrollChanged(200, 200, 100, 100);
-        awContents.computeScroll();
-        awContents.onMeasure(100, 100);
-        awContents.onDraw(new Canvas());
-        awContents.getMostRecentProgress();
-        assertEquals(0, awContents.computeHorizontalScrollOffset());
-        assertEquals(0, awContents.getContentWidthCss());
-        awContents.onKeyUp(KeyEvent.KEYCODE_BACK,
-                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
+                // The documentation for WebView#destroy() reads "This method should be called
+                // after this WebView has been removed from the view system. No other methods
+                // may be called on this WebView after destroy".
+                // However, some apps do not respect that restriction so we need to ensure that
+                // we fail gracefully and do not crash when APIs are invoked after destruction.
+                // Due to the large number of APIs we only test a representative selection here.
+                awContents.clearHistory();
+                awContents.loadUrl("http://www.google.com");
+                awContents.findAllAsync("search");
+                Assert.assertNull(awContents.getUrl());
+                Assert.assertFalse(awContents.canGoBack());
+                awContents.disableJavascriptInterfacesInspection();
+                awContents.invokeZoomPicker();
+                awContents.onResume();
+                awContents.stopLoading();
+                awContents.onWindowVisibilityChanged(View.VISIBLE);
+                awContents.requestFocus();
+                awContents.isMultiTouchZoomSupported();
+                awContents.setOverScrollMode(View.OVER_SCROLL_NEVER);
+                awContents.pauseTimers();
+                awContents.onContainerViewScrollChanged(200, 200, 100, 100);
+                awContents.computeScroll();
+                awContents.onMeasure(100, 100);
+                awContents.onDraw(new Canvas());
+                awContents.getMostRecentProgress();
+                Assert.assertEquals(0, awContents.computeHorizontalScrollOffset());
+                Assert.assertEquals(0, awContents.getContentWidthCss());
+                awContents.onKeyUp(KeyEvent.KEYCODE_BACK,
+                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
+            }
+        });
     }
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testUseAwSettingsAfterDestroy() throws Throwable {
         AwTestContainerView awTestContainerView =
-                createAwTestContainerViewOnMainSync(mContentsClient);
-        AwSettings awSettings = getAwSettingsOnUiThread(awTestContainerView.getAwContents());
-        loadDataSync(awTestContainerView.getAwContents(), mContentsClient.getOnPageFinishedHelper(),
-                CommonResources.ABOUT_HTML, "text/html", false);
-        destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
+        AwSettings awSettings =
+                mActivityTestRule.getAwSettingsOnUiThread(awTestContainerView.getAwContents());
+        mActivityTestRule.loadDataSync(awTestContainerView.getAwContents(),
+                mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML, "text/html",
+                false);
+        mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
 
         // AwSettings should still be usable even after native side is destroyed.
         String newFontFamily = "serif";
         awSettings.setStandardFontFamily(newFontFamily);
-        assertEquals(newFontFamily, awSettings.getStandardFontFamily());
+        Assert.assertEquals(newFontFamily, awSettings.getStandardFontFamily());
         boolean newBlockNetworkLoads = !awSettings.getBlockNetworkLoads();
         awSettings.setBlockNetworkLoads(newBlockNetworkLoads);
-        assertEquals(newBlockNetworkLoads, awSettings.getBlockNetworkLoads());
+        Assert.assertEquals(newBlockNetworkLoads, awSettings.getBlockNetworkLoads());
     }
 
     private int callDocumentHasImagesSync(final AwContents awContents)
@@ -178,21 +209,23 @@
                 s.release();
             }
         });
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 awContents.documentHasImages(msg);
             }
         });
-        assertTrue(s.tryAcquire(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        Assert.assertTrue(s.tryAcquire(AwActivityTestRule.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
         int result = val.get();
         return result;
     }
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testDocumentHasImages() throws Throwable {
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
 
         final CallbackHelper loadHelper = mContentsClient.getOnPageFinishedHelper();
@@ -202,21 +235,22 @@
         final String imageDoc = "<head/><body><img/><img/></body>";
 
         // Make sure a document that does not have images returns 0
-        loadDataSync(awContents, loadHelper, emptyDoc, mime, false);
+        mActivityTestRule.loadDataSync(awContents, loadHelper, emptyDoc, mime, false);
         int result = callDocumentHasImagesSync(awContents);
-        assertEquals(0, result);
+        Assert.assertEquals(0, result);
 
         // Make sure a document that does have images returns 1
-        loadDataSync(awContents, loadHelper, imageDoc, mime, false);
+        mActivityTestRule.loadDataSync(awContents, loadHelper, imageDoc, mime, false);
         result = callDocumentHasImagesSync(awContents);
-        assertEquals(1, result);
+        Assert.assertEquals(1, result);
     }
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testClearCacheMemoryAndDisk() throws Throwable {
         final AwTestContainerView testContainer =
-                createAwTestContainerViewOnMainSync(mContentsClient);
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final AwContents awContents = testContainer.getAwContents();
 
         TestWebServer webServer = TestWebServer.start();
@@ -230,46 +264,44 @@
                     pagePath, "<html><body>foo</body></html>", headers);
 
             // First load to populate cache.
-            clearCacheOnUiThread(awContents, true);
-            loadUrlSync(awContents,
-                        mContentsClient.getOnPageFinishedHelper(),
-                        pageUrl);
-            assertEquals(1, webServer.getRequestCount(pagePath));
+            mActivityTestRule.clearCacheOnUiThread(awContents, true);
+            mActivityTestRule.loadUrlSync(
+                    awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+            Assert.assertEquals(1, webServer.getRequestCount(pagePath));
 
             // Load about:blank so next load is not treated as reload by webkit and force
             // revalidate with the server.
-            loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+            mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
             // No clearCache call, so should be loaded from cache.
-            loadUrlSync(awContents,
-                        mContentsClient.getOnPageFinishedHelper(),
-                        pageUrl);
-            assertEquals(1, webServer.getRequestCount(pagePath));
+            mActivityTestRule.loadUrlSync(
+                    awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+            Assert.assertEquals(1, webServer.getRequestCount(pagePath));
 
             // Same as above.
-            loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+            mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
             // Clear cache, so should hit server again.
-            clearCacheOnUiThread(awContents, true);
-            loadUrlSync(awContents,
-                        mContentsClient.getOnPageFinishedHelper(),
-                        pageUrl);
-            assertEquals(2, webServer.getRequestCount(pagePath));
+            mActivityTestRule.clearCacheOnUiThread(awContents, true);
+            mActivityTestRule.loadUrlSync(
+                    awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+            Assert.assertEquals(2, webServer.getRequestCount(pagePath));
         } finally {
             webServer.shutdown();
         }
     }
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testClearCacheInQuickSuccession() throws Throwable {
         final AwTestContainerView testContainer =
-                createAwTestContainerViewOnMainSync(new TestAwContentsClient());
+                mActivityTestRule.createAwTestContainerViewOnMainSync(new TestAwContentsClient());
         final AwContents awContents = testContainer.getAwContents();
 
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 for (int i = 0; i < 10; ++i) {
@@ -279,11 +311,13 @@
         });
     }
 
+    @Test
     @SmallTest
     @Feature({"AndroidWebView"})
     public void testGetFavicon() throws Throwable {
         AwContents.setShouldDownloadFavicons();
-        final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        final AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final AwContents awContents = testView.getAwContents();
 
         TestWebServer webServer = TestWebServer.start();
@@ -298,10 +332,11 @@
             // the page load completes which makes it slightly hard to test.
             final Bitmap defaultFavicon = awContents.getFavicon();
 
-            getAwSettingsOnUiThread(awContents).setImagesEnabled(true);
-            loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+            mActivityTestRule.getAwSettingsOnUiThread(awContents).setImagesEnabled(true);
+            mActivityTestRule.loadUrlSync(
+                    awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
 
-            pollUiThread(new Callable<Boolean>() {
+            mActivityTestRule.pollUiThread(new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
                     return awContents.getFavicon() != null
@@ -312,19 +347,21 @@
             final Object originalFaviconSource = (new URL(faviconUrl)).getContent();
             final Bitmap originalFavicon =
                     BitmapFactory.decodeStream((InputStream) originalFaviconSource);
-            assertNotNull(originalFavicon);
+            Assert.assertNotNull(originalFavicon);
 
-            assertTrue(awContents.getFavicon().sameAs(originalFavicon));
+            Assert.assertTrue(awContents.getFavicon().sameAs(originalFavicon));
 
         } finally {
             webServer.shutdown();
         }
     }
 
+    @Test
     @Feature({"AndroidWebView", "Downloads"})
     @SmallTest
     public void testDownload() throws Throwable {
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
 
         final String data = "download data";
@@ -343,42 +380,47 @@
             final OnDownloadStartHelper downloadStartHelper =
                     mContentsClient.getOnDownloadStartHelper();
             final int callCount = downloadStartHelper.getCallCount();
-            loadUrlAsync(awContents, pageUrl);
+            mActivityTestRule.loadUrlAsync(awContents, pageUrl);
             downloadStartHelper.waitForCallback(callCount);
 
-            assertEquals(pageUrl, downloadStartHelper.getUrl());
-            assertEquals(contentDisposition, downloadStartHelper.getContentDisposition());
-            assertEquals(mimeType, downloadStartHelper.getMimeType());
-            assertEquals(data.length(), downloadStartHelper.getContentLength());
+            Assert.assertEquals(pageUrl, downloadStartHelper.getUrl());
+            Assert.assertEquals(contentDisposition, downloadStartHelper.getContentDisposition());
+            Assert.assertEquals(mimeType, downloadStartHelper.getMimeType());
+            Assert.assertEquals(data.length(), downloadStartHelper.getContentLength());
         } finally {
             webServer.shutdown();
         }
     }
 
+    @Test
     @Feature({"AndroidWebView", "setNetworkAvailable"})
     @SmallTest
     public void testSetNetworkAvailable() throws Throwable {
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
         String script = "navigator.onLine";
 
-        enableJavaScriptOnUiThread(awContents);
-        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+        mActivityTestRule.enableJavaScriptOnUiThread(awContents);
+        mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
 
         // Default to "online".
-        assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
-                script));
+        Assert.assertEquals("true",
+                mActivityTestRule.executeJavaScriptAndWaitForResult(
+                        awContents, mContentsClient, script));
 
         // Forcing "offline".
-        setNetworkAvailableOnUiThread(awContents, false);
-        assertEquals("false", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
-                script));
+        mActivityTestRule.setNetworkAvailableOnUiThread(awContents, false);
+        Assert.assertEquals("false",
+                mActivityTestRule.executeJavaScriptAndWaitForResult(
+                        awContents, mContentsClient, script));
 
         // Forcing "online".
-        setNetworkAvailableOnUiThread(awContents, true);
-        assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
-                script));
+        mActivityTestRule.setNetworkAvailableOnUiThread(awContents, true);
+        Assert.assertEquals("true",
+                mActivityTestRule.executeJavaScriptAndWaitForResult(
+                        awContents, mContentsClient, script));
     }
 
 
@@ -394,13 +436,15 @@
         }
     }
 
+    @Test
     @Feature({"AndroidWebView", "Android-JavaBridge"})
     @SmallTest
     public void testJavaBridge() throws Throwable {
-        final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        final AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final CallbackHelper callback = new CallbackHelper();
 
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 AwContents awContents = testView.getAwContents();
@@ -413,56 +457,64 @@
         callback.waitForCallback(0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
     }
 
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     public void testEscapingOfErrorPage() throws Throwable {
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
         String script = "window.failed == true";
 
-        enableJavaScriptOnUiThread(awContents);
+        mActivityTestRule.enableJavaScriptOnUiThread(awContents);
         CallbackHelper onPageFinishedHelper = mContentsClient.getOnPageFinishedHelper();
         int currentCallCount = onPageFinishedHelper.getCallCount();
-        loadUrlAsync(awContents,
+        mActivityTestRule.loadUrlAsync(awContents,
                 "file:///file-that-does-not-exist#<script>window.failed = true;</script>");
-        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
-                                             TimeUnit.MILLISECONDS);
+        onPageFinishedHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
 
-        assertEquals("false", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
-                script));
+        Assert.assertEquals("false",
+                mActivityTestRule.executeJavaScriptAndWaitForResult(
+                        awContents, mContentsClient, script));
     }
 
+    @Test
     @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
     @Feature({"AndroidWebView"})
     @SmallTest
     public void testCanInjectHeaders() throws Throwable {
         final AwTestContainerView testContainer =
-                createAwTestContainerViewOnMainSync(mContentsClient);
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final AwContents awContents = testContainer.getAwContents();
 
-        enableJavaScriptOnUiThread(awContents);
+        mActivityTestRule.enableJavaScriptOnUiThread(awContents);
 
-        EmbeddedTestServer testServer =
-                EmbeddedTestServer.createAndStartServer(getInstrumentation().getContext());
+        EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
 
         try {
             String url = testServer.getURL("/echoheader?X-foo");
 
             final Map<String, String> extraHeaders = new HashMap<String, String>();
             extraHeaders.put("X-foo", "bar");
-            loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders);
+            mActivityTestRule.loadUrlSync(
+                    awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders);
 
-            String xfoo = getJavaScriptResultBodyTextContent(awContents, mContentsClient);
-            assertEquals("bar", xfoo);
+            String xfoo = mActivityTestRule.getJavaScriptResultBodyTextContent(
+                    awContents, mContentsClient);
+            Assert.assertEquals("bar", xfoo);
 
             url = testServer.getURL("/echoheader?Referer");
 
             extraHeaders.clear();
             extraHeaders.put("Referer", "http://www.example.com/");
-            loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders);
+            mActivityTestRule.loadUrlSync(
+                    awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders);
 
-            String referer = getJavaScriptResultBodyTextContent(awContents, mContentsClient);
-            assertEquals("http://www.example.com/", referer);
+            String referer = mActivityTestRule.getJavaScriptResultBodyTextContent(
+                    awContents, mContentsClient);
+            Assert.assertEquals("http://www.example.com/", referer);
         } finally {
             testServer.stopAndDestroyServer();
         }
@@ -471,17 +523,19 @@
     // This is a meta test that we don't accidentally turn off hardware
     // acceleration in instrumentation tests without notice. Do not add the
     // @DisableHardwareAccelerationForTest annotation for this test.
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     public void testHardwareModeWorks() throws Throwable {
         AwTestContainerView testContainer =
-                createAwTestContainerViewOnMainSync(mContentsClient);
-        assertTrue(testContainer.isHardwareAccelerated());
-        assertTrue(testContainer.isBackedByHardwareView());
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
+        Assert.assertTrue(testContainer.isHardwareAccelerated());
+        Assert.assertTrue(testContainer.isBackedByHardwareView());
     }
 
     // TODO(hush): more ssl tests. And put the ssl tests into a separate test
     // class.
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     // If the user allows the ssl error, the same ssl error will not trigger
@@ -489,7 +543,7 @@
     // error will still trigger the onReceivedSslError callback.
     public void testSslPreferences() throws Throwable {
         final AwTestContainerView testContainer =
-                createAwTestContainerViewOnMainSync(mContentsClient);
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final AwContents awContents = testContainer.getAwContents();
         TestWebServer webServer = TestWebServer.startSsl();
         final String pagePath = "/hello.html";
@@ -499,54 +553,62 @@
                 mContentsClient.getOnReceivedSslErrorHelper();
         int onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
 
-        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+        mActivityTestRule.loadUrlSync(
+                awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
 
-        assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
-        assertEquals(1, webServer.getRequestCount(pagePath));
+        Assert.assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
+        Assert.assertEquals(1, webServer.getRequestCount(pagePath));
 
         // Now load the page again. This time, we expect no ssl error, because
         // user's decision should be remembered.
         onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
-        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
-        assertEquals(onSslErrorCallCount, onReceivedSslErrorHelper.getCallCount());
+        mActivityTestRule.loadUrlSync(
+                awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+        Assert.assertEquals(onSslErrorCallCount, onReceivedSslErrorHelper.getCallCount());
 
         // Now clear the ssl preferences then load the same url again. Expect to see
         // onReceivedSslError getting called again.
         awContents.clearSslPreferences();
         onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
-        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
-        assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
+        mActivityTestRule.loadUrlSync(
+                awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+        Assert.assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
 
         // Now clear the stored decisions and tell the client to deny ssl errors.
         awContents.clearSslPreferences();
         mContentsClient.setAllowSslError(false);
         onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
-        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
-        assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
+        mActivityTestRule.loadUrlSync(
+                awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+        Assert.assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
 
         // Now load the same page again. This time, we still expect onReceivedSslError,
         // because we only remember user's decision if it is "allow".
         onSslErrorCallCount = onReceivedSslErrorHelper.getCallCount();
-        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
-        assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
+        mActivityTestRule.loadUrlSync(
+                awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
+        Assert.assertEquals(onSslErrorCallCount + 1, onReceivedSslErrorHelper.getCallCount());
     }
 
     /**
      * Verifies that Web Notifications and the Push API are not exposed in WebView.
      */
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     public void testPushAndNotificationsDisabled() throws Throwable {
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
 
         String script = "window.Notification || window.PushManager";
 
-        enableJavaScriptOnUiThread(awContents);
-        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+        mActivityTestRule.enableJavaScriptOnUiThread(awContents);
+        mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
-        assertEquals("null", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
-                script));
+        Assert.assertEquals("null",
+                mActivityTestRule.executeJavaScriptAndWaitForResult(
+                        awContents, mContentsClient, script));
     }
 
     private static class MockBindingManager implements BindingManager {
@@ -573,8 +635,8 @@
                     } catch (InterruptedException e) {
                     }
                 }
-                assertTrue(mForegroundState.size() != 0);
-                assertEquals(inForeground, mForegroundState.get(0).booleanValue());
+                Assert.assertTrue(mForegroundState.size() != 0);
+                Assert.assertEquals(inForeground, mForegroundState.get(0));
                 mForegroundState.remove(0);
                 return;
             }
@@ -612,6 +674,7 @@
     /**
      * Verifies that a child process is actually gets created with WEBVIEW_SANDBOXED_RENDERER flag.
      */
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     @CommandLineFlags.Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER)
@@ -619,21 +682,24 @@
     public void testSandboxedRendererWorks() throws Throwable {
         MockBindingManager bindingManager = new MockBindingManager();
         ChildProcessLauncherHelper.setBindingManagerForTesting(bindingManager);
-        assertFalse(bindingManager.isChildProcessCreated());
+        Assert.assertFalse(bindingManager.isChildProcessCreated());
 
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
         final String pageTitle = "I am sandboxed";
-        loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+        mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
                 "<html><head><title>" + pageTitle + "</title></head></html>", "text/html", false);
-        assertEquals(pageTitle, getTitleOnUiThread(awContents));
+        Assert.assertEquals(pageTitle, mActivityTestRule.getTitleOnUiThread(awContents));
 
-        assertTrue(bindingManager.isChildProcessCreated());
+        Assert.assertTrue(bindingManager.isChildProcessCreated());
 
         // Test end-to-end interaction with the renderer.
-        AwSettings awSettings = getAwSettingsOnUiThread(awContents);
+        AwSettings awSettings = mActivityTestRule.getAwSettingsOnUiThread(awContents);
         awSettings.setJavaScriptEnabled(true);
-        assertEquals("42", JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
+        Assert.assertEquals("42",
+                JSUtils.executeJavaScriptAndWaitForResult(
+                        InstrumentationRegistry.getInstrumentation(), awContents,
                         mContentsClient.getOnEvaluateJavaScriptResultHelper(), "21 + 21"));
     }
 
@@ -641,6 +707,7 @@
      * By default the renderer should be considererd to be in the
      * foreground.
      */
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     @CommandLineFlags.Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER)
@@ -648,14 +715,15 @@
     public void testRendererPriorityStartsHigh() throws Throwable {
         MockBindingManager bindingManager = new MockBindingManager();
         ChildProcessLauncherHelper.setBindingManagerForTesting(bindingManager);
-        assertFalse(bindingManager.isChildProcessCreated());
+        Assert.assertFalse(bindingManager.isChildProcessCreated());
 
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         AwContents awContents = testView.getAwContents();
-        loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), "<html></html>",
-                "text/html", false);
+        mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+                "<html></html>", "text/html", false);
 
-        assertTrue(bindingManager.isChildProcessCreated());
+        Assert.assertTrue(bindingManager.isChildProcessCreated());
         bindingManager.assertSetInForegroundCall(true);
     }
 
@@ -663,6 +731,7 @@
      * If we specify that the priority is WAIVED, then the renderer
      * should not be in the foreground.
      */
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     @CommandLineFlags.Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER)
@@ -670,21 +739,22 @@
     public void testRendererPriorityLow() throws Throwable {
         MockBindingManager bindingManager = new MockBindingManager();
         ChildProcessLauncherHelper.setBindingManagerForTesting(bindingManager);
-        assertFalse(bindingManager.isChildProcessCreated());
+        Assert.assertFalse(bindingManager.isChildProcessCreated());
 
-        final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        final AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final AwContents awContents = testView.getAwContents();
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 awContents.setRendererPriorityPolicy(RendererPriority.WAIVED, false);
             }
         });
-        loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), "<html></html>",
-                "text/html", false);
+        mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+                "<html></html>", "text/html", false);
 
-        assertTrue(awContents.isPageVisible());
-        assertTrue(bindingManager.isChildProcessCreated());
+        Assert.assertTrue(awContents.isPageVisible());
+        Assert.assertTrue(bindingManager.isChildProcessCreated());
         bindingManager.assertSetInForegroundCall(false);
     }
 
@@ -693,6 +763,7 @@
      * background, then pausing the view should send the renderer to
      * the background.
      */
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
     @CommandLineFlags.Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER)
@@ -700,23 +771,24 @@
     public void testRendererPriorityManaged() throws Throwable {
         MockBindingManager bindingManager = new MockBindingManager();
         ChildProcessLauncherHelper.setBindingManagerForTesting(bindingManager);
-        assertFalse(bindingManager.isChildProcessCreated());
+        Assert.assertFalse(bindingManager.isChildProcessCreated());
 
-        final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        final AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final AwContents awContents = testView.getAwContents();
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 awContents.setRendererPriorityPolicy(RendererPriority.HIGH, true);
             }
         });
-        loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), "<html></html>",
-                "text/html", false);
+        mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
+                "<html></html>", "text/html", false);
 
-        assertTrue(awContents.isPageVisible());
-        assertTrue(bindingManager.isChildProcessCreated());
+        Assert.assertTrue(awContents.isPageVisible());
+        Assert.assertTrue(bindingManager.isChildProcessCreated());
         bindingManager.assertSetInForegroundCall(true);
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 awContents.onPause();
@@ -725,30 +797,39 @@
         bindingManager.assertSetInForegroundCall(false);
     }
 
+    @Test
     @Feature({"AndroidWebView"})
     @SmallTest
-    @UiThreadTest
     @CommandLineFlags.Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER)
     @SkipCommandLineParameterization
     public void testPauseDestroyResume() throws Throwable {
-        AwContents awContents;
-        awContents = createAwTestContainerView(mContentsClient).getAwContents();
-        awContents.pauseTimers();
-        awContents.pauseTimers();
-        awContents.destroy();
-        awContents = createAwTestContainerView(mContentsClient).getAwContents();
-        awContents.resumeTimers();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                AwContents awContents;
+                awContents = mActivityTestRule.createAwTestContainerView(mContentsClient)
+                                     .getAwContents();
+                awContents.pauseTimers();
+                awContents.pauseTimers();
+                awContents.destroy();
+                awContents = mActivityTestRule.createAwTestContainerView(mContentsClient)
+                                     .getAwContents();
+                awContents.resumeTimers();
+            }
+        });
     }
 
     /** Regression test for https://crbug.com/732976. Load a data URL, then immediately
      * after that load a javascript URL. The data URL navigation shouldn't be blocked.
      */
+    @Test
     @LargeTest
     @Feature({"AndroidWebView"})
     public void testJavaScriptUrlAfterLoadData() throws Throwable {
-        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwTestContainerView testView =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
         final AwContents awContents = testView.getAwContents();
-        getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // Run javascript navigation immediately, without waiting for the completion of data
@@ -760,10 +841,10 @@
 
         mContentsClient.getOnPageFinishedHelper().waitForCallback(
                 0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertEquals("data:text/html,<html>test</html>", awContents.getLastCommittedUrl());
+        Assert.assertEquals("data:text/html,<html>test</html>", awContents.getLastCommittedUrl());
 
         TestAwContentsClient.AddMessageToConsoleHelper consoleHelper =
                 mContentsClient.getAddMessageToConsoleHelper();
-        assertEquals(0, consoleHelper.getMessages().size());
+        Assert.assertEquals(0, consoleHelper.getMessages().size());
     }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java
index 328b864..a864b82 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java
@@ -31,7 +31,7 @@
     private int mSecondBrowserServicePid;
 
     @Override
-    protected boolean needsBrowserProcessStarted() {
+    public boolean needsBrowserProcessStarted() {
         return false;
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index eb19757..fd5db6f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -2930,7 +2930,7 @@
     }
 
     @Override
-    protected TestDependencyFactory createTestDependencyFactory() {
+    public TestDependencyFactory createTestDependencyFactory() {
         if (mOverridenFactory == null) {
             return new TestDependencyFactory();
         } else {
@@ -2960,12 +2960,12 @@
         final AwTestContainerView mContainerView = createAwTestContainerViewOnMainSync(client);
         final AwContents awContents = mContainerView.getAwContents();
         enableJavaScriptOnUiThread(awContents);
-        JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
+        JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), awContents,
                 client.getOnEvaluateJavaScriptResultHelper(),
                 "window.emptyDocumentPersistenceTest = true;");
         loadUrlSync(awContents, client.getOnPageFinishedHelper(),
                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
-        String result = JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
+        String result = JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), awContents,
                 client.getOnEvaluateJavaScriptResultHelper(),
                 "window.emptyDocumentPersistenceTest ? 'set' : 'not set';");
         assertEquals(allow ? "\"set\"" : "\"not set\"", result);
@@ -3038,12 +3038,12 @@
                 awContents, client.getOnPageFinishedHelper(), testPageHtml, "text/html", false);
 
         // Focus on an empty DIV.
-        JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
+        JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), awContents,
                 client.getOnEvaluateJavaScriptResultHelper(), "window.a.focus();");
         assertEquals(1, getSelectionChangeCountForSelectionUpdateTest(awContents, client));
 
         // Create and delete a zero-width space. See crbug.com/698752 for details.
-        JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
+        JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), awContents,
                 client.getOnEvaluateJavaScriptResultHelper(),
                 "(function() {"
                         + "var sel = window.getSelection();"
@@ -3075,16 +3075,16 @@
         String expectedTitle = Integer.toString(mTitleIdx);
         // Since selectionchange event is posted on a message loop, we run another message loop
         // before we get the result. On Chromium both run on the same message loop.
-        JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
+        JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), awContents,
                 client.getOnEvaluateJavaScriptResultHelper(),
                 "setTimeout(function() { document.title = '" + expectedTitle + "'; });");
         pollTitleAs(expectedTitle, awContents);
 
-        String result = JSUtils.executeJavaScriptAndWaitForResult(
-                this, awContents, client.getOnEvaluateJavaScriptResultHelper(), "window.cnt");
+        String result = JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), awContents,
+                client.getOnEvaluateJavaScriptResultHelper(), "window.cnt");
         // Clean up
-        JSUtils.executeJavaScriptAndWaitForResult(
-                this, awContents, client.getOnEvaluateJavaScriptResultHelper(), "window.cnt = 0;");
+        JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), awContents,
+                client.getOnEvaluateJavaScriptResultHelper(), "window.cnt = 0;");
         return Integer.parseInt(result);
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
index 898d9eb..80f6261 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
@@ -33,13 +33,13 @@
     }
 
     @Override
-    protected boolean needsAwBrowserContextCreated() {
+    public boolean needsAwBrowserContextCreated() {
         return false;
     }
 
 
     @Override
-    protected boolean needsBrowserProcessStarted() {
+    public boolean needsBrowserProcessStarted() {
         // Don't start the browser process in AwTestBase - we want to start it ourselves with
         // strictmode policies turned on.
         return false;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
index f67a272..c39c643 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
@@ -9,12 +9,9 @@
 import android.app.Instrumentation;
 import android.content.Context;
 import android.os.Build;
-import android.util.AndroidRuntimeException;
-import android.util.Log;
 import android.view.ViewGroup;
 
 import org.chromium.android_webview.AwBrowserContext;
-import org.chromium.android_webview.AwBrowserProcess;
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.DependencyFactory;
 import org.chromium.android_webview.AwContents.InternalAccessDelegate;
@@ -22,18 +19,13 @@
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.AwSwitches;
-import org.chromium.android_webview.test.util.GraphicsTestUtils;
-import org.chromium.android_webview.test.util.JSUtils;
-import org.chromium.base.ThreadUtils;
+import org.chromium.android_webview.test.AwTestCommon.AwTestCommonCallback;
+import org.chromium.base.Log;
 import org.chromium.base.test.BaseActivityInstrumentationTestCase;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.InMemorySharedPreferences;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.parameter.CommandLineParameter;
-import org.chromium.content.browser.test.util.Criteria;
-import org.chromium.content.browser.test.util.CriteriaHelper;
-import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
-import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.net.test.util.TestWebServer;
 
 import java.lang.annotation.Annotation;
@@ -41,94 +33,102 @@
 import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
- * A base class for android_webview tests. WebView only runs on KitKat and later,
- * so make sure no one attempts to run the tests on earlier OS releases.
- *
- * By default, all tests run both in single-process mode, and with sandboxed renderer.
- * If a test doesn't yet work with sandboxed renderer, an entire class, or an individual test
- * method can be marked for single-process testing only by adding the following annotation:
- *
- * @SkipCommandLineParameterization
+ * A base class for android_webview tests. WebView only runs on KitKat and
+ * later, so make sure no one attempts to run the tests on earlier OS releases.
+ * By default, all tests run both in single-process mode, and with sandboxed
+ * renderer. If a test doesn't yet work with sandboxed renderer, an entire
+ * class, or an individual test method can be marked for single-process testing
+ * only by adding @SkipCommandLineParameterization to the test
  */
 @MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT)
 @CommandLineParameter({"", AwSwitches.WEBVIEW_SANDBOXED_RENDERER})
-public class AwTestBase extends BaseActivityInstrumentationTestCase<AwTestRunnerActivity> {
-    public static final long WAIT_TIMEOUT_MS = scaleTimeout(15000);
-    public static final int CHECK_INTERVAL = 100;
+public class AwTestBase extends BaseActivityInstrumentationTestCase<AwTestRunnerActivity>
+        implements AwTestCommonCallback {
     private static final String TAG = "AwTestBase";
-    private static final Pattern MAYBE_QUOTED_STRING = Pattern.compile("^(\"?)(.*)\\1$");
 
-    // The browser context needs to be a process-wide singleton.
-    private AwBrowserContext mBrowserContext;
+    public static final long WAIT_TIMEOUT_MS = scaleTimeout(15000);
+
+    public static final int CHECK_INTERVAL = 100;
+
+    private final AwTestCommon mTestCommon;
 
     public AwTestBase() {
         super(AwTestRunnerActivity.class);
+        mTestCommon = new AwTestCommon(this);
     }
 
     @Override
     protected void setUp() throws Exception {
-        if (needsAwBrowserContextCreated()) {
-            createAwBrowserContext();
-        }
-
-        super.setUp();
-        if (needsBrowserProcessStarted()) {
-            startBrowserProcess();
-        }
+        mTestCommon.setUp();
     }
 
-    protected void createAwBrowserContext() {
-        if (mBrowserContext != null) {
-            throw new AndroidRuntimeException("There should only be one browser context.");
-        }
-        getActivity(); // The Activity must be launched in order to load native code
-        final InMemorySharedPreferences prefs = new InMemorySharedPreferences();
-        final Context appContext = getInstrumentation().getTargetContext().getApplicationContext();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mBrowserContext = createAwBrowserContextOnUiThread(prefs, appContext);
-            }
-        });
-    }
-
-    protected AwBrowserContext createAwBrowserContextOnUiThread(
+    @Override
+    public AwBrowserContext createAwBrowserContextOnUiThread(
             InMemorySharedPreferences prefs, Context appContext) {
         return new AwBrowserContext(prefs, appContext);
     }
 
+    @Override
+    public TestDependencyFactory createTestDependencyFactory() {
+        return new TestDependencyFactory();
+    }
+
+    /**
+     * Override this to return false if the test doesn't want to create an
+     * AwBrowserContext automatically.
+     */
+    @Override
+    public boolean needsAwBrowserContextCreated() {
+        return true;
+    }
+
+    /**
+     * Override this to return false if the test doesn't want the browser
+     * startup sequence to be run automatically.
+     *
+     * @return Whether the instrumentation test requires the browser process to
+     *         already be started.
+     */
+    @Override
+    public boolean needsBrowserProcessStarted() {
+        return true;
+    }
+
+    /**
+     * Checks the current test has |clazz| annotation. Note this swallows
+     * NoSuchMethodException and returns false in that case.
+     */
+    @Override
+    public boolean testMethodHasAnnotation(Class<? extends Annotation> clazz) {
+        String testName = getName();
+        Method method = null;
+        try {
+            method = getClass().getMethod(testName);
+        } catch (NoSuchMethodException e) {
+            Log.w(TAG, "Test method name not found.", e);
+            return false;
+        }
+
+        // Cast to AnnotatedElement to work around a compilation failure.
+        // Method.isAnnotationPresent() was removed in Java 8 (which is used by
+        // the Android N SDK),
+        // so compilation with Java 7 fails. See crbug.com/608792.
+        return ((AnnotatedElement) method).isAnnotationPresent(clazz);
+    }
+
+    @Override
+    public void runOnUiThread(Runnable runnable) {
+        getInstrumentation().runOnMainSync(runnable);
+    }
+
+    protected void createAwBrowserContext() {
+        mTestCommon.createAwBrowserContext();
+    }
+
     protected void startBrowserProcess() throws Exception {
-        // The Activity must be launched in order for proper webview statics to be setup.
-        getActivity();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                AwBrowserProcess.start();
-            }
-        });
-    }
-
-    /**
-     * Override this to return false if the test doesn't want to create an AwBrowserContext
-     * automatically.
-     */
-    protected boolean needsAwBrowserContextCreated() {
-        return true;
-    }
-
-    /**
-     * Override this to return false if the test doesn't want the browser startup sequence to
-     * be run automatically.
-     * @return Whether the instrumentation test requires the browser process to already be started.
-     */
-    protected boolean needsBrowserProcessStarted() {
-        return true;
+        mTestCommon.startBrowserProcess();
     }
 
     /**
@@ -141,28 +141,16 @@
      */
     public <R> R runTestOnUiThreadAndGetResult(Callable<R> callable)
             throws Exception {
-        FutureTask<R> task = new FutureTask<R>(callable);
-        getInstrumentation().runOnMainSync(task);
-        return task.get();
+        return mTestCommon.runTestOnUiThreadAndGetResult(callable);
     }
 
     public void enableJavaScriptOnUiThread(final AwContents awContents) {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.getSettings().setJavaScriptEnabled(true);
-            }
-        });
+        mTestCommon.enableJavaScriptOnUiThread(awContents);
     }
 
     public void setNetworkAvailableOnUiThread(final AwContents awContents,
             final boolean networkUp) {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.setNetworkAvailable(networkUp);
-            }
-        });
+        mTestCommon.setNetworkAvailableOnUiThread(awContents, networkUp);
     }
 
     /**
@@ -171,30 +159,22 @@
     public void loadUrlSync(final AwContents awContents,
             CallbackHelper onPageFinishedHelper,
             final String url) throws Exception {
-        loadUrlSync(awContents, onPageFinishedHelper, url, null);
+        mTestCommon.loadUrlSync(awContents, onPageFinishedHelper, url);
     }
 
     public void loadUrlSync(final AwContents awContents,
             CallbackHelper onPageFinishedHelper,
             final String url,
             final Map<String, String> extraHeaders) throws Exception {
-        int currentCallCount = onPageFinishedHelper.getCallCount();
-        loadUrlAsync(awContents, url, extraHeaders);
-        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
+        mTestCommon.loadUrlSync(awContents, onPageFinishedHelper, url, extraHeaders);
     }
 
     public void loadUrlSyncAndExpectError(final AwContents awContents,
             CallbackHelper onPageFinishedHelper,
             CallbackHelper onReceivedErrorHelper,
             final String url) throws Exception {
-        int onErrorCallCount = onReceivedErrorHelper.getCallCount();
-        int onFinishedCallCount = onPageFinishedHelper.getCallCount();
-        loadUrlAsync(awContents, url);
-        onReceivedErrorHelper.waitForCallback(onErrorCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
-        onPageFinishedHelper.waitForCallback(onFinishedCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
+        mTestCommon.loadUrlSyncAndExpectError(
+                awContents, onPageFinishedHelper, onReceivedErrorHelper, url);
     }
 
     /**
@@ -202,18 +182,13 @@
      */
     public void loadUrlAsync(final AwContents awContents,
             final String url) throws Exception {
-        loadUrlAsync(awContents, url, null);
+        mTestCommon.loadUrlAsync(awContents, url);
     }
 
     public void loadUrlAsync(final AwContents awContents,
             final String url,
             final Map<String, String> extraHeaders) {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.loadUrl(url, extraHeaders);
-            }
-        });
+        mTestCommon.loadUrlAsync(awContents, url, extraHeaders);
     }
 
     /**
@@ -222,10 +197,7 @@
     public void postUrlSync(final AwContents awContents,
             CallbackHelper onPageFinishedHelper, final String url,
             byte[] postData) throws Exception {
-        int currentCallCount = onPageFinishedHelper.getCallCount();
-        postUrlAsync(awContents, url, postData);
-        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
+        mTestCommon.postUrlSync(awContents, onPageFinishedHelper, url, postData);
     }
 
     /**
@@ -233,17 +205,7 @@
      */
     public void postUrlAsync(final AwContents awContents,
             final String url, byte[] postData) throws Exception {
-        class PostUrl implements Runnable {
-            byte[] mPostData;
-            public PostUrl(byte[] postData) {
-                mPostData = postData;
-            }
-            @Override
-            public void run() {
-                awContents.postUrl(url, mPostData);
-            }
-        }
-        getInstrumentation().runOnMainSync(new PostUrl(postData));
+        mTestCommon.postUrlAsync(awContents, url, postData);
     }
 
     /**
@@ -253,10 +215,7 @@
             CallbackHelper onPageFinishedHelper,
             final String data, final String mimeType,
             final boolean isBase64Encoded) throws Exception {
-        int currentCallCount = onPageFinishedHelper.getCallCount();
-        loadDataAsync(awContents, data, mimeType, isBase64Encoded);
-        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
+        mTestCommon.loadDataSync(awContents, onPageFinishedHelper, data, mimeType, isBase64Encoded);
     }
 
     public void loadDataSyncWithCharset(final AwContents awContents,
@@ -264,16 +223,8 @@
             final String data, final String mimeType,
             final boolean isBase64Encoded, final String charset)
             throws Exception {
-        int currentCallCount = onPageFinishedHelper.getCallCount();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.loadUrl(LoadUrlParams.createLoadDataParams(
-                        data, mimeType, isBase64Encoded, charset));
-            }
-        });
-        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
+        mTestCommon.loadDataSyncWithCharset(
+                awContents, onPageFinishedHelper, data, mimeType, isBase64Encoded, charset);
     }
 
     /**
@@ -282,34 +233,22 @@
     public void loadDataAsync(final AwContents awContents, final String data,
             final String mimeType, final boolean isBase64Encoded)
             throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.loadData(data, mimeType, isBase64Encoded ? "base64" : null);
-            }
-        });
+        mTestCommon.loadDataAsync(awContents, data, mimeType, isBase64Encoded);
     }
 
     public void loadDataWithBaseUrlSync(final AwContents awContents,
             CallbackHelper onPageFinishedHelper, final String data, final String mimeType,
             final boolean isBase64Encoded, final String baseUrl,
             final String historyUrl) throws Throwable {
-        int currentCallCount = onPageFinishedHelper.getCallCount();
-        loadDataWithBaseUrlAsync(awContents, data, mimeType, isBase64Encoded, baseUrl, historyUrl);
-        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
+        mTestCommon.loadDataWithBaseUrlSync(awContents, onPageFinishedHelper, data, mimeType,
+                isBase64Encoded, baseUrl, historyUrl);
     }
 
     public void loadDataWithBaseUrlAsync(final AwContents awContents,
             final String data, final String mimeType, final boolean isBase64Encoded,
             final String baseUrl, final String historyUrl) throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                awContents.loadDataWithBaseURL(
-                        baseUrl, data, mimeType, isBase64Encoded ? "base64" : null, historyUrl);
-            }
-        });
+        mTestCommon.loadDataWithBaseUrlAsync(
+                awContents, data, mimeType, isBase64Encoded, baseUrl, historyUrl);
     }
 
     /**
@@ -317,104 +256,211 @@
      */
     public void reloadSync(final AwContents awContents,
             CallbackHelper onPageFinishedHelper) throws Exception {
-        int currentCallCount = onPageFinishedHelper.getCallCount();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.getNavigationController().reload(true);
-            }
-        });
-        onPageFinishedHelper.waitForCallback(currentCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
+        mTestCommon.reloadSync(awContents, onPageFinishedHelper);
     }
 
     /**
      * Stops loading on the UI thread.
      */
     public void stopLoading(final AwContents awContents) {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.stopLoading();
-            }
-        });
+        mTestCommon.stopLoading(awContents);
     }
 
     public void waitForVisualStateCallback(final AwContents awContents) throws Exception {
-        final CallbackHelper ch = new CallbackHelper();
-        final int chCount = ch.getCallCount();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                final long requestId = 666;
-                awContents.insertVisualStateCallback(requestId,
-                        new AwContents.VisualStateCallback() {
-                            @Override
-                            public void onComplete(long id) {
-                                assertEquals(requestId, id);
-                                ch.notifyCalled();
-                            }
-                        });
-            }
-        });
-        ch.waitForCallback(chCount);
+        mTestCommon.waitForVisualStateCallback(awContents);
     }
 
     public void insertVisualStateCallbackOnUIThread(final AwContents awContents,
             final long requestId, final AwContents.VisualStateCallback callback) {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.insertVisualStateCallback(requestId, callback);
-            }
-        });
+        mTestCommon.insertVisualStateCallbackOnUIThread(awContents, requestId, callback);
     }
 
-    // Waits for the pixel at the center of AwContents to color up into expectedColor.
-    // Note that this is a stricter condition that waiting for a visual state callback,
-    // as visual state callback only indicates that *something* has appeared in WebView.
+    // Waits for the pixel at the center of AwContents to color up into
+    // expectedColor.
+    // Note that this is a stricter condition that waiting for a visual state
+    // callback,
+    // as visual state callback only indicates that *something* has appeared in
+    // WebView.
     public void waitForPixelColorAtCenterOfView(final AwContents awContents,
             final AwTestContainerView testContainerView, final int expectedColor) throws Exception {
-        pollUiThread(new Callable<Boolean>() {
-            @Override
-            public Boolean call() throws Exception {
-                return GraphicsTestUtils.getPixelColorAtCenterOfView(awContents, testContainerView)
-                        == expectedColor;
-            }
-        });
+        mTestCommon.waitForPixelColorAtCenterOfView(awContents, testContainerView, expectedColor);
+    }
+
+    public AwTestContainerView createAwTestContainerView(
+            final AwContentsClient awContentsClient) {
+        return mTestCommon.createAwTestContainerView(awContentsClient);
+    }
+
+    public AwTestContainerView createAwTestContainerView(final AwContentsClient awContentsClient,
+            boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
+        return mTestCommon.createAwTestContainerView(
+                awContentsClient, supportsLegacyQuirks, testDependencyFactory);
+    }
+
+    public AwBrowserContext getAwBrowserContext() {
+        return mTestCommon.getAwBrowserContext();
+    }
+
+    public AwTestContainerView createDetachedAwTestContainerView(
+            final AwContentsClient awContentsClient) {
+        return mTestCommon.createDetachedAwTestContainerView(awContentsClient);
+    }
+
+    public AwTestContainerView createDetachedAwTestContainerView(
+            final AwContentsClient awContentsClient, boolean supportsLegacyQuirks,
+            TestDependencyFactory testDependencyFactory) {
+        return mTestCommon.createDetachedAwTestContainerView(
+                awContentsClient, supportsLegacyQuirks, testDependencyFactory);
+    }
+
+    protected boolean isHardwareAcceleratedTest() {
+        return mTestCommon.isHardwareAcceleratedTest();
+    }
+
+    public AwTestContainerView createAwTestContainerViewOnMainSync(
+            final AwContentsClient client) throws Exception {
+        return mTestCommon.createAwTestContainerViewOnMainSync(client);
+    }
+
+    public AwTestContainerView createAwTestContainerViewOnMainSync(
+            final AwContentsClient client, final boolean supportsLegacyQuirks) {
+        return mTestCommon.createAwTestContainerViewOnMainSync(client, supportsLegacyQuirks);
+    }
+
+    public AwTestContainerView createAwTestContainerViewOnMainSync(final AwContentsClient client,
+            final boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
+        return mTestCommon.createAwTestContainerViewOnMainSync(
+                client, supportsLegacyQuirks, testDependencyFactory);
+    }
+
+    public void destroyAwContentsOnMainSync(final AwContents awContents) {
+        mTestCommon.destroyAwContentsOnMainSync(awContents);
+    }
+
+    public String getTitleOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.getTitleOnUiThread(awContents);
+    }
+
+    public AwSettings getAwSettingsOnUiThread(
+            final AwContents awContents) throws Exception {
+        return mTestCommon.getAwSettingsOnUiThread(awContents);
     }
 
     /**
-     * Checks the current test has |clazz| annotation. Note this swallows NoSuchMethodException
-     * and returns false in that case.
+     * Verify double quotes in both sides of the raw string. Strip the double
+     * quotes and returns rest of the string.
      */
-    private boolean testMethodHasAnnotation(Class<? extends Annotation> clazz) {
-        String testName = getName();
-        Method method = null;
-        try {
-            method = getClass().getMethod(testName);
-        } catch (NoSuchMethodException e) {
-            Log.w(TAG, "Test method name not found.", e);
-            return false;
-        }
-
-        // Cast to AnnotatedElement to work around a compilation failure.
-        // Method.isAnnotationPresent() was removed in Java 8 (which is used by the Android N SDK),
-        // so compilation with Java 7 fails. See crbug.com/608792.
-        return ((AnnotatedElement) method).isAnnotationPresent(clazz);
+    protected String maybeStripDoubleQuotes(String raw) {
+        return mTestCommon.maybeStripDoubleQuotes(raw);
     }
 
     /**
-     * Factory class used in creation of test AwContents instances.
-     *
-     * Test cases can provide subclass instances to the createAwTest* methods in order to create an
-     * AwContents instance with injected test dependencies.
+     * Executes the given snippet of JavaScript code within the given
+     * ContentView. Returns the result of its execution in JSON format.
+     */
+    public String executeJavaScriptAndWaitForResult(final AwContents awContents,
+            TestAwContentsClient viewClient, final String code) throws Exception {
+        return mTestCommon.executeJavaScriptAndWaitForResult(awContents, viewClient, code);
+    }
+
+    /**
+     * Executes JavaScript code within the given ContentView to get the text content in
+     * document body. Returns the result string without double quotes.
+     */
+    protected String getJavaScriptResultBodyTextContent(
+            final AwContents awContents, final TestAwContentsClient viewClient) throws Exception {
+        return mTestCommon.getJavaScriptResultBodyTextContent(awContents, viewClient);
+    }
+
+    /**
+     * Wrapper around CriteriaHelper.pollInstrumentationThread. This uses AwTestBase-specifc
+     * timeouts and treats timeouts and exceptions as test failures automatically.
+     */
+    public static void pollInstrumentationThread(final Callable<Boolean> callable)
+            throws Exception {
+        AwTestCommon.pollInstrumentationThread(callable);
+    }
+
+    /**
+     * Wrapper around {@link AwTestBase#poll()} but runs the callable on the UI
+     * thread.
+     */
+    public void pollUiThread(final Callable<Boolean> callable) throws Exception {
+        mTestCommon.pollUiThread(callable);
+    }
+
+    /**
+     * Clears the resource cache. Note that the cache is per-application, so
+     * this will clear the cache for all WebViews used.
+     */
+    public void clearCacheOnUiThread(
+            final AwContents awContents,
+            final boolean includeDiskFiles) throws Exception {
+        mTestCommon.clearCacheOnUiThread(awContents, includeDiskFiles);
+    }
+
+    /**
+     * Returns pure page scale.
+     */
+    public float getScaleOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.getScaleOnUiThread(awContents);
+    }
+
+    /**
+     * Returns page scale multiplied by the screen density.
+     */
+    public float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.getPixelScaleOnUiThread(awContents);
+    }
+
+    /**
+     * Returns whether a user can zoom the page in.
+     */
+    public boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.canZoomInOnUiThread(awContents);
+    }
+
+    /**
+     * Returns whether a user can zoom the page out.
+     */
+    public boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
+        return mTestCommon.canZoomOutOnUiThread(awContents);
+    }
+
+    public void killRenderProcessOnUiThreadAsync(final AwContents awContents) throws Exception {
+        mTestCommon.killRenderProcessOnUiThreadAsync(awContents);
+    }
+
+    /**
+     * Loads the main html then triggers the popup window.
+     */
+    public void triggerPopup(final AwContents parentAwContents,
+            TestAwContentsClient parentAwContentsClient, TestWebServer testWebServer,
+            String mainHtml, String popupHtml, String popupPath, String triggerScript)
+            throws Exception {
+        mTestCommon.triggerPopup(parentAwContents, parentAwContentsClient, testWebServer, mainHtml,
+                popupHtml, popupPath, triggerScript);
+    }
+
+    /**
+     * Supplies the popup window with AwContents then waits for the popup window
+     * to finish loading.
+     */
+    public PopupInfo connectPendingPopup(final AwContents parentAwContents) throws Exception {
+        return mTestCommon.connectPendingPopup(parentAwContents);
+    }
+
+    /**
+     * Factory class used in creation of test AwContents instances. Test cases
+     * can provide subclass instances to the createAwTest* methods in order to
+     * create an AwContents instance with injected test dependencies.
      */
     public static class TestDependencyFactory extends AwContents.DependencyFactory {
-        public AwTestContainerView createAwTestContainerView(AwTestRunnerActivity activity,
-                boolean allowHardwareAcceleration) {
+        public AwTestContainerView createAwTestContainerView(
+                AwTestRunnerActivity activity, boolean allowHardwareAcceleration) {
             return new AwTestContainerView(activity, allowHardwareAcceleration);
         }
+
         public AwSettings createAwSettings(Context context, boolean supportsLegacyQuirks) {
             return new AwSettings(context, false /* isAccessFromFileURLsGrantedByDefault */,
                     supportsLegacyQuirks, false /* allowEmptyDocumentPersistence */,
@@ -432,279 +478,6 @@
         }
     }
 
-    protected TestDependencyFactory createTestDependencyFactory() {
-        return new TestDependencyFactory();
-    }
-
-    public AwTestContainerView createAwTestContainerView(
-            final AwContentsClient awContentsClient) {
-        return createAwTestContainerView(awContentsClient, false, null);
-    }
-
-    public AwTestContainerView createAwTestContainerView(final AwContentsClient awContentsClient,
-            boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
-        AwTestContainerView testContainerView = createDetachedAwTestContainerView(
-                awContentsClient, supportsLegacyQuirks, testDependencyFactory);
-        getActivity().addView(testContainerView);
-        testContainerView.requestFocus();
-        return testContainerView;
-    }
-
-    public AwBrowserContext getAwBrowserContext() {
-        return mBrowserContext;
-    }
-
-    public AwTestContainerView createDetachedAwTestContainerView(
-            final AwContentsClient awContentsClient) {
-        return createDetachedAwTestContainerView(awContentsClient, false, null);
-    }
-
-    public AwTestContainerView createDetachedAwTestContainerView(
-            final AwContentsClient awContentsClient, boolean supportsLegacyQuirks,
-            TestDependencyFactory testDependencyFactory) {
-        if (testDependencyFactory == null) {
-            testDependencyFactory = createTestDependencyFactory();
-        }
-        boolean allowHardwareAcceleration = isHardwareAcceleratedTest();
-        final AwTestContainerView testContainerView =
-                testDependencyFactory.createAwTestContainerView(
-                        getActivity(), allowHardwareAcceleration);
-
-        AwSettings awSettings =
-                testDependencyFactory.createAwSettings(getActivity(), supportsLegacyQuirks);
-        AwContents awContents = testDependencyFactory.createAwContents(mBrowserContext,
-                testContainerView, testContainerView.getContext(),
-                testContainerView.getInternalAccessDelegate(),
-                testContainerView.getNativeDrawGLFunctorFactory(), awContentsClient, awSettings,
-                testDependencyFactory);
-        testContainerView.initialize(awContents);
-        return testContainerView;
-    }
-
-    protected boolean isHardwareAcceleratedTest() {
-        return !testMethodHasAnnotation(DisableHardwareAccelerationForTest.class);
-    }
-
-    public AwTestContainerView createAwTestContainerViewOnMainSync(
-            final AwContentsClient client) throws Exception {
-        return createAwTestContainerViewOnMainSync(client, false, null);
-    }
-
-    public AwTestContainerView createAwTestContainerViewOnMainSync(
-            final AwContentsClient client, final boolean supportsLegacyQuirks) {
-        return createAwTestContainerViewOnMainSync(client, supportsLegacyQuirks, null);
-    }
-
-    public AwTestContainerView createAwTestContainerViewOnMainSync(final AwContentsClient client,
-            final boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<AwTestContainerView>() {
-            @Override
-            public AwTestContainerView call() {
-                return createAwTestContainerView(
-                        client, supportsLegacyQuirks, testDependencyFactory);
-            }
-        });
-    }
-
-    public void destroyAwContentsOnMainSync(final AwContents awContents) {
-        if (awContents == null) return;
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.destroy();
-            }
-        });
-    }
-
-    public String getTitleOnUiThread(final AwContents awContents) throws Exception {
-        return runTestOnUiThreadAndGetResult(new Callable<String>() {
-            @Override
-            public String call() throws Exception {
-                return awContents.getTitle();
-            }
-        });
-    }
-
-    public AwSettings getAwSettingsOnUiThread(
-            final AwContents awContents) throws Exception {
-        return runTestOnUiThreadAndGetResult(new Callable<AwSettings>() {
-            @Override
-            public AwSettings call() throws Exception {
-                return awContents.getSettings();
-            }
-        });
-    }
-
-    /**
-     * Verify double quotes in both sides of the raw string. Strip the double quotes and
-     * returns rest of the string.
-     */
-    protected String maybeStripDoubleQuotes(String raw) {
-        assertNotNull(raw);
-        Matcher m = MAYBE_QUOTED_STRING.matcher(raw);
-        assertTrue(m.matches());
-        return m.group(2);
-    }
-
-    /**
-     * Executes the given snippet of JavaScript code within the given ContentView. Returns the
-     * result of its execution in JSON format.
-     */
-    public String executeJavaScriptAndWaitForResult(final AwContents awContents,
-            TestAwContentsClient viewClient, final String code) throws Exception {
-        return JSUtils.executeJavaScriptAndWaitForResult(this, awContents,
-                viewClient.getOnEvaluateJavaScriptResultHelper(),
-                code);
-    }
-
-    /**
-     * Executes JavaScript code within the given ContentView to get the text content in
-     * document body. Returns the result string without double quotes.
-     */
-    protected String getJavaScriptResultBodyTextContent(
-            final AwContents awContents, final TestAwContentsClient viewClient) throws Exception {
-        String raw = executeJavaScriptAndWaitForResult(
-                awContents, viewClient, "document.body.textContent");
-        return maybeStripDoubleQuotes(raw);
-    }
-
-    /**
-     * Wrapper around CriteriaHelper.pollInstrumentationThread. This uses AwTestBase-specifc
-     * timeouts and treats timeouts and exceptions as test failures automatically.
-     */
-    public static void pollInstrumentationThread(final Callable<Boolean> callable)
-            throws Exception {
-        CriteriaHelper.pollInstrumentationThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                try {
-                    return callable.call();
-                } catch (Throwable e) {
-                    Log.e(TAG, "Exception while polling.", e);
-                    return false;
-                }
-            }
-        }, WAIT_TIMEOUT_MS, CHECK_INTERVAL);
-    }
-
-    /**
-     * Wrapper around {@link AwTestBase#poll()} but runs the callable on the UI thread.
-     */
-    public void pollUiThread(final Callable<Boolean> callable) throws Exception {
-        pollInstrumentationThread(new Callable<Boolean>() {
-            @Override
-            public Boolean call() throws Exception {
-                return runTestOnUiThreadAndGetResult(callable);
-            }
-        });
-    }
-
-    /**
-     * Clears the resource cache. Note that the cache is per-application, so this will clear the
-     * cache for all WebViews used.
-     */
-    public void clearCacheOnUiThread(
-            final AwContents awContents,
-            final boolean includeDiskFiles) throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.clearCache(includeDiskFiles);
-            }
-        });
-    }
-
-    /**
-     * Returns pure page scale.
-     */
-    public float getScaleOnUiThread(final AwContents awContents) throws Exception {
-        return runTestOnUiThreadAndGetResult(new Callable<Float>() {
-            @Override
-            public Float call() throws Exception {
-                return awContents.getPageScaleFactor();
-            }
-        });
-    }
-
-    /**
-     * Returns page scale multiplied by the screen density.
-     */
-    public float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
-        return runTestOnUiThreadAndGetResult(new Callable<Float>() {
-            @Override
-            public Float call() throws Exception {
-                return awContents.getScale();
-            }
-        });
-    }
-
-    /**
-     * Returns whether a user can zoom the page in.
-     */
-    public boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
-        return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
-            @Override
-            public Boolean call() throws Exception {
-                return awContents.canZoomIn();
-            }
-        });
-    }
-
-    /**
-     * Returns whether a user can zoom the page out.
-     */
-    public boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
-        return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
-            @Override
-            public Boolean call() throws Exception {
-                return awContents.canZoomOut();
-            }
-        });
-    }
-
-    public void killRenderProcessOnUiThreadAsync(final AwContents awContents) throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                awContents.killRenderProcess();
-            }
-        });
-    }
-
-    /**
-     * Loads the main html then triggers the popup window.
-     */
-    public void triggerPopup(final AwContents parentAwContents,
-            TestAwContentsClient parentAwContentsClient, TestWebServer testWebServer,
-            String mainHtml, String popupHtml, String popupPath, String triggerScript)
-            throws Exception {
-        enableJavaScriptOnUiThread(parentAwContents);
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                parentAwContents.getSettings().setSupportMultipleWindows(true);
-                parentAwContents.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
-            }
-        });
-
-        final String parentUrl = testWebServer.setResponse("/popupParent.html", mainHtml, null);
-        if (popupHtml != null) {
-            testWebServer.setResponse(popupPath, popupHtml, null);
-        } else {
-            testWebServer.setResponseWithNoContentStatus(popupPath);
-        }
-
-        parentAwContentsClient.getOnCreateWindowHelper().setReturnValue(true);
-        loadUrlSync(parentAwContents, parentAwContentsClient.getOnPageFinishedHelper(), parentUrl);
-
-        TestAwContentsClient.OnCreateWindowHelper onCreateWindowHelper =
-                parentAwContentsClient.getOnCreateWindowHelper();
-        int currentCallCount = onCreateWindowHelper.getCallCount();
-        parentAwContents.evaluateJavaScriptForTests(triggerScript, null);
-        onCreateWindowHelper.waitForCallback(
-                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-    }
-
     /**
      * POD object for holding references to helper objects of a popup window.
      */
@@ -712,6 +485,7 @@
         public final TestAwContentsClient popupContentsClient;
         public final AwTestContainerView popupContainerView;
         public final AwContents popupContents;
+
         public PopupInfo(TestAwContentsClient popupContentsClient,
                 AwTestContainerView popupContainerView, AwContents popupContents) {
             this.popupContentsClient = popupContentsClient;
@@ -719,37 +493,4 @@
             this.popupContents = popupContents;
         }
     }
-
-    /**
-     * Supplies the popup window with AwContents then waits for the popup window to finish loading.
-     */
-    public PopupInfo connectPendingPopup(final AwContents parentAwContents) throws Exception {
-        TestAwContentsClient popupContentsClient;
-        AwTestContainerView popupContainerView;
-        final AwContents popupContents;
-        popupContentsClient = new TestAwContentsClient();
-        popupContainerView = createAwTestContainerViewOnMainSync(popupContentsClient);
-        popupContents = popupContainerView.getAwContents();
-        enableJavaScriptOnUiThread(popupContents);
-
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                parentAwContents.supplyContentsForPopup(popupContents);
-            }
-        });
-
-        OnPageFinishedHelper onPageFinishedHelper = popupContentsClient.getOnPageFinishedHelper();
-        int finishCallCount = onPageFinishedHelper.getCallCount();
-        TestAwContentsClient.OnReceivedTitleHelper onReceivedTitleHelper =
-                popupContentsClient.getOnReceivedTitleHelper();
-        int titleCallCount = onReceivedTitleHelper.getCallCount();
-
-        onPageFinishedHelper.waitForCallback(finishCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
-        onReceivedTitleHelper.waitForCallback(titleCallCount, 1, WAIT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
-
-        return new PopupInfo(popupContentsClient, popupContainerView, popupContents);
-    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestCommon.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestCommon.java
new file mode 100644
index 0000000..5ea6498
--- /dev/null
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestCommon.java
@@ -0,0 +1,570 @@
+// 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.
+
+package org.chromium.android_webview.test;
+
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.util.AndroidRuntimeException;
+
+import org.junit.Assert;
+
+import org.chromium.android_webview.AwBrowserContext;
+import org.chromium.android_webview.AwBrowserProcess;
+import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwContentsClient;
+import org.chromium.android_webview.AwSettings;
+import org.chromium.android_webview.test.AwTestBase.PopupInfo;
+import org.chromium.android_webview.test.AwTestBase.TestDependencyFactory;
+import org.chromium.android_webview.test.util.GraphicsTestUtils;
+import org.chromium.android_webview.test.util.JSUtils;
+import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.InMemorySharedPreferences;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.net.test.util.TestWebServer;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+// TODO(yolandyan): move this class to its test rule once JUnit4 migration is over
+final class AwTestCommon {
+    static final long WAIT_TIMEOUT_MS = scaleTimeout(15000);
+
+    static final int CHECK_INTERVAL = 100;
+
+    private static final String TAG = "AwTestCommon";
+
+    private static final Pattern MAYBE_QUOTED_STRING = Pattern.compile("^(\"?)(.*)\\1$");
+
+    // The browser context needs to be a process-wide singleton.
+    private AwBrowserContext mBrowserContext;
+
+    private final AwTestCommonCallback mCallback;
+
+    AwTestCommon(AwTestCommonCallback callback) {
+        mCallback = callback;
+    }
+
+    void setUp() throws Exception {
+        if (mCallback.needsAwBrowserContextCreated()) {
+            createAwBrowserContext();
+        }
+        if (mCallback.needsBrowserProcessStarted()) {
+            startBrowserProcess();
+        }
+    }
+
+    void createAwBrowserContext() {
+        if (mBrowserContext != null) {
+            throw new AndroidRuntimeException("There should only be one browser context.");
+        }
+        mCallback.getActivity(); // The Activity must be launched in order to load native code
+        final InMemorySharedPreferences prefs = new InMemorySharedPreferences();
+        final Context appContext =
+                mCallback.getInstrumentation().getTargetContext().getApplicationContext();
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBrowserContext =
+                        mCallback.createAwBrowserContextOnUiThread(prefs, appContext);
+            }
+        });
+    }
+
+    void startBrowserProcess() throws Exception {
+        // The Activity must be launched in order for proper webview statics to be setup.
+        mCallback.getActivity();
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                AwBrowserProcess.start();
+            }
+        });
+    }
+
+    <R> R runTestOnUiThreadAndGetResult(Callable<R> callable) throws Exception {
+        FutureTask<R> task = new FutureTask<R>(callable);
+        mCallback.getInstrumentation().runOnMainSync(task);
+        return task.get();
+    }
+
+    void enableJavaScriptOnUiThread(final AwContents awContents) {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.getSettings().setJavaScriptEnabled(true);
+            }
+        });
+    }
+
+    void setNetworkAvailableOnUiThread(final AwContents awContents, final boolean networkUp) {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.setNetworkAvailable(networkUp);
+            }
+        });
+    }
+
+    void loadUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String url) throws Exception {
+        loadUrlSync(awContents, onPageFinishedHelper, url, null);
+    }
+
+    void loadUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String url, final Map<String, String> extraHeaders) throws Exception {
+        int currentCallCount = onPageFinishedHelper.getCallCount();
+        loadUrlAsync(awContents, url, extraHeaders);
+        onPageFinishedHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    void loadUrlSyncAndExpectError(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            CallbackHelper onReceivedErrorHelper, final String url) throws Exception {
+        int onErrorCallCount = onReceivedErrorHelper.getCallCount();
+        int onFinishedCallCount = onPageFinishedHelper.getCallCount();
+        loadUrlAsync(awContents, url);
+        onReceivedErrorHelper.waitForCallback(
+                onErrorCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        onPageFinishedHelper.waitForCallback(
+                onFinishedCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    void loadUrlAsync(final AwContents awContents, final String url) throws Exception {
+        loadUrlAsync(awContents, url, null);
+    }
+
+    void loadUrlAsync(
+            final AwContents awContents, final String url, final Map<String, String> extraHeaders) {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.loadUrl(url, extraHeaders);
+            }
+        });
+    }
+
+    void postUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String url, byte[] postData) throws Exception {
+        int currentCallCount = onPageFinishedHelper.getCallCount();
+        postUrlAsync(awContents, url, postData);
+        onPageFinishedHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    void postUrlAsync(final AwContents awContents, final String url, byte[] postData)
+            throws Exception {
+        class PostUrl implements Runnable {
+            byte[] mPostData;
+            public PostUrl(byte[] postData) {
+                mPostData = postData;
+            }
+            @Override
+            public void run() {
+                awContents.postUrl(url, mPostData);
+            }
+        }
+        mCallback.getInstrumentation().runOnMainSync(new PostUrl(postData));
+    }
+
+    void loadDataSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String data, final String mimeType, final boolean isBase64Encoded)
+            throws Exception {
+        int currentCallCount = onPageFinishedHelper.getCallCount();
+        loadDataAsync(awContents, data, mimeType, isBase64Encoded);
+        onPageFinishedHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    void loadDataSyncWithCharset(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String data, final String mimeType, final boolean isBase64Encoded,
+            final String charset) throws Exception {
+        int currentCallCount = onPageFinishedHelper.getCallCount();
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.loadUrl(LoadUrlParams.createLoadDataParams(
+                        data, mimeType, isBase64Encoded, charset));
+            }
+        });
+        onPageFinishedHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    void loadDataAsync(final AwContents awContents, final String data, final String mimeType,
+            final boolean isBase64Encoded) throws Exception {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.loadData(data, mimeType, isBase64Encoded ? "base64" : null);
+            }
+        });
+    }
+
+    void loadDataWithBaseUrlSync(final AwContents awContents, CallbackHelper onPageFinishedHelper,
+            final String data, final String mimeType, final boolean isBase64Encoded,
+            final String baseUrl, final String historyUrl) throws Throwable {
+        int currentCallCount = onPageFinishedHelper.getCallCount();
+        loadDataWithBaseUrlAsync(awContents, data, mimeType, isBase64Encoded, baseUrl, historyUrl);
+        onPageFinishedHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    void loadDataWithBaseUrlAsync(final AwContents awContents, final String data,
+            final String mimeType, final boolean isBase64Encoded, final String baseUrl,
+            final String historyUrl) throws Throwable {
+        mCallback.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                awContents.loadDataWithBaseURL(
+                        baseUrl, data, mimeType, isBase64Encoded ? "base64" : null, historyUrl);
+            }
+        });
+    }
+
+    void reloadSync(final AwContents awContents, CallbackHelper onPageFinishedHelper)
+            throws Exception {
+        int currentCallCount = onPageFinishedHelper.getCallCount();
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.getNavigationController().reload(true);
+            }
+        });
+        onPageFinishedHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    void stopLoading(final AwContents awContents) {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.stopLoading();
+            }
+        });
+    }
+
+    void waitForVisualStateCallback(final AwContents awContents) throws Exception {
+        final CallbackHelper ch = new CallbackHelper();
+        final int chCount = ch.getCallCount();
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                final long requestId = 666;
+                awContents.insertVisualStateCallback(
+                        requestId, new AwContents.VisualStateCallback() {
+                            @Override
+                            public void onComplete(long id) {
+                                Assert.assertEquals(requestId, id);
+                                ch.notifyCalled();
+                            }
+                        });
+            }
+        });
+        ch.waitForCallback(chCount);
+    }
+
+    void insertVisualStateCallbackOnUIThread(final AwContents awContents, final long requestId,
+            final AwContents.VisualStateCallback callback) {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.insertVisualStateCallback(requestId, callback);
+            }
+        });
+    }
+
+    void waitForPixelColorAtCenterOfView(final AwContents awContents,
+            final AwTestContainerView testContainerView, final int expectedColor) throws Exception {
+        pollUiThread(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return GraphicsTestUtils.getPixelColorAtCenterOfView(awContents, testContainerView)
+                        == expectedColor;
+            }
+        });
+    }
+
+    AwTestContainerView createAwTestContainerView(final AwContentsClient awContentsClient) {
+        return createAwTestContainerView(awContentsClient, false, null);
+    }
+
+    AwTestContainerView createAwTestContainerView(final AwContentsClient awContentsClient,
+            boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
+        AwTestContainerView testContainerView = createDetachedAwTestContainerView(
+                awContentsClient, supportsLegacyQuirks, testDependencyFactory);
+        mCallback.getActivity().addView(testContainerView);
+        testContainerView.requestFocus();
+        return testContainerView;
+    }
+
+    AwBrowserContext getAwBrowserContext() {
+        return mBrowserContext;
+    }
+
+    AwTestContainerView createDetachedAwTestContainerView(final AwContentsClient awContentsClient) {
+        return createDetachedAwTestContainerView(awContentsClient, false, null);
+    }
+
+    AwTestContainerView createDetachedAwTestContainerView(final AwContentsClient awContentsClient,
+            boolean supportsLegacyQuirks, TestDependencyFactory testDependencyFactory) {
+        if (testDependencyFactory == null) {
+            testDependencyFactory = mCallback.createTestDependencyFactory();
+        }
+        boolean allowHardwareAcceleration = isHardwareAcceleratedTest();
+        final AwTestContainerView testContainerView =
+                testDependencyFactory.createAwTestContainerView(
+                        mCallback.getActivity(), allowHardwareAcceleration);
+
+        AwSettings awSettings = testDependencyFactory.createAwSettings(
+                mCallback.getActivity(), supportsLegacyQuirks);
+        AwContents awContents = testDependencyFactory.createAwContents(mBrowserContext,
+                testContainerView, testContainerView.getContext(),
+                testContainerView.getInternalAccessDelegate(),
+                testContainerView.getNativeDrawGLFunctorFactory(), awContentsClient, awSettings,
+                testDependencyFactory);
+        testContainerView.initialize(awContents);
+        return testContainerView;
+    }
+
+    boolean isHardwareAcceleratedTest() {
+        return !mCallback.testMethodHasAnnotation(DisableHardwareAccelerationForTest.class);
+    }
+
+    AwTestContainerView createAwTestContainerViewOnMainSync(final AwContentsClient client)
+            throws Exception {
+        return createAwTestContainerViewOnMainSync(client, false, null);
+    }
+
+    AwTestContainerView createAwTestContainerViewOnMainSync(
+            final AwContentsClient client, final boolean supportsLegacyQuirks) {
+        return createAwTestContainerViewOnMainSync(client, supportsLegacyQuirks, null);
+    }
+
+    AwTestContainerView createAwTestContainerViewOnMainSync(final AwContentsClient client,
+            final boolean supportsLegacyQuirks, final TestDependencyFactory testDependencyFactory) {
+        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<AwTestContainerView>() {
+            @Override
+            public AwTestContainerView call() {
+                return createAwTestContainerView(
+                        client, supportsLegacyQuirks, testDependencyFactory);
+            }
+        });
+    }
+
+    void destroyAwContentsOnMainSync(final AwContents awContents) {
+        if (awContents == null) return;
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.destroy();
+            }
+        });
+    }
+
+    String getTitleOnUiThread(final AwContents awContents) throws Exception {
+        return runTestOnUiThreadAndGetResult(new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                return awContents.getTitle();
+            }
+        });
+    }
+
+    AwSettings getAwSettingsOnUiThread(final AwContents awContents) throws Exception {
+        return runTestOnUiThreadAndGetResult(new Callable<AwSettings>() {
+            @Override
+            public AwSettings call() throws Exception {
+                return awContents.getSettings();
+            }
+        });
+    }
+
+    String maybeStripDoubleQuotes(String raw) {
+        Assert.assertNotNull(raw);
+        Matcher m = MAYBE_QUOTED_STRING.matcher(raw);
+        Assert.assertTrue(m.matches());
+        return m.group(2);
+    }
+
+    String executeJavaScriptAndWaitForResult(final AwContents awContents,
+            TestAwContentsClient viewClient, final String code) throws Exception {
+        return JSUtils.executeJavaScriptAndWaitForResult(mCallback.getInstrumentation(), awContents,
+                viewClient.getOnEvaluateJavaScriptResultHelper(), code);
+    }
+
+    /**
+     * Executes JavaScript code within the given ContentView to get the text content in
+     * document body. Returns the result string without double quotes.
+     */
+    String getJavaScriptResultBodyTextContent(
+            final AwContents awContents, final TestAwContentsClient viewClient) throws Exception {
+        String raw = executeJavaScriptAndWaitForResult(
+                awContents, viewClient, "document.body.textContent");
+        return maybeStripDoubleQuotes(raw);
+    }
+
+    static void pollInstrumentationThread(final Callable<Boolean> callable) throws Exception {
+        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                try {
+                    return callable.call();
+                } catch (Throwable e) {
+                    Log.e(TAG, "Exception while polling.", e);
+                    return false;
+                }
+            }
+        }, WAIT_TIMEOUT_MS, CHECK_INTERVAL);
+    }
+
+    void pollUiThread(final Callable<Boolean> callable) throws Exception {
+        pollInstrumentationThread(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return runTestOnUiThreadAndGetResult(callable);
+            }
+        });
+    }
+
+    void clearCacheOnUiThread(final AwContents awContents, final boolean includeDiskFiles)
+            throws Exception {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.clearCache(includeDiskFiles);
+            }
+        });
+    }
+
+    float getScaleOnUiThread(final AwContents awContents) throws Exception {
+        return runTestOnUiThreadAndGetResult(new Callable<Float>() {
+            @Override
+            public Float call() throws Exception {
+                return awContents.getPageScaleFactor();
+            }
+        });
+    }
+
+    float getPixelScaleOnUiThread(final AwContents awContents) throws Exception {
+        return runTestOnUiThreadAndGetResult(new Callable<Float>() {
+            @Override
+            public Float call() throws Exception {
+                return awContents.getScale();
+            }
+        });
+    }
+
+    boolean canZoomInOnUiThread(final AwContents awContents) throws Exception {
+        return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return awContents.canZoomIn();
+            }
+        });
+    }
+
+    boolean canZoomOutOnUiThread(final AwContents awContents) throws Exception {
+        return runTestOnUiThreadAndGetResult(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return awContents.canZoomOut();
+            }
+        });
+    }
+
+    void killRenderProcessOnUiThreadAsync(final AwContents awContents) throws Exception {
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                awContents.killRenderProcess();
+            }
+        });
+    }
+
+    void triggerPopup(final AwContents parentAwContents,
+            TestAwContentsClient parentAwContentsClient, TestWebServer testWebServer,
+            String mainHtml, String popupHtml, String popupPath, String triggerScript)
+            throws Exception {
+        enableJavaScriptOnUiThread(parentAwContents);
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                parentAwContents.getSettings().setSupportMultipleWindows(true);
+                parentAwContents.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
+            }
+        });
+
+        final String parentUrl = testWebServer.setResponse("/popupParent.html", mainHtml, null);
+        if (popupHtml != null) {
+            testWebServer.setResponse(popupPath, popupHtml, null);
+        } else {
+            testWebServer.setResponseWithNoContentStatus(popupPath);
+        }
+
+        parentAwContentsClient.getOnCreateWindowHelper().setReturnValue(true);
+        loadUrlSync(parentAwContents, parentAwContentsClient.getOnPageFinishedHelper(), parentUrl);
+
+        TestAwContentsClient.OnCreateWindowHelper onCreateWindowHelper =
+                parentAwContentsClient.getOnCreateWindowHelper();
+        int currentCallCount = onCreateWindowHelper.getCallCount();
+        parentAwContents.evaluateJavaScriptForTests(triggerScript, null);
+        onCreateWindowHelper.waitForCallback(
+                currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    PopupInfo connectPendingPopup(final AwContents parentAwContents) throws Exception {
+        TestAwContentsClient popupContentsClient;
+        AwTestContainerView popupContainerView;
+        final AwContents popupContents;
+        popupContentsClient = new TestAwContentsClient();
+        popupContainerView = createAwTestContainerViewOnMainSync(popupContentsClient);
+        popupContents = popupContainerView.getAwContents();
+        enableJavaScriptOnUiThread(popupContents);
+
+        mCallback.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                parentAwContents.supplyContentsForPopup(popupContents);
+            }
+        });
+
+        OnPageFinishedHelper onPageFinishedHelper = popupContentsClient.getOnPageFinishedHelper();
+        int finishCallCount = onPageFinishedHelper.getCallCount();
+        TestAwContentsClient.OnReceivedTitleHelper onReceivedTitleHelper =
+                popupContentsClient.getOnReceivedTitleHelper();
+        int titleCallCount = onReceivedTitleHelper.getCallCount();
+
+        onPageFinishedHelper.waitForCallback(
+                finishCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        onReceivedTitleHelper.waitForCallback(
+                titleCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
+        return new PopupInfo(popupContentsClient, popupContainerView, popupContents);
+    }
+
+    interface AwTestCommonCallback {
+        Instrumentation getInstrumentation();
+        AwTestRunnerActivity getActivity();
+        void runOnUiThread(Runnable runnable) throws Throwable;
+        boolean testMethodHasAnnotation(Class<? extends Annotation> clazz);
+        AwBrowserContext createAwBrowserContextOnUiThread(
+                InMemorySharedPreferences prefs, Context appContext);
+        TestDependencyFactory createTestDependencyFactory();
+        boolean needsAwBrowserContextCreated();
+        boolean needsBrowserProcessStarted();
+    }
+}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
index 8e39f2e6..1aa25a7 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
@@ -264,7 +264,7 @@
             int onPageFinishedCallCount = onPageFinishedHelper.getCallCount();
             int onPageStartedCallCount = onPageStartedHelper.getCallCount();
 
-            JSUtils.clickOnLinkUsingJs(this, mAwContents,
+            JSUtils.clickOnLinkUsingJs(getInstrumentation(), mAwContents,
                     mContentsClient.getOnEvaluateJavaScriptResultHelper(), "link");
 
             onPageFinishedHelper.waitForCallback(onPageFinishedCallCount);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java
index d49a3de4..777ff9c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java
@@ -43,12 +43,12 @@
     }
 
     @Override
-    protected boolean needsAwBrowserContextCreated() {
+    public boolean needsAwBrowserContextCreated() {
         return false;
     }
 
     @Override
-    protected boolean needsBrowserProcessStarted() {
+    public boolean needsBrowserProcessStarted() {
         return false;
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index f094c9a..d9405ab5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -114,13 +114,12 @@
 
     private void setCookieWithJavaScript(final String name, final String value)
             throws Throwable {
-        JSUtils.executeJavaScriptAndWaitForResult(
-                this, mAwContents,
+        JSUtils.executeJavaScriptAndWaitForResult(getInstrumentation(), mAwContents,
                 mContentsClient.getOnEvaluateJavaScriptResultHelper(),
                 "var expirationDate = new Date();"
-                + "expirationDate.setDate(expirationDate.getDate() + 5);"
-                + "document.cookie='" + name + "=" + value
-                + "; expires=' + expirationDate.toUTCString();");
+                        + "expirationDate.setDate(expirationDate.getDate() + 5);"
+                        + "document.cookie='" + name + "=" + value
+                        + "; expires=' + expirationDate.toUTCString();");
     }
 
     @MediumTest
@@ -366,15 +365,14 @@
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
     public void testThirdPartyCookie() throws Throwable {
-        // In theory we need two servers to test this, one server ('the first party') which returns
-        // a response with a link to a second server ('the third party') at different origin. This
-        // second server attempts to set a cookie which should fail if AcceptThirdPartyCookie() is
-        // false.
-        // Strictly according to the letter of RFC6454 it should be possible to set this situation
-        // up with two TestServers on different ports (these count as having different origins) but
-        // Chrome is not strict about this and does not check the port. Instead we cheat making some
-        // of the urls come from localhost and some from 127.0.0.1 which count (both in theory and
-        // pratice) as having different origins.
+        // In theory we need two servers to test this, one server ('the first
+        // party') which returns a response with a link to a second server ('the third party') at
+        // different origin. This second server attempts to set a cookie which should fail if
+        // AcceptThirdPartyCookie() is false. Strictly according to the letter of RFC6454 it should
+        // be possible to set this situation up with two TestServers on different ports (these count
+        // as having different origins) but Chrome is not strict about this and does not check the
+        // port. Instead we cheat making some of the urls come from localhost and some from
+        // 127.0.0.1 which count (both in theory and pratice) as having different origins.
         TestWebServer webServer = TestWebServer.start();
         try {
             // Turn global allow on.
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
index c636700..494855b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
@@ -102,7 +102,7 @@
     }
 
     @Override
-    protected TestDependencyFactory createTestDependencyFactory() {
+    public TestDependencyFactory createTestDependencyFactory() {
         return mOverridenFactory == null ? new TestDependencyFactory() : mOverridenFactory;
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java
index d08ee36..2c42c0a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/HttpCacheTest.java
@@ -18,7 +18,7 @@
 public class HttpCacheTest extends AwTestBase {
 
     @Override
-    protected boolean needsBrowserProcessStarted() {
+    public boolean needsBrowserProcessStarted() {
         return false;
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
index 5ac1e250..675ff54 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
@@ -288,8 +288,8 @@
         validateHeadersValue(awContents, contentsClient, extraHeaders, true);
 
         int currentCallCount = contentsClient.getOnPageFinishedHelper().getCallCount();
-        JSUtils.clickOnLinkUsingJs(
-                this, awContents, contentsClient.getOnEvaluateJavaScriptResultHelper(), "click");
+        JSUtils.clickOnLinkUsingJs(getInstrumentation(), awContents,
+                contentsClient.getOnEvaluateJavaScriptResultHelper(), "click");
         contentsClient.getOnPageFinishedHelper().waitForCallback(
                 currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         // No extra headers for the page navigated via clicking.
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index 220cfad..e459e3a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -310,7 +310,7 @@
      * sites are malicious
      */
     @Override
-    protected AwBrowserContext createAwBrowserContextOnUiThread(
+    public AwBrowserContext createAwBrowserContextOnUiThread(
             InMemorySharedPreferences prefs, Context appContext) {
         return new MockAwBrowserContext(prefs, appContext);
     }
@@ -984,4 +984,15 @@
         // trace unless on the instrumentation thread.
         assertTrue("Callback should run on UI Thread", mOnUiThread);
     }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testGetSafeBrowsingPrivacyPolicyUrl() throws Throwable {
+        final Uri privacyPolicyUrl = Uri.parse("https://www.google.com/chrome/browser/privacy/")
+                                             .buildUpon()
+                                             .fragment("safe-browsing-policies")
+                                             .build();
+        assertEquals(privacyPolicyUrl, AwContentsStatics.getSafeBrowsingPrivacyPolicyUrl());
+        assertNotNull(AwContentsStatics.getSafeBrowsingPrivacyPolicyUrl());
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/JSUtils.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/JSUtils.java
index 306be04..4f35e75 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/util/JSUtils.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/JSUtils.java
@@ -4,12 +4,12 @@
 
 package org.chromium.android_webview.test.util;
 
-import android.test.InstrumentationTestCase;
-
-import junit.framework.Assert;
-
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
+import android.app.Instrumentation;
+
+import org.junit.Assert;
+
 import org.chromium.android_webview.AwContents;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
@@ -22,18 +22,16 @@
     private static final long WAIT_TIMEOUT_MS = scaleTimeout(2000);
     private static final int CHECK_INTERVAL = 100;
 
-    public static void clickOnLinkUsingJs(
-            final InstrumentationTestCase testCase,
+    public static void clickOnLinkUsingJs(final Instrumentation instrumentation,
             final AwContents awContents,
             final OnEvaluateJavaScriptResultHelper onEvaluateJavaScriptResultHelper,
             final String linkId) throws Exception {
-
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
                 try {
-                    String linkIsNotNull = executeJavaScriptAndWaitForResult(testCase, awContents,
-                            onEvaluateJavaScriptResultHelper,
+                    String linkIsNotNull = executeJavaScriptAndWaitForResult(instrumentation,
+                            awContents, onEvaluateJavaScriptResultHelper,
                             "document.getElementById('" + linkId + "') != null");
                     return linkIsNotNull.equals("true");
                 } catch (Throwable t) {
@@ -44,7 +42,7 @@
             }
         }, WAIT_TIMEOUT_MS, CHECK_INTERVAL);
 
-        testCase.getInstrumentation().runOnMainSync(new Runnable() {
+        instrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 awContents.getWebContents().evaluateJavaScriptForTests(
@@ -56,12 +54,11 @@
         });
     }
 
-    public static String executeJavaScriptAndWaitForResult(
-            InstrumentationTestCase testCase,
+    public static String executeJavaScriptAndWaitForResult(Instrumentation instrumentation,
             final AwContents awContents,
             final OnEvaluateJavaScriptResultHelper onEvaluateJavaScriptResultHelper,
             final String code) throws Exception {
-        testCase.getInstrumentation().runOnMainSync(new Runnable() {
+        instrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 onEvaluateJavaScriptResultHelper.evaluateJavaScriptForTests(
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index c3ebb792..8c3b2e3e 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -26,6 +26,8 @@
     "//base:base_java_test_support",
     "//components/policy/android:policy_java_test_support",
     "//content/public/android:content_java",
+    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/junit",
     "//ui/android:ui_java",
   ]
 
@@ -40,6 +42,7 @@
     "shell/src/org/chromium/android_webview/test/AwInstrumentationTestRunner.java",
     "shell/src/org/chromium/android_webview/test/AwTestContainerView.java",
     "shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java",
+    "shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java",
     "shell/src/org/chromium/android_webview/test/NullContentsClient.java",
     "shell/src/org/chromium/android_webview/test/SecondBrowserProcess.java",
     "shell/src/org/chromium/android_webview/test/TestContentProvider.java",
@@ -126,7 +129,9 @@
     "//device/geolocation:geolocation_java_test_support",
     "//net/android:net_java",
     "//net/android:net_java_test_support",
+    "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
+    "//third_party/junit",
     "//ui/android:ui_java",
   ]
   java_files = [
@@ -166,7 +171,9 @@
     "../javatests/src/org/chromium/android_webview/test/AwServiceWorkerClientTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwSettingsTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java",
+    "../javatests/src/org/chromium/android_webview/test/AwTestCommon.java",
     "../javatests/src/org/chromium/android_webview/test/AwTestBase.java",
+    "../javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java",
     "../javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwVariationsSeedFetchServiceTest.java",
     "../javatests/src/org/chromium/android_webview/test/AwZoomTest.java",
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java
new file mode 100644
index 0000000..4bedc5d
--- /dev/null
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java
@@ -0,0 +1,97 @@
+// 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.
+
+package org.chromium.android_webview.test;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+
+import org.chromium.android_webview.AwSwitches;
+import org.chromium.base.CollectionUtil;
+import org.chromium.base.CommandLine;
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.BaseTestResult.PreTestHook;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.parameter.SkipCommandLineParameterization;
+import org.chromium.policy.test.annotations.Policies;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A custom runner for //chrome JUnit4 tests.
+ */
+public final class AwJUnit4ClassRunner extends BaseJUnit4ClassRunner {
+    /**
+     * Create an AwJUnit4ClassRunner to run {@code klass} and initialize values
+     *
+     * @param klass Test class to run
+     * @throws InitializationError if the test class is malformed
+     */
+    public AwJUnit4ClassRunner(Class<?> klass) throws InitializationError {
+        super(klass, null, defaultPreTestHooks());
+    }
+
+    private static List<PreTestHook> defaultPreTestHooks() {
+        return CollectionUtil.newArrayList(Policies.getRegistrationHook());
+    }
+
+    @Override
+    protected List<FrameworkMethod> getChildren() {
+        List<FrameworkMethod> result = new ArrayList<>();
+        for (FrameworkMethod method : computeTestMethods()) {
+            if (method.getAnnotation(SkipCommandLineParameterization.class) == null) {
+                result.add(new WebViewMultiProcessFrameworkMethod(method));
+            }
+            result.add(method);
+        }
+        return result;
+    }
+
+    @Override
+    protected void runChild(FrameworkMethod method, RunNotifier notifier) {
+        CommandLineFlags.setUp(InstrumentationRegistry.getTargetContext(), method.getMethod());
+        if (method instanceof WebViewMultiProcessFrameworkMethod) {
+            CommandLine.getInstance().appendSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
+        }
+        super.runChild(method, notifier);
+    }
+
+    /**
+     * Custom FrameworkMethod class indicate this test method will run in multiprocess mode.
+     *
+     * The clas also add "__multiprocess_mode" postfix to the test name.
+     */
+    private static class WebViewMultiProcessFrameworkMethod extends FrameworkMethod {
+        public WebViewMultiProcessFrameworkMethod(FrameworkMethod method) {
+            super(method.getMethod());
+        }
+
+        @Override
+        public String getName() {
+            return super.getName() + "__multiprocess_mode";
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof WebViewMultiProcessFrameworkMethod) {
+                WebViewMultiProcessFrameworkMethod method =
+                        (WebViewMultiProcessFrameworkMethod) obj;
+                return super.equals(obj) && method.getName().equals(getName());
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 17;
+            result = 31 * result + super.hashCode();
+            result = 31 * result + getName().hashCode();
+            return result;
+        }
+    }
+}
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 71eb479..d0bb651 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -525,14 +525,17 @@
        TapAndClickOutsideClosesPeekingAppList) {
   const bool test_mouse_event = TestMouseEventParam();
   app_list_presenter_impl()->Show(GetPrimaryDisplayId());
-  EXPECT_EQ(app_list_presenter_impl()->GetView()->app_list_state(),
-            app_list::AppListView::PEEKING);
+  EXPECT_EQ(app_list::AppListView::PEEKING,
+            app_list_presenter_impl()->GetView()->app_list_state());
   ui::test::EventGenerator& generator = GetEventGenerator();
 
   // Tapping outside the bounds closes the app list.
-  gfx::Point tap_point =
-      app_list_presenter_impl()->GetView()->bounds().origin();
-  tap_point.Offset(0, -10);
+  const gfx::Rect peeking_bounds =
+      app_list_presenter_impl()->GetView()->GetBoundsInScreen();
+  gfx::Point tap_point = peeking_bounds.origin();
+  tap_point.Offset(10, -10);
+  ASSERT_FALSE(peeking_bounds.Contains(tap_point));
+
   if (test_mouse_event) {
     generator.MoveMouseTo(tap_point);
     generator.ClickLeftButton();
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index ff8fe16..b3d5baa 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -41,6 +41,7 @@
     "shelf_types.h",
     "shell_window_ids.cc",
     "shell_window_ids.h",
+    "voice_interaction_state.h",
     "window_pin_type.cc",
     "window_pin_type.h",
     "window_properties.cc",
diff --git a/ash/public/cpp/voice_interaction_state.h b/ash/public/cpp/voice_interaction_state.h
new file mode 100644
index 0000000..fd9d049
--- /dev/null
+++ b/ash/public/cpp/voice_interaction_state.h
@@ -0,0 +1,21 @@
+// 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 ASH_PUBLIC_CPP_VOICE_INTERACTION_STATE_H_
+#define ASH_PUBLIC_CPP_VOICE_INTERACTION_STATE_H_
+
+namespace ash {
+
+enum class VoiceInteractionState {
+  // Voice interaction service is not ready yet, request sent will be waiting.
+  NOT_READY = 0,
+  // Voice interaction session is stopped.
+  STOPPED,
+  // Voice interaction session is currently running.
+  RUNNING
+};
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_VOICE_INTERACTION_STATE_H_
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 569640d..70a3281 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -100,8 +100,6 @@
     "shelf_overflow.icon",
     "shelf_overview.1x.icon",
     "shelf_overview.icon",
-    "shelf_voice_interaction.1x.icon",
-    "shelf_voice_interaction.icon",
     "system_menu_accessibility.1x.icon",
     "system_menu_accessibility.icon",
     "system_menu_accessibility_auto_click.1x.icon",
diff --git a/ash/resources/vector_icons/shelf_voice_interaction.1x.icon b/ash/resources/vector_icons/shelf_voice_interaction.1x.icon
deleted file mode 100644
index 57ed627..0000000
--- a/ash/resources/vector_icons/shelf_voice_interaction.1x.icon
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 24,
-PATH_COLOR_ARGB, 0xFF, 0x34, 0xA8, 0x53,
-MOVE_TO, 22.36f, 8.64f,
-CUBIC_TO, 23.27f, 8.64f, 24, 7.9f, 24, 7,
-CUBIC_TO, 24, 6.1f, 23.27f, 5.36f, 22.36f, 5.36f,
-CUBIC_TO, 21.46f, 5.36f, 20.73f, 6.1f, 20.73f, 7,
-CUBIC_TO, 20.73f, 7.9f, 21.46f, 8.64f, 22.36f, 8.64f,
-LINE_TO, 22.36f, 8.64f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xEB, 0x43, 0x35,
-MOVE_TO, 17.45f, 14.09f,
-CUBIC_TO, 19.26f, 14.09f, 20.73f, 12.63f, 20.73f, 10.82f,
-CUBIC_TO, 20.73f, 9.01f, 19.26f, 7.55f, 17.45f, 7.55f,
-CUBIC_TO, 15.65f, 7.55f, 14.18f, 9.01f, 14.18f, 10.82f,
-CUBIC_TO, 14.18f, 12.63f, 15.65f, 14.09f, 17.45f, 14.09f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xFB, 0xBC, 0x05,
-MOVE_TO, 17.45f, 22.82f,
-CUBIC_TO, 19.56f, 22.82f, 21.27f, 21.11f, 21.27f, 19,
-CUBIC_TO, 21.27f, 16.89f, 19.56f, 15.18f, 17.45f, 15.18f,
-CUBIC_TO, 15.35f, 15.18f, 13.64f, 16.89f, 13.64f, 19,
-CUBIC_TO, 13.64f, 21.11f, 15.35f, 22.82f, 17.45f, 22.82f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0x42, 0x85, 0xF4,
-MOVE_TO, 6.55f, 14.09f,
-CUBIC_TO, 10.16f, 14.09f, 13.09f, 11.16f, 13.09f, 7.55f,
-CUBIC_TO, 13.09f, 3.93f, 10.16f, 1, 6.55f, 1,
-CUBIC_TO, 2.93f, 1, 0, 3.93f, 0, 7.55f,
-CUBIC_TO, 0, 11.16f, 2.93f, 14.09f, 6.55f, 14.09f,
-CLOSE,
-END
diff --git a/ash/resources/vector_icons/shelf_voice_interaction.icon b/ash/resources/vector_icons/shelf_voice_interaction.icon
deleted file mode 100644
index 188c78d..0000000
--- a/ash/resources/vector_icons/shelf_voice_interaction.icon
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 48,
-PATH_COLOR_ARGB, 0xFF, 0x34, 0xA8, 0x53,
-MOVE_TO, 44.73f, 17.27f,
-CUBIC_TO, 46.53f, 17.27f, 48, 15.81f, 48, 14,
-CUBIC_TO, 48, 12.19f, 46.53f, 10.73f, 44.73f, 10.73f,
-CUBIC_TO, 42.92f, 10.73f, 41.45f, 12.19f, 41.45f, 14,
-CUBIC_TO, 41.45f, 15.81f, 42.92f, 17.27f, 44.73f, 17.27f,
-LINE_TO, 44.73f, 17.27f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xEB, 0x43, 0x35,
-MOVE_TO, 34.91f, 28.18f,
-CUBIC_TO, 38.52f, 28.18f, 41.45f, 25.25f, 41.45f, 21.64f,
-CUBIC_TO, 41.45f, 18.02f, 38.52f, 15.09f, 34.91f, 15.09f,
-CUBIC_TO, 31.29f, 15.09f, 28.36f, 18.02f, 28.36f, 21.64f,
-CUBIC_TO, 28.36f, 25.25f, 31.29f, 28.18f, 34.91f, 28.18f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0xFB, 0xBC, 0x05,
-MOVE_TO, 34.91f, 45.64f,
-CUBIC_TO, 39.13f, 45.64f, 42.55f, 42.22f, 42.55f, 38,
-CUBIC_TO, 42.55f, 33.78f, 39.13f, 30.36f, 34.91f, 30.36f,
-CUBIC_TO, 30.69f, 30.36f, 27.27f, 33.78f, 27.27f, 38,
-CUBIC_TO, 27.27f, 42.22f, 30.69f, 45.64f, 34.91f, 45.64f,
-CLOSE,
-NEW_PATH,
-PATH_COLOR_ARGB, 0xFF, 0x42, 0x85, 0xF4,
-MOVE_TO, 13.09f, 28.18f,
-CUBIC_TO, 20.32f, 28.18f, 26.18f, 22.32f, 26.18f, 15.09f,
-CUBIC_TO, 26.18f, 7.86f, 20.32f, 2, 13.09f, 2,
-CUBIC_TO, 5.86f, 2, 0, 7.86f, 0, 15.09f,
-CUBIC_TO, 0, 22.32f, 5.86f, 28.18f, 13.09f, 28.18f,
-CLOSE,
-END
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index a782a63..57cfa11 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -381,7 +381,8 @@
 
     if (chromeos::switches::IsVoiceInteractionEnabled())
       // active: 100% alpha, inactive: 54% alpha
-      fg_flags.setAlpha(voice_interaction_running_
+      fg_flags.setAlpha(voice_interaction_state_ ==
+                                ash::VoiceInteractionState::RUNNING
                             ? kVoiceInteractionRunningAlpha
                             : kVoiceInteractionNotRunningAlpha);
 
@@ -459,19 +460,34 @@
     OnAppListDismissed();
 }
 
-void AppListButton::OnVoiceInteractionStatusChanged(bool running) {
-  voice_interaction_running_ = running;
+void AppListButton::OnVoiceInteractionStatusChanged(
+    ash::VoiceInteractionState state) {
+  voice_interaction_state_ = state;
   SchedulePaint();
 
-  // Voice interaction window shows up, we start hiding the animation if it is
-  // running.
-  if (running && voice_interaction_overlay_->IsBursting()) {
-    voice_interaction_animation_hide_delay_timer_->Start(
-        FROM_HERE,
-        base::TimeDelta::FromMilliseconds(
-            kVoiceInteractionAnimationHideDelayMs),
-        base::Bind(&VoiceInteractionOverlay::HideAnimation,
-                   base::Unretained(voice_interaction_overlay_)));
+  switch (state) {
+    case ash::VoiceInteractionState::STOPPED:
+      break;
+    case ash::VoiceInteractionState::NOT_READY:
+      // If we are showing the bursting or waiting animation, no need to do
+      // anything. Otherwise show the waiting animation now.
+      if (!voice_interaction_overlay_->IsBursting() &&
+          !voice_interaction_overlay_->IsWaiting()) {
+        voice_interaction_overlay_->WaitingAnimation();
+      }
+      break;
+    case ash::VoiceInteractionState::RUNNING:
+      // we start hiding the animation if it is running.
+      if (voice_interaction_overlay_->IsBursting() ||
+          voice_interaction_overlay_->IsWaiting()) {
+        voice_interaction_animation_hide_delay_timer_->Start(
+            FROM_HERE,
+            base::TimeDelta::FromMilliseconds(
+                kVoiceInteractionAnimationHideDelayMs),
+            base::Bind(&VoiceInteractionOverlay::HideAnimation,
+                       base::Unretained(voice_interaction_overlay_)));
+      }
+      break;
   }
 }
 
@@ -479,9 +495,10 @@
   // We only show the voice interaction icon and related animation when the
   // shelf is at the bottom position and voice interaction is not running.
   ShelfAlignment alignment = shelf_->alignment();
-  bool show_icon = (alignment == SHELF_ALIGNMENT_BOTTOM ||
-                    alignment == SHELF_ALIGNMENT_BOTTOM_LOCKED) &&
-                   !voice_interaction_running_;
+  bool show_icon =
+      (alignment == SHELF_ALIGNMENT_BOTTOM ||
+       alignment == SHELF_ALIGNMENT_BOTTOM_LOCKED) &&
+      (voice_interaction_state_ == ash::VoiceInteractionState::STOPPED);
   voice_interaction_overlay_->StartAnimation(show_icon);
 }
 
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
index 2bf9232..a12d66bf 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/app_list_button.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ash/ash_export.h"
+#include "ash/public/cpp/voice_interaction_state.h"
 #include "ash/shell_observer.h"
 #include "base/macros.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -69,7 +70,8 @@
   // ShellObserver:
   void OnAppListVisibilityChanged(bool shown,
                                   aura::Window* root_window) override;
-  void OnVoiceInteractionStatusChanged(bool running) override;
+  void OnVoiceInteractionStatusChanged(
+      ash::VoiceInteractionState state) override;
 
   void StartVoiceInteractionAnimation();
 
@@ -98,7 +100,8 @@
   std::unique_ptr<base::OneShotTimer>
       voice_interaction_animation_hide_delay_timer_;
 
-  bool voice_interaction_running_ = false;
+  ash::VoiceInteractionState voice_interaction_state_ =
+      ash::VoiceInteractionState::STOPPED;
 
   // Flag that gets set each time we receive a mouse or gesture event. It is
   // then used to render the ink drop in the right location.
diff --git a/ash/shelf/voice_interaction_overlay.cc b/ash/shelf/voice_interaction_overlay.cc
index 97b934a..2495e28 100644
--- a/ash/shelf/voice_interaction_overlay.cc
+++ b/ash/shelf/voice_interaction_overlay.cc
@@ -288,6 +288,22 @@
         gfx::Tween::LINEAR_OUT_SLOW_IN, animation_observer);
   }
 
+  void SetToLarge(const gfx::PointF& new_center) {
+    PaintedShapeTransforms transforms;
+    SetPaintedLayersVisible(true);
+    // Hide the foreground layers and only show the background layers.
+    for (int i = 0; i < PAINTED_SHAPE_COUNT; ++i)
+      painted_layers_[i]->SetVisible(false);
+    // Hide the shadow layer
+    shadow_layer_->SetVisible(false);
+
+    center_point_ = new_center;
+    // Set the painted layers to the large rectangle size
+    CalculateRectTransforms(large_size_, kBackgroundCornerRadiusDip,
+                            &transforms);
+    SetTransforms(transforms);
+  }
+
   void ResetShape() {
     // This reverts to the original small round shape.
     shadow_layer_->SetVisible(true);
@@ -581,9 +597,6 @@
       icon_layer_(base::MakeUnique<VoiceInteractionIcon>()),
       background_layer_(base::MakeUnique<VoiceInteractionIconBackground>()),
       host_view_(host_view),
-      is_bursting_(false),
-      show_icon_(false),
-      should_hide_animation_(false),
       circle_layer_delegate_(kRippleColor, kRippleCircleInitRadiusDip) {
   SetPaintToLayer(ui::LAYER_NOT_DRAWN);
   layer()->set_name("VoiceInteractionOverlay:ROOT_LAYER");
@@ -605,7 +618,7 @@
 VoiceInteractionOverlay::~VoiceInteractionOverlay() {}
 
 void VoiceInteractionOverlay::StartAnimation(bool show_icon) {
-  is_bursting_ = false;
+  animation_state_ = AnimationState::STARTING;
   show_icon_ = show_icon;
   SetVisible(true);
 
@@ -715,8 +728,7 @@
 }
 
 void VoiceInteractionOverlay::BurstAnimation() {
-  is_bursting_ = true;
-  should_hide_animation_ = false;
+  animation_state_ = AnimationState::BURSTING;
 
   gfx::Point center = host_view_->GetAppListButtonCenterPoint();
   gfx::Transform transform;
@@ -766,10 +778,6 @@
   }
 
   // Setup background animation.
-  ui::CallbackLayerAnimationObserver* observer =
-      new ui::CallbackLayerAnimationObserver(
-          base::Bind(&VoiceInteractionOverlay::AnimationEndedCallback,
-                     base::Unretained(this)));
   // Transform to new shape.
   // We want to animate from the background's current position into a larger
   // size. The animation moves the background's center point while morphing from
@@ -785,12 +793,70 @@
       gfx::PointF(
           kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip - x_offset,
           -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip - y_offset),
-      observer);
-  observer->SetActive();
+      nullptr);
+}
+
+void VoiceInteractionOverlay::WaitingAnimation() {
+  // If we are already playing burst animation, it will end up at waiting state
+  // anyway.  No need to do anything.
+  if (IsBursting())
+    return;
+
+  animation_state_ = AnimationState::WAITING;
+
+  gfx::Point center = host_view_->GetAppListButtonCenterPoint();
+  gfx::Transform transform;
+
+  ripple_layer_->SetOpacity(0);
+  icon_layer_->SetOpacity(0);
+  background_layer_->SetOpacity(0);
+  SetVisible(true);
+
+  // Setup icon layer.
+  {
+    transform.Translate(kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip -
+                            kIconEndSizeDip / 2,
+                        -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip -
+                            kIconEndSizeDip / 2);
+    SkMScalar scale_factor = kIconEndSizeDip / kIconInitSizeDip;
+    transform.Scale(scale_factor, scale_factor);
+    icon_layer_->SetTransform(transform);
+
+    ui::ScopedLayerAnimationSettings settings(icon_layer_->GetAnimator());
+    settings.SetTransitionDuration(
+        base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
+    settings.SetPreemptionStrategy(
+        ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
+    settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+
+    icon_layer_->SetOpacity(1);
+    icon_layer_->StartAnimation();
+  }
+
+  // Setup background layer.
+  {
+    float x_offset = center.x() - kBackgroundSizeDip / 2;
+    float y_offset = center.y() - kBackgroundSizeDip / 2;
+
+    transform.MakeIdentity();
+    background_layer_->SetTransform(transform);
+    background_layer_->SetToLarge(gfx::PointF(
+        kBackgroundLargeWidthDip / 2 + kBackgroundPaddingDip - x_offset,
+        -kBackgroundLargeHeightDip / 2 - kBackgroundPaddingDip - y_offset));
+
+    ui::ScopedLayerAnimationSettings settings(background_layer_->GetAnimator());
+    settings.SetTransitionDuration(
+        base::TimeDelta::FromMilliseconds(kBackgroundMorphDurationMs));
+    settings.SetPreemptionStrategy(
+        ui::LayerAnimator::PreemptionStrategy::ENQUEUE_NEW_ANIMATION);
+    settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+
+    background_layer_->SetOpacity(1);
+  }
 }
 
 void VoiceInteractionOverlay::EndAnimation() {
-  if (is_bursting_) {
+  if (IsBursting()) {
     // Too late, user action already fired, we have to finish what's started.
     return;
   }
@@ -868,14 +934,7 @@
 }
 
 void VoiceInteractionOverlay::HideAnimation() {
-  is_bursting_ = false;
-
-  if (background_layer_->GetAnimator()->is_animating()) {
-    // Wait for current animation to finish
-    should_hide_animation_ = true;
-    return;
-  }
-  should_hide_animation_ = false;
+  animation_state_ = AnimationState::HIDDEN;
 
   // Setup ripple animations.
   {
@@ -915,12 +974,4 @@
   }
 }
 
-bool VoiceInteractionOverlay::AnimationEndedCallback(
-    const ui::CallbackLayerAnimationObserver& observer) {
-  if (should_hide_animation_)
-    HideAnimation();
-
-  return true;
-}
-
 }  // namespace ash
diff --git a/ash/shelf/voice_interaction_overlay.h b/ash/shelf/voice_interaction_overlay.h
index f126751..4f89c08 100644
--- a/ash/shelf/voice_interaction_overlay.h
+++ b/ash/shelf/voice_interaction_overlay.h
@@ -13,10 +13,6 @@
 #include "ui/views/animation/ink_drop_painted_layer_delegates.h"
 #include "ui/views/view.h"
 
-namespace ui {
-class CallbackLayerAnimationObserver;
-}  // namespace ui
-
 namespace ash {
 
 class AppListButton;
@@ -31,12 +27,25 @@
   void StartAnimation(bool show_icon);
   void EndAnimation();
   void BurstAnimation();
+  void WaitingAnimation();
   void HideAnimation();
-  bool IsBursting() const { return is_bursting_; }
+  bool IsBursting() const {
+    return AnimationState::BURSTING == animation_state_;
+  }
+  bool IsWaiting() const { return AnimationState::WAITING == animation_state_; }
 
  private:
-  bool AnimationEndedCallback(
-      const ui::CallbackLayerAnimationObserver& observer);
+  enum class AnimationState {
+    // Indicates no animation is playing.
+    HIDDEN = 0,
+    // Indicates currently playing the starting animation.
+    STARTING,
+    // Indiates the current animation is in the bursting phase, which means no
+    // turning back.
+    BURSTING,
+    // Indicates currently playing the waiting animation.
+    WAITING
+  };
 
   std::unique_ptr<ui::Layer> ripple_layer_;
   std::unique_ptr<VoiceInteractionIcon> icon_layer_;
@@ -44,16 +53,10 @@
 
   AppListButton* host_view_;
 
-  // Indiates the current animation is in the bursting phase, which means no
-  // turning back.
-  bool is_bursting_;
+  AnimationState animation_state_ = AnimationState::HIDDEN;
 
   // Whether showing the icon animation or not.
-  bool show_icon_;
-
-  // Whether we should hide the burst animation when the animation ends. This is
-  // used to synchronize the animation and the underlying window's appearance.
-  bool should_hide_animation_;
+  bool show_icon_ = false;
 
   views::CircleLayerDelegate circle_layer_delegate_;
 
diff --git a/ash/shell.cc b/ash/shell.cc
index b656fd50..b2f7edf4 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -617,9 +617,9 @@
     observer.OnAppListVisibilityChanged(visible, root_window);
 }
 
-void Shell::NotifyVoiceInteractionStatusChanged(bool running) {
+void Shell::NotifyVoiceInteractionStatusChanged(VoiceInteractionState state) {
   for (auto& observer : shell_observers_)
-    observer.OnVoiceInteractionStatusChanged(running);
+    observer.OnVoiceInteractionStatusChanged(state);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ash/shell.h b/ash/shell.h
index 1a34aebd..5518b03 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -173,6 +173,7 @@
 
 enum class Config;
 enum class LoginStatus;
+enum class VoiceInteractionState;
 
 // Shell is a singleton object that presents the Shell API and implements the
 // RootWindow's delegate interface.
@@ -617,7 +618,7 @@
 
   void NotifyAppListVisibilityChanged(bool visible, aura::Window* root_window);
 
-  void NotifyVoiceInteractionStatusChanged(bool running);
+  void NotifyVoiceInteractionStatusChanged(VoiceInteractionState state);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(ExtendedDesktopTest, TestCursor);
diff --git a/ash/shell_observer.h b/ash/shell_observer.h
index fbf20b4..5e4f2561 100644
--- a/ash/shell_observer.h
+++ b/ash/shell_observer.h
@@ -16,6 +16,8 @@
 
 namespace ash {
 
+enum class VoiceInteractionState;
+
 class ASH_EXPORT ShellObserver {
  public:
   // Called when the AppList is shown or dismissed.
@@ -68,8 +70,8 @@
   // Called when a new KeyboardController is created.
   virtual void OnKeyboardControllerCreated() {}
 
-  // Called when voice interaction session starts / finishes.
-  virtual void OnVoiceInteractionStatusChanged(bool running) {}
+  // Called when voice interaction session state changes.
+  virtual void OnVoiceInteractionStatusChanged(VoiceInteractionState state) {}
 
   // Called at the end of Shell::Init.
   virtual void OnShellInitialized() {}
diff --git a/ash/wallpaper/wallpaper_controller.cc b/ash/wallpaper/wallpaper_controller.cc
index 39c54bc..4aa97499 100644
--- a/ash/wallpaper/wallpaper_controller.cc
+++ b/ash/wallpaper/wallpaper_controller.cc
@@ -9,6 +9,7 @@
 
 #include "ash/ash_switches.h"
 #include "ash/display/window_tree_host_manager.h"
+#include "ash/login/ui/login_constants.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -425,6 +426,9 @@
       return;
   }
 
+  if (Shell::Get()->session_controller()->IsUserSessionBlocked())
+    component->SetWallpaperBlur(login_constants::kBlurSigma);
+
   RootWindowController* controller =
       RootWindowController::ForWindow(root_window);
   controller->SetAnimatingWallpaperWidgetController(
diff --git a/ash/wallpaper/wallpaper_view.cc b/ash/wallpaper/wallpaper_view.cc
index ec74bc7..bb7f32c 100644
--- a/ash/wallpaper/wallpaper_view.cc
+++ b/ash/wallpaper/wallpaper_view.cc
@@ -263,8 +263,6 @@
 
   aura::Window* container = root_window->GetChildById(container_id);
   wallpaper_widget->SetBounds(container->bounds());
-  if (Shell::Get()->session_controller()->IsUserSessionBlocked())
-    wallpaper_widget->GetLayer()->SetLayerBlur(login_constants::kBlurSigma);
 
   return wallpaper_widget;
 }
diff --git a/ash/wallpaper/wallpaper_widget_controller.cc b/ash/wallpaper/wallpaper_widget_controller.cc
index ecedde7..db2883c 100644
--- a/ash/wallpaper/wallpaper_widget_controller.cc
+++ b/ash/wallpaper/wallpaper_widget_controller.cc
@@ -70,6 +70,8 @@
 
 WallpaperWidgetController::~WallpaperWidgetController() {
   if (widget_) {
+    if (widget_->GetLayer()->layer_blur() > 0.0f)
+      widget_parent_->layer()->SetCacheRenderSurface(false);
     views::Widget* widget = widget_;
     RemoveObservers();
     widget->CloseNow();
@@ -88,11 +90,15 @@
 bool WallpaperWidgetController::Reparent(aura::Window* root_window,
                                          int container) {
   if (widget_) {
+    // Ensures the cache render surface of the old parent is unset.
+    widget_parent_->layer()->SetCacheRenderSurface(false);
     widget_parent_->RemoveObserver(this);
     aura::Window* window = widget_->GetNativeWindow();
     root_window->GetChildById(container)->AddChild(window);
     widget_parent_ = widget_->GetNativeWindow()->parent();
     widget_parent_->AddObserver(this);
+    if (widget_->GetLayer()->layer_blur() > 0.0f)
+      widget_parent_->layer()->SetCacheRenderSurface(true);
     return true;
   }
   // Nothing to reparent.
@@ -128,6 +134,12 @@
   }
 }
 
+void WallpaperWidgetController::SetWallpaperBlur(float blur_sigma) {
+  widget_->GetLayer()->SetLayerBlur(blur_sigma);
+  // Force the use of cache render surface to make blur more efficient.
+  widget_parent_->layer()->SetCacheRenderSurface(blur_sigma > 0.0f);
+}
+
 AnimatingWallpaperWidgetController::AnimatingWallpaperWidgetController(
     WallpaperWidgetController* controller)
     : controller_(controller) {}
diff --git a/ash/wallpaper/wallpaper_widget_controller.h b/ash/wallpaper/wallpaper_widget_controller.h
index 1c379ee9..99363043b 100644
--- a/ash/wallpaper/wallpaper_widget_controller.h
+++ b/ash/wallpaper/wallpaper_widget_controller.h
@@ -42,6 +42,9 @@
   // necessary this as |layer_| doesn't have access to the root window).
   void StartAnimating(RootWindowController* root_window_controller);
 
+  // Blur pixels of the wallpaper layer by 3 * the given amount.
+  void SetWallpaperBlur(float blur_sigma);
+
   views::Widget* widget() { return widget_; }
 
  private:
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java
index a239fd4..ba92a56c 100644
--- a/base/android/java/src/org/chromium/base/ContentUriUtils.java
+++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java
@@ -176,10 +176,7 @@
     public static String getDisplayName(Uri uri, Context context, String columnField) {
         if (uri == null) return "";
         ContentResolver contentResolver = context.getContentResolver();
-        Cursor cursor = null;
-        try {
-            cursor = contentResolver.query(uri, null, null, null, null);
-
+        try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) {
             if (cursor != null && cursor.getCount() >= 1) {
                 cursor.moveToFirst();
                 int displayNameIndex = cursor.getColumnIndex(columnField);
@@ -207,8 +204,6 @@
             // Some android models don't handle the provider call correctly.
             // see crbug.com/345393
             return "";
-        } finally {
-            StreamUtil.closeQuietly(cursor);
         }
         return "";
     }
@@ -226,10 +221,7 @@
             return false;
         }
         ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver();
-        Cursor cursor = null;
-        try {
-            cursor = contentResolver.query(uri, null, null, null, null);
-
+        try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) {
             if (cursor != null && cursor.getCount() >= 1) {
                 cursor.moveToFirst();
                 return hasVirtualFlag(cursor);
@@ -238,8 +230,6 @@
             // Some android models don't handle the provider call correctly.
             // see crbug.com/345393
             return false;
-        } finally {
-            StreamUtil.closeQuietly(cursor);
         }
         return false;
     }
diff --git a/base/android/java/src/org/chromium/base/SecureRandomInitializer.java b/base/android/java/src/org/chromium/base/SecureRandomInitializer.java
index 294b1e6..bfd7b49 100644
--- a/base/android/java/src/org/chromium/base/SecureRandomInitializer.java
+++ b/base/android/java/src/org/chromium/base/SecureRandomInitializer.java
@@ -24,16 +24,12 @@
      * Safely initializes the random number generator, by seeding it with data from /dev/urandom.
      */
     public static void initialize(SecureRandom generator) throws IOException {
-        FileInputStream fis = null;
-        try {
+        try (FileInputStream fis = new FileInputStream("/dev/urandom")) {
             byte[] seedBytes = new byte[NUM_RANDOM_BYTES];
-            fis = new FileInputStream("/dev/urandom");
             if (fis.read(seedBytes) != seedBytes.length) {
                 throw new IOException("Failed to get enough random data.");
             }
             generator.setSeed(seedBytes);
-        } finally {
-            StreamUtil.closeQuietly(fis);
         }
     }
 }
diff --git a/base/message_loop/message_loop_io_posix_unittest.cc b/base/message_loop/message_loop_io_posix_unittest.cc
index 75d25ad..4a6d103 100644
--- a/base/message_loop/message_loop_io_posix_unittest.cc
+++ b/base/message_loop/message_loop_io_posix_unittest.cc
@@ -36,26 +36,10 @@
     int err = pipe(pipefds);
     ASSERT_EQ(0, err);
     read_fd_ = ScopedFD(pipefds[0]);
-    ASSERT_TRUE(SetNonBlocking(read_fd_.get()));
     write_fd_ = ScopedFD(pipefds[1]);
   }
 
   void TriggerReadEvent() {
-    // First empty the pipe buffer.
-    while (true) {
-      char c;
-      int result = HANDLE_EINTR(read(read_fd_.get(), &c, 1));
-      if (result == -1) {
-        if (errno != EWOULDBLOCK) {
-          PLOG(ERROR) << "read";
-          FAIL();
-        }
-        break;
-      }
-
-      EXPECT_GT(result, 0);
-    };
-
     // Write from the other end of the pipe to trigger the event.
     char c = '\0';
     EXPECT_EQ(1, HANDLE_EINTR(write(write_fd_.get(), &c, 1)));
@@ -87,20 +71,49 @@
   std::unique_ptr<MessageLoopForIO::FileDescriptorWatcher> watcher_to_delete_;
 };
 
-// Watcher that calls specified closures when read/write events occur.
+// Watcher that calls specified closures when read/write events occur. Verifies
+// that each non-null closure passed to this class is called once and only once.
+// Also resets the read event by reading from the FD.
 class CallClosureHandler : public MessageLoopForIO::Watcher {
  public:
   CallClosureHandler(OnceClosure read_closure, OnceClosure write_closure)
       : read_closure_(std::move(read_closure)),
         write_closure_(std::move(write_closure)) {}
 
-  ~CallClosureHandler() override {}
+  ~CallClosureHandler() override {
+    EXPECT_TRUE(read_closure_.is_null());
+    EXPECT_TRUE(write_closure_.is_null());
+  }
 
-  // base:MessagePumpFuchsia::Watcher interface
+  void SetReadClosure(OnceClosure read_closure) {
+    EXPECT_TRUE(read_closure_.is_null());
+    read_closure_ = std::move(read_closure);
+  }
+
+  void SetWriteClosure(OnceClosure write_closure) {
+    EXPECT_TRUE(write_closure_.is_null());
+    write_closure_ = std::move(write_closure);
+  }
+
+  // base:MessagePumpFuchsia::Watcher interface.
   void OnFileCanReadWithoutBlocking(int fd) override {
+    // Empty the pipe buffer to reset the event. Otherwise libevent
+    // implementation of MessageLoop may call the event handler again even if
+    // |read_closure_| below quits the RunLoop.
+    char c;
+    int result = HANDLE_EINTR(read(fd, &c, 1));
+    if (result == -1) {
+      PLOG(ERROR) << "read";
+      FAIL();
+    }
+    EXPECT_EQ(result, 1);
+
+    ASSERT_FALSE(read_closure_.is_null());
     std::move(read_closure_).Run();
   }
+
   void OnFileCanWriteWithoutBlocking(int fd) override {
+    ASSERT_FALSE(write_closure_.is_null());
     std::move(write_closure_).Run();
   }
 
@@ -238,6 +251,92 @@
   RunLoop().RunUntilIdle();
 }
 
+// Verify that non-persistent watcher is called only once.
+TEST_F(MessageLoopForIoPosixTest, NonPersistentWatcher) {
+  MessageLoopForIO message_loop;
+  MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
+
+  RunLoop run_loop;
+  CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure());
+
+  // Create a non-persistent watcher.
+  ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+      read_fd_.get(), /* persistent= */ false, MessageLoopForIO::WATCH_READ,
+      &watcher, &handler));
+
+  TriggerReadEvent();
+  run_loop.Run();
+
+  // Trigger the event again. handler should not be called again.
+  TriggerReadEvent();
+  RunLoop().RunUntilIdle();
+}
+
+// Verify that persistent watcher is called every time the event is triggered.
+TEST_F(MessageLoopForIoPosixTest, PersistentWatcher) {
+  MessageLoopForIO message_loop;
+  MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
+
+  RunLoop run_loop1;
+  CallClosureHandler handler(run_loop1.QuitClosure(), OnceClosure());
+
+  // Create persistent watcher.
+  ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+      read_fd_.get(), /* persistent= */ true, MessageLoopForIO::WATCH_READ,
+      &watcher, &handler));
+
+  TriggerReadEvent();
+  run_loop1.Run();
+
+  RunLoop run_loop2;
+  handler.SetReadClosure(run_loop2.QuitClosure());
+
+  // Trigger the event again. handler should be called now, which will quit
+  // run_loop2.
+  TriggerReadEvent();
+  run_loop2.Run();
+}
+
+void StopWatchingAndWatchAgain(
+    MessageLoopForIO::FileDescriptorWatcher* controller,
+    int fd,
+    MessageLoopForIO::Watcher* new_handler,
+    RunLoop* run_loop) {
+  controller->StopWatchingFileDescriptor();
+
+  ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+      fd, /* persistent= */ true, MessageLoopForIO::WATCH_READ, controller,
+      new_handler));
+
+  run_loop->Quit();
+}
+
+// Verify that a watcher can be stopped and reused from an event handler.
+TEST_F(MessageLoopForIoPosixTest, StopAndRestartFromHandler) {
+  MessageLoopForIO message_loop;
+  MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
+
+  RunLoop run_loop1;
+  RunLoop run_loop2;
+  CallClosureHandler handler2(run_loop2.QuitClosure(), OnceClosure());
+  CallClosureHandler handler1(BindOnce(&StopWatchingAndWatchAgain, &watcher,
+                                       read_fd_.get(), &handler2, &run_loop1),
+                              OnceClosure());
+
+  // Create persistent watcher.
+  ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+      read_fd_.get(), /* persistent= */ true, MessageLoopForIO::WATCH_READ,
+      &watcher, &handler1));
+
+  TriggerReadEvent();
+  run_loop1.Run();
+
+  // Trigger the event again. handler2 should be called now, which will quit
+  // run_loop2
+  TriggerReadEvent();
+  run_loop2.Run();
+}
+
 }  // namespace
 
 #endif  // !defined(OS_NACL)
diff --git a/base/message_loop/message_pump_fuchsia.cc b/base/message_loop/message_pump_fuchsia.cc
index ab1fab1..4199ffc 100644
--- a/base/message_loop/message_pump_fuchsia.cc
+++ b/base/message_loop/message_pump_fuchsia.cc
@@ -18,16 +18,19 @@
 MessagePumpFuchsia::MxHandleWatchController::~MxHandleWatchController() {
   if (!StopWatchingMxHandle())
     NOTREACHED();
-  if (was_destroyed_) {
-    DCHECK(!*was_destroyed_);
-    *was_destroyed_ = true;
-  }
 }
 
 bool MessagePumpFuchsia::MxHandleWatchController::StopWatchingMxHandle() {
-  // Clear |persistent_| flag, so that if we are stopped mid-callback, we
-  // won't re-instate the wait operation.
-  persistent_ = false;
+  if (was_stopped_) {
+    DCHECK(!*was_stopped_);
+    *was_stopped_ = true;
+
+    // |was_stopped_| points at a value stored on the stack, which will go out
+    // of scope. MessagePumpFuchsia::Run() will reset it only if the value is
+    // false. So we need to reset this pointer here as well, to make sure it's
+    // not used again.
+    was_stopped_ = nullptr;
+  }
 
   // If the pump is gone, or we haven't begun waiting, then there is nothing
   // to cancel.
@@ -50,17 +53,17 @@
   uint32_t events;
   __mxio_wait_end(io_, signals, &events);
 
-  // Each |watcher_| callback we invoke may delete |this| from under us. The
-  // pump has set |was_destroyed_| to point to a safe location on the calling
-  // stack, so we can use that to detect deletion mid-callback avoid doing
-  // further work that would touch |this|.
-  bool* was_destroyed = was_destroyed_;
+  // Each |watcher_| callback we invoke may stop or delete |this|. The pump has
+  // set |was_stopped_| to point to a safe location on the calling stack, so we
+  // can use that to detect being stopped mid-callback and avoid doing further
+  // work that would touch |this|.
+  bool* was_stopped = was_stopped_;
   if (events & MXIO_EVT_WRITABLE)
     watcher_->OnFileCanWriteWithoutBlocking(fd_);
-  if (!*was_destroyed && (events & MXIO_EVT_READABLE))
+  if (!*was_stopped && (events & MXIO_EVT_READABLE))
     watcher_->OnFileCanReadWithoutBlocking(fd_);
 
-  // Don't add additional work here without checking |*was_destroyed_| again.
+  // Don't add additional work here without checking |*was_stopped_| again.
 }
 
 MessagePumpFuchsia::FdWatchController::FdWatchController(
@@ -247,18 +250,18 @@
 
       mx_signals_t signals = controller->WaitEnd(packet.signal.observed);
 
-      // In the case of a persistent Watch, the Watch may be deleted by the
-      // caller within the callback, in which case |controller| is no longer
-      // valid, and we mustn't continue the watch. We check for this with a bool
-      // on the stack, which the Watch receives a pointer to, to set it so we
-      // can detect destruction.
-      bool controller_was_destroyed = false;
-      controller->was_destroyed_ = &controller_was_destroyed;
+      // In the case of a persistent Watch, the Watch may be stopped and
+      // potentially deleted by the caller within the callback, in which case
+      // |controller| should not be accessed again, and we mustn't continue the
+      // watch. We check for this with a bool on the stack, which the Watch
+      // receives a pointer to.
+      bool controller_was_stopped = false;
+      controller->was_stopped_ = &controller_was_stopped;
 
       controller->watcher_->OnMxHandleSignalled(controller->handle_, signals);
 
-      if (!controller_was_destroyed) {
-        controller->was_destroyed_ = nullptr;
+      if (!controller_was_stopped) {
+        controller->was_stopped_ = nullptr;
         if (controller->persistent_)
           controller->WaitBegin();
       }
diff --git a/base/message_loop/message_pump_fuchsia.h b/base/message_loop/message_pump_fuchsia.h
index 1aad9f7..41b4904 100644
--- a/base/message_loop/message_pump_fuchsia.h
+++ b/base/message_loop/message_pump_fuchsia.h
@@ -58,10 +58,10 @@
     // This bool is used by the pump when invoking the MxHandleWatcher callback,
     // and by the FdHandleWatchController when invoking read & write callbacks,
     // to cope with the possibility of the caller deleting the *Watcher within
-    // the callback. The pump sets |was_destroyed_| to a location on the stack,
+    // the callback. The pump sets |was_stopped_| to a location on the stack,
     // and the Watcher writes to it, if set, when deleted, allowing the pump
     // to check the value on the stack to short-cut any post-callback work.
-    bool* was_destroyed_ = nullptr;
+    bool* was_stopped_ = nullptr;
 
    protected:
     friend class MessagePumpFuchsia;
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 7c2a6c2..7cdddcd 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -48,9 +48,6 @@
 constexpr uintptr_t kDummyValue = 0xDEADBEEF;
 #endif
 
-// TODO(asvitkine): Remove this after crbug/736675.
-char g_last_logged_histogram_name[256] = {0};
-
 bool ReadHistogramArguments(PickleIterator* iter,
                             std::string* histogram_name,
                             int* flags,
@@ -478,9 +475,6 @@
   DCHECK_EQ(0, ranges(0));
   DCHECK_EQ(kSampleType_MAX, ranges(bucket_count()));
 
-  strlcpy(g_last_logged_histogram_name, histogram_name().c_str(),
-          sizeof(g_last_logged_histogram_name));
-
   if (value > kSampleType_MAX - 1)
     value = kSampleType_MAX - 1;
   if (value < 0)
@@ -584,9 +578,9 @@
     return is_valid;
 
   // Abort if a problem is found (except "flags", which could legally be zero).
-  const std::string debug_string = base::StringPrintf(
-      "%s/%" PRIu32 "/%d/%s", histogram_name().c_str(), bad_fields,
-      corrupted_count, g_last_logged_histogram_name);
+  const std::string debug_string =
+      base::StringPrintf("%s/%" PRIu32 "/%d", histogram_name().c_str(),
+                         bad_fields, corrupted_count);
 #if !defined(OS_NACL)
   // Temporary for https://crbug.com/736675.
   base::debug::ScopedCrashKey crash_key("bad_histogram", debug_string);
diff --git a/base/process/memory_fuchsia.cc b/base/process/memory_fuchsia.cc
index 7ddaeb5..6f559a43 100644
--- a/base/process/memory_fuchsia.cc
+++ b/base/process/memory_fuchsia.cc
@@ -8,6 +8,10 @@
 
 namespace base {
 
+void EnableTerminationOnOutOfMemory() {
+  // Nothing to be done here.
+}
+
 void EnableTerminationOnHeapCorruption() {
   // Nothing to be done here.
 }
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 0223991..a610b22 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -62,6 +62,12 @@
 #include "base/win/windows_version.h"
 #endif
 
+#if defined(OS_FUCHSIA)
+// TODO(scottmg): For temporary code in OnOutputTimeout().
+#include <magenta/syscalls.h>
+#include <magenta/syscalls/object.h>
+#endif
+
 namespace base {
 
 // See https://groups.google.com/a/chromium.org/d/msg/chromium-dev/nkdTP7sstSc/uT3FaE_sgkAJ .
@@ -1169,6 +1175,26 @@
 #else
     fprintf(stdout, "\t%s\n", pair.second.GetCommandLineString().c_str());
 #endif
+
+#if defined(OS_FUCHSIA)
+    // TODO(scottmg): Temporary code to try to identify why child processes
+    // appear to not be terminated after a timeout correctly.
+    // https://crbug.com/750370 and https://crbug.com/738275.
+
+    mx_info_process_t proc_info = {};
+    mx_status_t status =
+        mx_object_get_info(pair.first, MX_INFO_PROCESS, &proc_info,
+                           sizeof(proc_info), nullptr, nullptr);
+    if (status != MX_OK) {
+      fprintf(stdout, "mx_object_get_info failed for '%s', status=%d\n",
+              pair.second.GetCommandLineString().c_str(), status);
+    } else {
+      fprintf(stdout, "  return_code=%d\n", proc_info.return_code);
+      fprintf(stdout, "  started=%d\n", proc_info.started);
+      fprintf(stdout, "  exited=%d\n", proc_info.exited);
+      fprintf(stdout, "  debugger_attached=%d\n", proc_info.debugger_attached);
+    }
+#endif  // OS_FUCHSIA
   }
 
   fflush(stdout);
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h
index ad1b7d4..0fb7073 100644
--- a/base/threading/platform_thread.h
+++ b/base/threading/platform_thread.h
@@ -20,6 +20,8 @@
 #include <windows.h>
 #elif defined(OS_MACOSX)
 #include <mach/mach_types.h>
+#elif defined(OS_FUCHSIA)
+#include <magenta/types.h>
 #elif defined(OS_POSIX)
 #include <pthread.h>
 #include <unistd.h>
@@ -32,6 +34,8 @@
 typedef DWORD PlatformThreadId;
 #elif defined(OS_MACOSX)
 typedef mach_port_t PlatformThreadId;
+#elif defined(OS_FUCHSIA)
+typedef mx_handle_t PlatformThreadId;
 #elif defined(OS_POSIX)
 typedef pid_t PlatformThreadId;
 #endif
diff --git a/base/threading/platform_thread_fuchsia.cc b/base/threading/platform_thread_fuchsia.cc
index 6a9ed18..aea9551 100644
--- a/base/threading/platform_thread_fuchsia.cc
+++ b/base/threading/platform_thread_fuchsia.cc
@@ -52,7 +52,8 @@
 void PlatformThread::SetName(const std::string& name) {
   // TODO(fuchsia): There's no system-level API to communicate a thread name
   // (for the debugger, etc.), so for now only set to our internal mechanisms.
-  ThreadIdNameManager::GetInstance()->SetName(pthread_self(), name);
+  ThreadIdNameManager::GetInstance()->SetName(PlatformThread::CurrentId(),
+                                              name);
   tracked_objects::ThreadData::InitializeThreadContext(name);
 }
 
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index e049a54..d1e6e675 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -28,6 +28,10 @@
 #include <sys/syscall.h>
 #endif
 
+#if defined(OS_FUCHSIA)
+#include <magenta/process.h>
+#endif
+
 namespace base {
 
 void InitThreading();
@@ -137,7 +141,9 @@
   return syscall(__NR_gettid);
 #elif defined(OS_ANDROID)
   return gettid();
-#elif defined(OS_SOLARIS) || defined(OS_QNX) || defined(OS_FUCHSIA)
+#elif defined(OS_FUCHSIA)
+  return mx_thread_self();
+#elif defined(OS_SOLARIS) || defined(OS_QNX)
   return pthread_self();
 #elif defined(OS_NACL) && defined(__GLIBC__)
   return pthread_self();
diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn
index f00a35c..80c21c1 100644
--- a/build/android/BUILD.gn
+++ b/build/android/BUILD.gn
@@ -11,6 +11,13 @@
       "buildhooks/java/org/chromium/build/BuildHooks.java",
       "buildhooks/java/org/chromium/build/Callback.java",
     ]
+
+    # Make all targets pull in the try-with-resources support files.
+    # If an apk ends up not using any such statements, ProGuard will remove
+    # them.
+    deps = [
+      "//third_party/bazel/desugar:desugar_runtime_java",
+    ]
     no_build_hooks = true
     supports_android = true
   }
diff --git a/build/android/gyp/create_test_runner_script.py b/build/android/gyp/create_test_runner_script.py
index f6a2c3d..a67da882 100755
--- a/build/android/gyp/create_test_runner_script.py
+++ b/build/android/gyp/create_test_runner_script.py
@@ -70,14 +70,14 @@
   group.add_argument('--additional-apk-incremental', action='append',
                      dest='additional_apks_incremental', default=[])
   group.add_argument('--apk-under-test')
-  group.add_argument('--apk-under-test-incremental-install-script')
+  group.add_argument('--apk-under-test-incremental-install-json')
   group.add_argument('--executable-dist-dir')
   group.add_argument('--isolate-file-path')
   group.add_argument('--output-directory')
   group.add_argument('--runtime-deps-path')
   group.add_argument('--test-apk')
   group.add_argument('--test-jar')
-  group.add_argument('--test-apk-incremental-install-script')
+  group.add_argument('--test-apk-incremental-install-json')
   group.add_argument('--coverage-dir')
   group.add_argument('--android-manifest-path')
   group.add_argument('--resource-zips')
@@ -112,11 +112,11 @@
   if args.apk_under_test:
     test_runner_path_args.append(
         ('--apk-under-test', RelativizePathToScript(args.apk_under_test)))
-  if args.apk_under_test_incremental_install_script:
+  if args.apk_under_test_incremental_install_json:
     test_runner_path_args.append(
-        ('--apk-under-test-incremental-install-script',
+        ('--apk-under-test-incremental-install-json',
          RelativizePathToScript(
-             args.apk_under_test_incremental_install_script)))
+             args.apk_under_test_incremental_install_json)))
   if args.executable_dist_dir:
     test_runner_path_args.append(
         ('--executable-dist-dir',
@@ -136,10 +136,10 @@
   if args.test_jar:
     test_runner_path_args.append(
         ('--test-jar', RelativizePathToScript(args.test_jar)))
-  if args.test_apk_incremental_install_script:
+  if args.test_apk_incremental_install_json:
     test_runner_path_args.append(
-        ('--test-apk-incremental-install-script',
-         RelativizePathToScript(args.test_apk_incremental_install_script)))
+        ('--test-apk-incremental-install-json',
+         RelativizePathToScript(args.test_apk_incremental_install_json)))
   if args.coverage_dir:
     test_runner_path_args.append(
         ('--coverage-dir', RelativizePathToScript(args.coverage_dir)))
diff --git a/build/android/gyp/desugar.py b/build/android/gyp/desugar.py
index 6455279..bf51b007 100755
--- a/build/android/gyp/desugar.py
+++ b/build/android/gyp/desugar.py
@@ -6,9 +6,7 @@
 
 import argparse
 import os
-import shutil
 import sys
-import tempfile
 
 from util import build_utils
 
@@ -30,10 +28,9 @@
       bootclasspath_entry,
       '--output',
       output_jar,
-      # Disable try-with-resources due to proguard duplicate zip entry error
-      # TODO(zpeng): Enable try-with-resources with
-      #    desugar_try_with_resources_omit_runtime_classes
-      '--desugar_try_with_resources_if_needed=false',
+      # Don't include try-with-resources files in every .jar. Instead, they
+      # are included via //third_party/bazel/desugar:desugar_runtime_java.
+      '--desugar_try_with_resources_omit_runtime_classes',
   ]
   for path in classpath:
     cmd += ['--classpath_entry', path]
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py
index adeed36..3ca6bd5 100755
--- a/build/android/gyp/javac.py
+++ b/build/android/gyp/javac.py
@@ -371,8 +371,11 @@
   if options.java_version == '1.8' and options.bootclasspath:
     # Android's boot jar doesn't contain all java 8 classes.
     # See: https://github.com/evant/gradle-retrolambda/issues/23.
-    javac_path = os.path.realpath(distutils.spawn.find_executable('javac'))
-    jdk_dir = os.path.dirname(os.path.dirname(javac_path))
+    # Get the path of the jdk folder by searching for the 'jar' executable. We
+    # cannot search for the 'javac' executable because goma provides a custom
+    # version of 'javac'.
+    jar_path = os.path.realpath(distutils.spawn.find_executable('jar'))
+    jdk_dir = os.path.dirname(os.path.dirname(jar_path))
     rt_jar = os.path.join(jdk_dir, 'jre', 'lib', 'rt.jar')
     options.bootclasspath.append(rt_jar)
 
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 7ad4dde..6f2698fd 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -193,6 +193,8 @@
   <issue id="NewApi" severity="ignore"/>
   <issue id="NewApi">
     <ignore regexp="Attribute `paddingStart` referenced here can result in a crash on some specific devices older than API 17"/>
+    <!-- We support try-with-resources via desugar. -->
+    <ignore regexp="Try-with-resources requires API level 19"/>
     <ignore regexp="chrome/android/java/res/values-v17/styles.xml"/>
     <ignore regexp="chromecast/internal"/>
     <ignore regexp="com/android/tv"/>
diff --git a/build/android/pylib/utils/instrumentation_tracing.py b/build/android/pylib/utils/instrumentation_tracing.py
index fe23894..7e00c58 100644
--- a/build/android/pylib/utils/instrumentation_tracing.py
+++ b/build/android/pylib/utils/instrumentation_tracing.py
@@ -4,7 +4,15 @@
 
 """Functions to instrument all Python function calls.
 
-Generates a JSON file readable by Chrome's about:tracing."""
+This generates a JSON file readable by Chrome's about:tracing. To use it,
+either call start_instrumenting and stop_instrumenting at the appropriate times,
+or use the Instrument context manager.
+
+A function is only traced if it is from a Python module that matches at least
+one regular expression object in to_include, and does not match any in
+to_exclude. In between the start and stop events, every function call of a
+function from such a module will be added to the trace.
+"""
 
 import contextlib
 import functools
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 1d03946..573577e 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -71,6 +71,7 @@
 ../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
+../../third_party/catapult/devil/devil/utils/logging_common.py
 ../../third_party/catapult/devil/devil/utils/lsusb.py
 ../../third_party/catapult/devil/devil/utils/parallelizer.py
 ../../third_party/catapult/devil/devil/utils/reraiser_thread.py
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 1ff5744..8135f242 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -80,7 +80,7 @@
   }
 
   webview_public_framework_jar =
-      "//third_party/android_platform/webview/frameworks_7.1.1_r28.jar"
+      "//third_party/android_system_sdk/android_system.jar"
   if (!defined(webview_framework_jar)) {
     webview_framework_jar = webview_public_framework_jar
   }
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py
index 3852dba..50f2ef6 100755
--- a/build/mac_toolchain.py
+++ b/build/mac_toolchain.py
@@ -13,6 +13,7 @@
       user interaction.
 """
 
+from distutils.version import LooseVersion
 import os
 import platform
 import plistlib
@@ -132,10 +133,11 @@
     os.unlink(name)
 
 
-def AcceptLicense(target_os):
-  """Use xcodebuild to accept new toolchain license if necessary.  Don't accept
-  the license if a newer license has already been accepted. This only works if
-  xcodebuild and xcode-select are passwordless in sudoers."""
+def FinalizeUnpack(target_os):
+  """Use xcodebuild to accept new toolchain license and run first launch
+  installers if necessary.  Don't accept the license if a newer license has
+  already been accepted. This only works if xcodebuild and xcode-select are
+  passwordless in sudoers."""
 
   # Check old license
   try:
@@ -164,6 +166,11 @@
     pass
 
   print "Accepting license."
+  target_version_plist_path = \
+      os.path.join(TOOLCHAIN_BUILD_DIR % target_os,
+                   *['Contents','version.plist'])
+  target_version_plist = LoadPlist(target_version_plist_path)
+  short_version_string = target_version_plist['CFBundleShortVersionString']
   old_path = subprocess.Popen(['/usr/bin/xcode-select', '-p'],
                                stdout=subprocess.PIPE).communicate()[0].strip()
   try:
@@ -171,6 +178,11 @@
         TOOLCHAIN_BUILD_DIR % target_os, 'Contents/Developer')
     subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', build_dir])
     subprocess.check_call(['sudo', '/usr/bin/xcodebuild', '-license', 'accept'])
+
+    if target_os == 'ios' and \
+        LooseVersion(short_version_string) >= LooseVersion("9.0"):
+      print "Installing packages."
+      subprocess.check_call(['sudo', '/usr/bin/xcodebuild', '-runFirstLaunch'])
   finally:
     subprocess.check_call(['sudo', '/usr/bin/xcode-select', '-s', old_path])
 
@@ -216,7 +228,7 @@
 
   if ReadStampFile(target_os) == toolchain_version:
     print 'Toolchain (%s) is already up to date.' % toolchain_version
-    AcceptLicense(target_os)
+    FinalizeUnpack(target_os)
     return 0
 
   if not CanAccessToolchainBucket():
@@ -234,7 +246,7 @@
     toolchain_file = toolchain_filename % toolchain_version
     toolchain_full_url = TOOLCHAIN_URL + toolchain_file
     DownloadAndUnpack(toolchain_full_url, TOOLCHAIN_BUILD_DIR % target_os)
-    AcceptLicense(target_os)
+    FinalizeUnpack(target_os)
 
     print 'Toolchain %s unpacked.' % toolchain_version
     WriteStampFile(target_os, toolchain_version)
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index cbeeee4..182b8c21 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -265,10 +265,6 @@
     //
     "race:third_party/harfbuzz-ng/src/*\n"
 
-    // TODO(asvitkine): Remove these after crbug/736675.
-    "race:base::Histogram::AddCount\n"
-    "race:AddCount\n"
-
     // End of suppressions.
     ;  // Please keep this semicolon.
 
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 6555127..88dda3b 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -328,13 +328,18 @@
   if not win_sdk_dir:
     return
 
-  debug_files = ['dbghelp.dll', 'dbgcore.dll']
-  for debug_file in debug_files:
+  # List of debug files that should be copied, the first element of the tuple is
+  # the name of the file and the second indicates if it's optional.
+  debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)]
+  for debug_file, is_optional in debug_files:
     full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file)
     if not os.path.exists(full_path):
-      raise Exception('%s not found in "%s"\r\nYou must install the '
-                      '"Debugging Tools for Windows" feature from the Windows '
-                      '10 SDK.' % (debug_file, full_path))
+      if is_optional:
+        continue
+      else:
+        raise Exception('%s not found in "%s"\r\nYou must install the '
+                        '"Debugging Tools for Windows" feature from the Windows'
+                        ' 10 SDK.' % (debug_file, full_path))
     target_path = os.path.join(target_dir, debug_file)
     _CopyRuntimeImpl(target_path, full_path)
 
diff --git a/cc/base/synced_property.h b/cc/base/synced_property.h
index f0ff329..6b8b91e 100644
--- a/cc/base/synced_property.h
+++ b/cc/base/synced_property.h
@@ -62,31 +62,39 @@
   }
 
   // Push the latest value from the main thread onto pending tree-associated
-  // state.  Returns true if this had any effect.
-  bool PushFromMainThread(typename T::ValueType main_thread_value) {
-    bool changed = pending_base_.get() != main_thread_value;
-
+  // state. Returns true if pushing the value results in different values
+  // between the main layer tree and the pending tree.
+  bool PushMainToPending(typename T::ValueType main_thread_value) {
     reflected_delta_in_pending_tree_ = reflected_delta_in_main_tree_;
     reflected_delta_in_main_tree_ = T::Identity();
     pending_base_ = T(main_thread_value);
 
-    return changed;
+    return Current(false) != main_thread_value;
   }
 
   // Push the value associated with the pending tree to be the active base
-  // value.  As part of this, subtract the delta reflected in the pending tree
+  // value. As part of this, subtract the delta reflected in the pending tree
   // from the active tree delta (which will make the delta zero at steady state,
   // or make it contain only the difference since the last send).
+  // Returns true if pushing the update results in:
+  // 1) Different values on the pending tree and the active tree.
+  // 2) An update to the current value on the active tree.
+  // The reason for considering the second case only when pushing to the active
+  // tree, as opposed to when pushing to the pending tree, is that only the
+  // active tree computes state using this value which is not computed on the
+  // pending tree and not pushed during activation (aka scrollbar geometries).
   bool PushPendingToActive() {
-    bool changed = active_base_.get() != pending_base_.get() ||
-                   active_delta_.get() != PendingDelta().get();
+    typename T::ValueType pending_value_before_push = Current(false);
+    typename T::ValueType active_value_before_push = Current(true);
 
     active_base_ = pending_base_;
     active_delta_ = PendingDelta();
     reflected_delta_in_pending_tree_ = T::Identity();
     clobber_active_value_ = false;
 
-    return changed;
+    typename T::ValueType current_active_value = Current(true);
+    return pending_value_before_push != current_active_value ||
+           active_value_before_push != current_active_value;
   }
 
   // This simulates the consequences of the sent value getting committed and
diff --git a/cc/ipc/BUILD.gn b/cc/ipc/BUILD.gn
index 1f42567..4dfd33aa 100644
--- a/cc/ipc/BUILD.gn
+++ b/cc/ipc/BUILD.gn
@@ -48,7 +48,6 @@
     "local_surface_id.mojom",
     "quads.mojom",
     "selection.mojom",
-    "shared_bitmap_allocation_notifier.mojom",
     "shared_quad_state.mojom",
     "surface_id.mojom",
     "texture_mailbox.mojom",
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index 0ebedc1..cd1e43e 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -248,7 +248,11 @@
   }
   ref.positions_.resize(positions_size);
   ReadData(positions_size * sizeof(SkScalar), ref.positions_.data());
+
   // We don't write the cached shader, so don't attempt to read it either.
+
+  if (!(*shader)->IsValid())
+    valid_ = false;
 }
 
 bool PaintOpReader::AlignMemory(size_t alignment) {
diff --git a/cc/paint/paint_shader.cc b/cc/paint/paint_shader.cc
index c5138268..7d5d710 100644
--- a/cc/paint/paint_shader.cc
+++ b/cc/paint/paint_shader.cc
@@ -245,4 +245,28 @@
   return GetSkShader()->isOpaque();
 }
 
+bool PaintShader::IsValid() const {
+  // If we managed to create a shader already, then we should be valid.
+  if (cached_shader_)
+    return true;
+
+  switch (shader_type_) {
+    case Type::kColor:
+      return true;
+    case Type::kLinearGradient:
+    case Type::kRadialGradient:
+    case Type::kTwoPointConicalGradient:
+    case Type::kSweepGradient:
+      return colors_.size() >= 2 &&
+             (positions_.empty() || positions_.size() == colors_.size());
+    case Type::kImage:
+      return !!image_;
+    case Type::kPaintRecord:
+      return !!record_;
+    case Type::kShaderCount:
+      return false;
+  }
+  return false;
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_shader.h b/cc/paint/paint_shader.h
index 65ab74d..8a133709 100644
--- a/cc/paint/paint_shader.h
+++ b/cc/paint/paint_shader.h
@@ -116,6 +116,12 @@
 
   bool IsOpaque() const;
 
+  // Returns true if the shader looks like it is valid (ie the members required
+  // for this shader type all look reasonable. Returns false otherwise. Note
+  // that this is a best effort function since truly validating whether the
+  // shader is correct is hard.
+  bool IsValid() const;
+
  private:
   friend class PaintFlags;
   friend class PaintOpReader;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 10b3be3..f34b3a4 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -1256,7 +1256,7 @@
   tree_impl->set_top_controls_height(top_controls_height_);
   tree_impl->set_bottom_controls_height(bottom_controls_height_);
   tree_impl->PushBrowserControlsFromMainThread(top_controls_shown_ratio_);
-  tree_impl->elastic_overscroll()->PushFromMainThread(elastic_overscroll_);
+  tree_impl->elastic_overscroll()->PushMainToPending(elastic_overscroll_);
   if (tree_impl->IsActiveTree())
     tree_impl->elastic_overscroll()->PushPendingToActive();
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 77e3137..eddb892 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -4979,7 +4979,7 @@
   DrawFrame();
 
   host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(0.f);
-  host_impl_->active_tree()->top_controls_shown_ratio()->PushFromMainThread(
+  host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(
       30.f / top_controls_height_);
   host_impl_->active_tree()->top_controls_shown_ratio()->PushPendingToActive();
   EXPECT_FLOAT_EQ(30.f,
@@ -5013,7 +5013,7 @@
       layer_size_, layer_size_, layer_size_);
   DrawFrame();
 
-  host_impl_->active_tree()->top_controls_shown_ratio()->PushFromMainThread(
+  host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(
       20.f / top_controls_height_);
   host_impl_->active_tree()->top_controls_shown_ratio()->PushPendingToActive();
   host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(
@@ -5066,8 +5066,7 @@
   host_impl_->sync_tree()->PushBrowserControlsFromMainThread(1.f);
   host_impl_->sync_tree()->set_browser_controls_shrink_blink_size(true);
 
-  host_impl_->active_tree()->top_controls_shown_ratio()->PushFromMainThread(
-      1.f);
+  host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(1.f);
   host_impl_->active_tree()->top_controls_shown_ratio()->PushPendingToActive();
   host_impl_->active_tree()->SetCurrentBrowserControlsShownRatio(0.f);
 
@@ -5432,7 +5431,7 @@
     host_impl_->active_tree()
         ->top_controls_shown_ratio()
         ->PullDeltaForMainThread();
-    host_impl_->active_tree()->top_controls_shown_ratio()->PushFromMainThread(
+    host_impl_->active_tree()->top_controls_shown_ratio()->PushMainToPending(
         delta);
   }
 
@@ -12392,7 +12391,7 @@
 
     // When building property trees from impl side, the builder uses the scroll
     // offset of layer_impl to initialize the scroll offset in scroll tree:
-    //  scroll_tree.synced_scroll_offset.PushFromMainThread(
+    //  scroll_tree.synced_scroll_offset.PushMainToPending(
     //                                   layer->CurrentScrollOffset()).
     // However, layer_impl does not store scroll_offset, so it is using scroll
     // tree's scroll offset to initialize itself. Usually this approach works
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index ad5557d..2bc0189 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -755,26 +755,14 @@
 
   if (page_scale_factor) {
     DCHECK(!IsActiveTree() || !layer_tree_host_impl_->pending_tree());
-    changed_page_scale |= page_scale_factor_->Delta() != 1.f;
-    // TODO(enne): Once CDP goes away, ignore this call below.  The only time
-    // the property trees will differ is if there's been a page scale on the
-    // compositor thread after the begin frame, which is the delta check above.
     changed_page_scale |=
-        page_scale_factor_->PushFromMainThread(*page_scale_factor);
+        page_scale_factor_->PushMainToPending(*page_scale_factor);
   }
 
   if (IsActiveTree()) {
-    // TODO(enne): Pushing from pending to active should never require
-    // DidUpdatePageScale.  The values should already be set by the fully
-    // computed property trees being synced from one tree to another.  Remove
-    // this once CDP goes away.
     changed_page_scale |= page_scale_factor_->PushPendingToActive();
   }
 
-  // TODO(pdr): Simplify the logic here and the equivalent logic in
-  // ScrollTree::PushScrollUpdatesFromMainThread where two types of changes
-  // are detected: whether the current value changed, and whether the pushes
-  // have any effect.
   if (changed_page_scale)
     DidUpdatePageScale();
 
@@ -840,7 +828,7 @@
 
   if (top_controls_shown_ratio) {
     DCHECK(!IsActiveTree() || !layer_tree_host_impl_->pending_tree());
-    top_controls_shown_ratio_->PushFromMainThread(*top_controls_shown_ratio);
+    top_controls_shown_ratio_->PushMainToPending(*top_controls_shown_ratio);
   }
   if (IsActiveTree()) {
     bool changed_active = top_controls_shown_ratio_->PushPendingToActive();
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 88492b90..e3096fb7 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1413,21 +1413,22 @@
     SyncedScrollOffset* synced_scroll_offset =
         GetOrCreateSyncedScrollOffset(id);
 
-    bool changed = synced_scroll_offset->PushFromMainThread(map_entry.second);
+    // If the value on the main thread differs from the value on the pending
+    // tree after state sync, we need to update the scroll state on the newly
+    // committed PropertyTrees.
+    bool needs_scroll_update =
+        synced_scroll_offset->PushMainToPending(map_entry.second);
 
     // If we are committing directly to the active tree, push pending to active
-    // here.
+    // here. If the value differs between the pending and active trees, we need
+    // to update the scroll state on the newly activated PropertyTrees.
+    // In the case of pushing to the active tree, even if the pending and active
+    // tree state match but the value on the active tree changed, we need to
+    // update the scrollbar geometries.
     if (property_trees()->is_active)
-      changed |= synced_scroll_offset->PushPendingToActive();
+      needs_scroll_update |= synced_scroll_offset->PushPendingToActive();
 
-    // If the pushed main thread scroll offset differs from the current scroll
-    // offset (accounting for delta), ensure DidUpdateScrollOffset is called to
-    // update the TransformNode's scroll offset. We also need to ensure
-    // scrollbar geometries are updated if the underlying scroll offset changes.
-    // TODO(pdr): This is confusing because we need to account for multiple
-    // cases where the value is dirty.
-    if (changed || map_entry.second != synced_scroll_offset->Current(
-                                           property_trees()->is_active))
+    if (needs_scroll_update)
       sync_tree->DidUpdateScrollOffset(id);
   }
 }
@@ -1457,17 +1458,17 @@
     map_entry.second->AbortCommit();
 }
 
-bool ScrollTree::SetBaseScrollOffset(ElementId id,
+void ScrollTree::SetBaseScrollOffset(ElementId id,
                                      const gfx::ScrollOffset& scroll_offset) {
   if (property_trees()->is_main_thread) {
     scroll_offset_map_[id] = scroll_offset;
-    return true;
+    return;
   }
 
   // Scroll offset updates on the impl thread should only be for layers which
   // were created on the main thread. But this method is called when we build
   // PropertyTrees on the impl thread from LayerTreeImpl.
-  return GetOrCreateSyncedScrollOffset(id)->PushFromMainThread(scroll_offset);
+  GetOrCreateSyncedScrollOffset(id)->PushMainToPending(scroll_offset);
 }
 
 bool ScrollTree::SetScrollOffset(ElementId id,
@@ -1491,7 +1492,7 @@
     const gfx::ScrollOffset& offset) {
   DCHECK(!property_trees()->is_main_thread);
   SyncedScrollOffset* synced_scroll_offset = GetOrCreateSyncedScrollOffset(id);
-  bool changed = synced_scroll_offset->PushFromMainThread(offset);
+  bool changed = synced_scroll_offset->PushMainToPending(offset);
   if (property_trees()->is_active)
     changed |= synced_scroll_offset->PushPendingToActive();
   return changed;
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index 60f88e4..22ac11c 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -429,7 +429,7 @@
   void PushScrollUpdatesFromPendingTree(PropertyTrees* pending_property_trees,
                                         LayerTreeImpl* active_tree);
 
-  bool SetBaseScrollOffset(ElementId id,
+  void SetBaseScrollOffset(ElementId id,
                            const gfx::ScrollOffset& scroll_offset);
   bool SetScrollOffset(ElementId id, const gfx::ScrollOffset& scroll_offset);
   void SetScrollOffsetClobberActiveValue(ElementId id) {
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index daf5888..6753d9e 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -572,7 +572,7 @@
   // the pending base and active base must be the same at this stage.
   scoped_refptr<SyncedScrollOffset> scroll_layer_offset =
       new SyncedScrollOffset;
-  scroll_layer_offset->PushFromMainThread(scroll_layer->scroll_offset());
+  scroll_layer_offset->PushMainToPending(scroll_layer->scroll_offset());
   scroll_layer_offset->PushPendingToActive();
   EXPECT_TRUE(AreScrollOffsetsEqual(
       scroll_layer_offset.get(),
@@ -582,7 +582,7 @@
 
   scoped_refptr<SyncedScrollOffset> transient_scroll_layer_offset =
       new SyncedScrollOffset;
-  transient_scroll_layer_offset->PushFromMainThread(
+  transient_scroll_layer_offset->PushMainToPending(
       transient_scroll_layer->scroll_offset());
   transient_scroll_layer_offset->PushPendingToActive();
   EXPECT_TRUE(
@@ -628,7 +628,7 @@
   scroll_layer_offset->SetCurrent(gfx::ScrollOffset(20, 30));
   scroll_layer_offset->PullDeltaForMainThread();
   scroll_layer_offset->SetCurrent(gfx::ScrollOffset(40, 50));
-  scroll_layer_offset->PushFromMainThread(gfx::ScrollOffset(100, 100));
+  scroll_layer_offset->PushMainToPending(gfx::ScrollOffset(100, 100));
   scroll_layer_offset->PushPendingToActive();
   EXPECT_TRUE(AreScrollOffsetsEqual(
       scroll_layer_offset.get(),
diff --git a/chrome/VERSION b/chrome/VERSION
index d326ec9..97a8eb6 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=62
 MINOR=0
-BUILD=3183
+BUILD=3184
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
index faba0f76..a31031c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
@@ -33,7 +33,6 @@
 import android.widget.ImageButton;
 import android.widget.ListView;
 import android.widget.PopupWindow;
-import android.widget.PopupWindow.OnDismissListener;
 
 import org.chromium.base.AnimationFrameTimeHistogram;
 import org.chromium.base.ApiCompatibilityUtils;
@@ -177,18 +176,15 @@
 
         boolean anchorAtBottom = isAnchorAtBottom(anchorView, visibleDisplayFrame);
         int footerHeight = 0;
-        mPopup.setOnDismissListener(new OnDismissListener() {
-            @Override
-            public void onDismiss() {
-                if (anchorView instanceof ImageButton) {
-                    ((ImageButton) anchorView).setSelected(false);
-                }
-
-                if (mMenuItemEnterAnimator != null) mMenuItemEnterAnimator.cancel();
-
-                mHandler.appMenuDismissed();
-                mHandler.onMenuVisibilityChanged(false);
+        mPopup.setOnDismissListener(() -> {
+            if (anchorView instanceof ImageButton) {
+                ((ImageButton) anchorView).setSelected(false);
             }
+
+            if (mMenuItemEnterAnimator != null) mMenuItemEnterAnimator.cancel();
+
+            mHandler.appMenuDismissed();
+            mHandler.onMenuVisibilityChanged(false);
         });
 
         // Some OEMs don't actually let us change the background... but they still return the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
index b3e00ae..7d26ac8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
@@ -15,7 +15,6 @@
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
@@ -258,12 +257,7 @@
                 holder.title.setText(titleItem.getTitle());
                 holder.title.setEnabled(titleItem.isEnabled());
                 holder.title.setFocusable(titleItem.isEnabled());
-                holder.title.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        mAppMenu.onItemClick(titleItem);
-                    }
-                });
+                holder.title.setOnClickListener(v -> mAppMenu.onItemClick(titleItem));
 
                 if (subItem.isCheckable()) {
                     // Display a checkbox for the MenuItem.
@@ -330,19 +324,9 @@
             button.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
         }
 
-        button.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mAppMenu.onItemClick(item);
-            }
-        });
+        button.setOnClickListener(v -> mAppMenu.onItemClick(item));
 
-        button.setOnLongClickListener(new View.OnLongClickListener() {
-            @Override
-            public boolean onLongClick(View v) {
-                return mAppMenu.onItemLongClick(item, v);
-            }
-        });
+        button.setOnLongClickListener(v -> mAppMenu.onItemLongClick(item, v));
 
         highlightItemIfNecessary(button, true, item.getItemId());
 
@@ -366,12 +350,7 @@
         // This will ensure that the item is not highlighted when selected.
         convertView.setEnabled(isEnabled);
 
-        convertView.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mAppMenu.onItemClick(item);
-            }
-        });
+        convertView.setOnClickListener(v -> mAppMenu.onItemClick(item));
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
index 1f87c978..9fdbad7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
@@ -68,24 +68,21 @@
         // If user is dragging and the popup ListView is too big to display at once,
         // mDragScrolling animator scrolls mPopup.getListView() automatically depending on
         // the user's touch position.
-        mDragScrolling.setTimeListener(new TimeAnimator.TimeListener() {
-            @Override
-            public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
-                if (mAppMenu.getListView() == null) return;
+        mDragScrolling.setTimeListener((animation, totalTime, deltaTime) -> {
+            if (mAppMenu.getListView() == null) return;
 
-                // We keep both mDragScrollOffset and mDragScrollOffsetRounded because
-                // the actual scrolling is by the rounded value but at the same time we also
-                // want to keep the precise scroll value in float.
-                mDragScrollOffset += (deltaTime * 0.001f) * mDragScrollingVelocity;
-                int diff = Math.round(mDragScrollOffset - mDragScrollOffsetRounded);
-                mDragScrollOffsetRounded += diff;
-                mAppMenu.getListView().smoothScrollBy(diff, 0);
+            // We keep both mDragScrollOffset and mDragScrollOffsetRounded because
+            // the actual scrolling is by the rounded value but at the same time we also
+            // want to keep the precise scroll value in float.
+            mDragScrollOffset += (deltaTime * 0.001f) * mDragScrollingVelocity;
+            int diff = Math.round(mDragScrollOffset - mDragScrollOffsetRounded);
+            mDragScrollOffsetRounded += diff;
+            mAppMenu.getListView().smoothScrollBy(diff, 0);
 
-                // Force touch move event to highlight items correctly for the scrolled position.
-                if (!Float.isNaN(mLastTouchX) && !Float.isNaN(mLastTouchY)) {
-                    menuItemAction(Math.round(mLastTouchX), Math.round(mLastTouchY),
-                            ITEM_ACTION_HIGHLIGHT);
-                }
+            // Force touch move event to highlight items correctly for the scrolled position.
+            if (!Float.isNaN(mLastTouchX) && !Float.isNaN(mLastTouchY)) {
+                menuItemAction(Math.round(mLastTouchX), Math.round(mLastTouchY),
+                        ITEM_ACTION_HIGHLIGHT);
             }
         });
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
index 60ea19a2..92fe2be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
@@ -43,12 +43,7 @@
             mContext = null;
             // Clean up the native counterpart.  This is posted to allow the native counterpart
             // to fully finish the construction of this glue object before we attempt to delete it.
-            new Handler().post(new Runnable() {
-                @Override
-                public void run() {
-                    dismissed();
-                }
-            });
+            new Handler().post(() -> dismissed());
         } else {
             mAutofillPopup = new AutofillPopup(activity, anchorView, this);
             mContext = activity;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
index 73d1e979..774eca29 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
@@ -32,12 +32,7 @@
             mCardUnmaskPrompt = null;
             // Clean up the native counterpart.  This is posted to allow the native counterpart
             // to fully finish the construction of this glue object before we attempt to delete it.
-            new Handler().post(new Runnable() {
-                @Override
-                public void run() {
-                    dismissed();
-                }
-            });
+            new Handler().post(() -> dismissed());
         } else {
             mCardUnmaskPrompt = new CardUnmaskPrompt(activity, this, title, instructions,
                     confirmButtonLabel, ResourceId.mapToDrawableId(iconId),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index 0212c7d..c712cfbd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -21,11 +21,9 @@
 import android.text.Editable;
 import android.text.InputFilter;
 import android.text.TextWatcher;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.EditorInfo;
@@ -213,39 +211,27 @@
                 new InputFilter[] {new InputFilter.LengthFilter(mDelegate.getExpectedCvcLength())});
 
         // Hitting the "submit" button on the software keyboard should submit the form if valid.
-        mCardUnmaskInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
-            @Override
-            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-                if (actionId == EditorInfo.IME_ACTION_DONE) {
-                    Button positiveButton = mDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-                    if (positiveButton.isEnabled()) positiveButton.performClick();
-                    return true;
-                }
-                return false;
+        mCardUnmaskInput.setOnEditorActionListener((v14, actionId, event) -> {
+            if (actionId == EditorInfo.IME_ACTION_DONE) {
+                Button positiveButton = mDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+                if (positiveButton.isEnabled()) positiveButton.performClick();
+                return true;
             }
+            return false;
         });
 
         // Create the listeners to be notified when the user focuses out the input fields.
-        mCardUnmaskInput.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mDidFocusOnCvc = true;
-                validate();
-            }
+        mCardUnmaskInput.setOnFocusChangeListener((v13, hasFocus) -> {
+            mDidFocusOnCvc = true;
+            validate();
         });
-        mMonthInput.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mDidFocusOnMonth = true;
-                validate();
-            }
+        mMonthInput.setOnFocusChangeListener((v12, hasFocus) -> {
+            mDidFocusOnMonth = true;
+            validate();
         });
-        mYearInput.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mDidFocusOnYear = true;
-                validate();
-            }
+        mYearInput.setOnFocusChangeListener((v1, hasFocus) -> {
+            mDidFocusOnYear = true;
+            validate();
         });
 
         // Load the error messages to show to the user.
@@ -288,23 +274,14 @@
         // the dialog.
         Button verifyButton = mDialog.getButton(AlertDialog.BUTTON_POSITIVE);
         verifyButton.setEnabled(false);
-        verifyButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mDelegate.onUserInput(mCardUnmaskInput.getText().toString(),
+        verifyButton.setOnClickListener(
+                view -> mDelegate.onUserInput(mCardUnmaskInput.getText().toString(),
                         mMonthInput.getText().toString(),
                         Integer.toString(getFourDigitYear()),
-                        mStoreLocallyCheckbox != null && mStoreLocallyCheckbox.isChecked());
-            }
-        });
+                        mStoreLocallyCheckbox != null && mStoreLocallyCheckbox.isChecked()));
 
         mCardUnmaskInput.addTextChangedListener(this);
-        mCardUnmaskInput.post(new Runnable() {
-            @Override
-            public void run() {
-                setInitialFocus();
-            }
-        });
+        mCardUnmaskInput.post(() -> setInitialFocus());
     }
 
     public void update(String title, String instructions, boolean shouldRequestExpirationDate) {
@@ -345,12 +322,7 @@
                 setNoRetryError(errorMessage);
             }
         } else {
-            Runnable dismissRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    dismiss();
-                }
-            };
+            Runnable dismissRunnable = () -> dismiss();
             if (mSuccessMessageDurationMilliseconds > 0) {
                 mVerificationProgressBar.setVisibility(View.GONE);
                 mDialog.findViewById(R.id.verification_success).setVisibility(View.VISIBLE);
@@ -453,17 +425,9 @@
         mStoreLocallyTooltipPopup.setOutsideTouchable(true);
         mStoreLocallyTooltipPopup.setBackgroundDrawable(ApiCompatibilityUtils.getDrawable(
                 resources, R.drawable.store_locally_tooltip_background));
-        mStoreLocallyTooltipPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
-            @Override
-            public void onDismiss() {
-                Handler h = new Handler();
-                h.postDelayed(new Runnable() {
-                    @Override
-                    public void run() {
-                        mStoreLocallyTooltipPopup = null;
-                    }
-                }, 200);
-            }
+        mStoreLocallyTooltipPopup.setOnDismissListener(() -> {
+            Handler h = new Handler();
+            h.postDelayed(() -> mStoreLocallyTooltipPopup = null, 200);
         });
         mStoreLocallyTooltipPopup.showAsDropDown(mStoreLocallyCheckbox,
                 ViewCompat.getPaddingStart(mStoreLocallyCheckbox), 0);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java
index 2c3654cd2..85b976c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/PasswordGenerationPopupBridge.java
@@ -54,12 +54,7 @@
             mPopup = null;
             // Prevent destroying the native counterpart when it's about to derefence its own
             // members in UpdateBoundsAndRedrawPopup().
-            new Handler().post(new Runnable() {
-                @Override
-                public void run() {
-                    onDismiss();
-                }
-            });
+            new Handler().post(() -> onDismiss());
         } else {
             mPopup = new DropdownPopupWindow(mContext, anchorView);
             mPopup.setOnItemClickListener(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 261ef68..128c5df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -28,6 +28,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.speech.RecognizerIntent;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
 import android.text.InputType;
 import android.text.TextUtils;
@@ -164,6 +165,7 @@
     private NavigationButtonType mNavigationButtonType;
 
     // The type of the security icon currently active.
+    @DrawableRes
     private int mSecurityIconResource;
 
     private final OmniboxResultsAdapter mSuggestionListAdapter;
@@ -1306,6 +1308,7 @@
      * @param isOfflinePage Whether the page for which the icon is shown is an offline page.
      * @return The resource ID of the icon that should be displayed, 0 if no icon should show.
      */
+    @DrawableRes
     public static int getSecurityIconResource(
             int securityLevel, boolean isSmallDevice, boolean isOfflinePage) {
         if (isOfflinePage) {
@@ -1370,6 +1373,7 @@
     @Override
     public void updateSecurityIcon(int securityLevel) {
         boolean isSmallDevice = !DeviceFormFactor.isTablet();
+        @DrawableRes
         int id = getSecurityIconResource(
                 securityLevel, isSmallDevice, mToolbarDataProvider.isOfflinePage());
         if (id == 0) {
@@ -1422,6 +1426,15 @@
     }
 
     /**
+     * @return The ID of the drawable currently shown in the security icon.
+     */
+    @VisibleForTesting
+    @DrawableRes
+    int getSecurityIconResourceId() {
+        return mSecurityIconResource;
+    }
+
+    /**
      * Sets the type of the current navigation type and updates the UI to match it.
      * @param buttonType The type of navigation button to be shown.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
index cec5022..57dc10e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
@@ -189,28 +189,22 @@
 
         // If the user clicks [Cancel], send |toEdit| address back to the caller, which was the
         // original state (could be null, a complete address, a partial address).
-        mEditor.setCancelCallback(new Runnable() {
-            @Override
-            public void run() {
-                // This makes sure that onSubKeysReceived returns early if it's
-                // ever called when Cancel has already occurred.
-                mAdminAreasLoaded = true;
-                PersonalDataManager.getInstance().cancelPendingGetSubKeys();
-                callback.onResult(toEdit);
-            }
+        mEditor.setCancelCallback(() -> {
+            // This makes sure that onSubKeysReceived returns early if it's
+            // ever called when Cancel has already occurred.
+            mAdminAreasLoaded = true;
+            PersonalDataManager.getInstance().cancelPendingGetSubKeys();
+            callback.onResult(toEdit);
         });
 
         // If the user clicks [Done], save changes on disk, mark the address "complete," and send it
         // back to the caller.
-        mEditor.setDoneCallback(new Runnable() {
-            @Override
-            public void run() {
-                mAdminAreasLoaded = true;
-                PersonalDataManager.getInstance().cancelPendingGetSubKeys();
-                commitChanges(mProfile);
-                address.completeAddress(mProfile);
-                callback.onResult(address);
-            }
+        mEditor.setDoneCallback(() -> {
+            mAdminAreasLoaded = true;
+            PersonalDataManager.getInstance().cancelPendingGetSubKeys();
+            commitChanges(mProfile);
+            address.completeAddress(mProfile);
+            callback.onResult(address);
         });
 
         loadAdminAreasForCountry(mProfile.getCountryCode());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
index 6f8bfbe3..0777a1a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentApp.java
@@ -7,8 +7,6 @@
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.ServiceConnection;
@@ -183,11 +181,8 @@
             return;
         }
 
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                if (!mIsReadyToPayQueried) respondToGetInstrumentsQuery(null);
-            }
+        mHandler.postDelayed(() -> {
+            if (!mIsReadyToPayQueried) respondToGetInstrumentsQuery(null);
         }, SERVICE_CONNECTION_TIMEOUT_MS);
     }
 
@@ -198,19 +193,16 @@
         }
 
         if (mInstrumentsCallback == null) return;
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                ThreadUtils.assertOnUiThread();
-                if (mInstrumentsCallback == null) return;
-                List<PaymentInstrument> instruments = null;
-                if (instrument != null) {
-                    instruments = new ArrayList<>();
-                    instruments.add(instrument);
-                }
-                mInstrumentsCallback.onInstrumentsReady(AndroidPaymentApp.this, instruments);
-                mInstrumentsCallback = null;
+        mHandler.post(() -> {
+            ThreadUtils.assertOnUiThread();
+            if (mInstrumentsCallback == null) return;
+            List<PaymentInstrument> instruments = null;
+            if (instrument != null) {
+                instruments = new ArrayList<>();
+                instruments.add(instrument);
             }
+            mInstrumentsCallback.onInstrumentsReady(AndroidPaymentApp.this, instruments);
+            mInstrumentsCallback = null;
         });
     }
 
@@ -235,12 +227,7 @@
             respondToGetInstrumentsQuery(null);
             return;
         }
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                respondToGetInstrumentsQuery(null);
-            }
-        }, READY_TO_PAY_TIMEOUT_MS);
+        mHandler.postDelayed(() -> respondToGetInstrumentsQuery(null), READY_TO_PAY_TIMEOUT_MS);
     }
 
     @Override
@@ -298,27 +285,13 @@
                 .setTitle(R.string.external_app_leave_incognito_warning_title)
                 .setMessage(R.string.external_payment_app_leave_incognito_warning)
                 .setPositiveButton(R.string.ok,
-                        new OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                launchPaymentApp(id, merchantName, schemelessOrigin,
-                                        schemelessIframeOrigin, certificateChain, methodDataMap,
-                                        total, displayItems, modifiers);
-                            }
-                        })
+                        (OnClickListener) (dialog, which) -> launchPaymentApp(id, merchantName,
+                                schemelessOrigin,
+                                schemelessIframeOrigin, certificateChain, methodDataMap,
+                                total, displayItems, modifiers))
                 .setNegativeButton(R.string.cancel,
-                        new OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                notifyErrorInvokingPaymentApp();
-                            }
-                        })
-                .setOnCancelListener(new OnCancelListener() {
-                    @Override
-                    public void onCancel(DialogInterface dialog) {
-                        notifyErrorInvokingPaymentApp();
-                    }
-                })
+                        (OnClickListener) (dialog, which) -> notifyErrorInvokingPaymentApp())
+                .setOnCancelListener(dialog -> notifyErrorInvokingPaymentApp())
                 .show();
     }
 
@@ -443,12 +416,7 @@
     }
 
     private void notifyErrorInvokingPaymentApp() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mInstrumentDetailsCallback.onInstrumentDetailsError();
-            }
-        });
+        mHandler.post(() -> mInstrumentDetailsCallback.onInstrumentDetailsError());
     }
 
     private static String serializeDetails(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java
index 0c1514a..a3c6dce3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AutofillPaymentApp.java
@@ -96,12 +96,7 @@
             }
         }
 
-        new Handler().post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onInstrumentsReady(AutofillPaymentApp.this, instruments);
-            }
-        });
+        new Handler().post(() -> callback.onInstrumentsReady(AutofillPaymentApp.this, instruments));
     }
 
     /** @return A set of card networks (e.g., "visa", "amex") accepted by "basic-card" method. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index c48b16b1..91c3f30 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -33,7 +33,6 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -202,17 +201,14 @@
         }
 
         // Sort profiles for billing address according to completeness.
-        Collections.sort(mProfilesForBillingAddress, new Comparator<AutofillProfile>() {
-            @Override
-            public int compare(AutofillProfile a, AutofillProfile b) {
-                boolean isAComplete = AutofillAddress.checkAddressCompletionStatus(
-                                              a, AutofillAddress.NORMAL_COMPLETENESS_CHECK)
-                        == AutofillAddress.COMPLETE;
-                boolean isBComplete = AutofillAddress.checkAddressCompletionStatus(
-                                              b, AutofillAddress.NORMAL_COMPLETENESS_CHECK)
-                        == AutofillAddress.COMPLETE;
-                return ApiCompatibilityUtils.compareBoolean(isBComplete, isAComplete);
-            }
+        Collections.sort(mProfilesForBillingAddress, (a, b) -> {
+            boolean isAComplete = AutofillAddress.checkAddressCompletionStatus(
+                    a, AutofillAddress.NORMAL_COMPLETENESS_CHECK)
+                    == AutofillAddress.COMPLETE;
+            boolean isBComplete = AutofillAddress.checkAddressCompletionStatus(
+                    b, AutofillAddress.NORMAL_COMPLETENESS_CHECK)
+                    == AutofillAddress.COMPLETE;
+            return ApiCompatibilityUtils.compareBoolean(isBComplete, isAComplete);
         });
 
         mCardIssuerNetworks = new HashMap<>();
@@ -254,16 +250,13 @@
             }
         };
 
-        mCardIconGenerator = new EditorValueIconGenerator() {
-            @Override
-            public int getIconResourceId(@Nullable CharSequence value) {
-                if (value == null) return 0;
-                CardIssuerNetwork cardTypeInfo = mCardIssuerNetworks.get(
-                        PersonalDataManager.getInstance().getBasicCardIssuerNetwork(
-                                value.toString(), false));
-                if (cardTypeInfo == null) return 0;
-                return cardTypeInfo.icon;
-            }
+        mCardIconGenerator = value -> {
+            if (value == null) return 0;
+            CardIssuerNetwork cardTypeInfo = mCardIssuerNetworks.get(
+                    PersonalDataManager.getInstance().getBasicCardIssuerNetwork(
+                            value.toString(), false));
+            if (cardTypeInfo == null) return 0;
+            return cardTypeInfo.icon;
         };
 
         mCalendar = new AsyncTask<Void, Void, Calendar>() {
@@ -378,12 +371,7 @@
             try {
                 calendar = mCalendar.get();
             } catch (InterruptedException | ExecutionException e) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onResult(null);
-                    }
-                });
+                mHandler.post(() -> callback.onResult(null));
                 return;
             }
             assert calendar != null;
@@ -406,33 +394,25 @@
 
         // If the user clicks [Cancel], send |toEdit| card back to the caller (will return original
         // state, which could be null, a full card, or a partial card).
-        editor.setCancelCallback(new Runnable() {
-            @Override
-            public void run() {
-                callback.onResult(toEdit);
-            }
-        });
+        editor.setCancelCallback(() -> callback.onResult(toEdit));
 
         // If the user clicks [Done], save changes on disk, mark the card "complete," and send it
         // back to the caller.
-        editor.setDoneCallback(new Runnable() {
-            @Override
-            public void run() {
-                commitChanges(card, isNewCard);
+        editor.setDoneCallback(() -> {
+            commitChanges(card, isNewCard);
 
-                String methodName = card.getBasicCardIssuerNetwork();
-                if (mAcceptedBasicCardIssuerNetworks.contains(methodName)) {
-                    methodName = AutofillPaymentApp.BASIC_CARD_METHOD_NAME;
-                }
-                assert methodName != null;
-
-                AutofillProfile billingAddress =
-                        findTargetProfile(mProfilesForBillingAddress, card.getBillingAddressId());
-                assert billingAddress != null;
-
-                instrument.completeInstrument(card, methodName, billingAddress);
-                callback.onResult(instrument);
+            String methodName = card.getBasicCardIssuerNetwork();
+            if (mAcceptedBasicCardIssuerNetworks.contains(methodName)) {
+                methodName = AutofillPaymentApp.BASIC_CARD_METHOD_NAME;
             }
+            assert methodName != null;
+
+            AutofillProfile billingAddress =
+                    findTargetProfile(mProfilesForBillingAddress, card.getBillingAddressId());
+            assert billingAddress != null;
+
+            instrument.completeInstrument(card, methodName, billingAddress);
+            callback.onResult(instrument);
         });
 
         mEditorDialog.show(editor);
@@ -502,13 +482,10 @@
                     null /* value */);
             if (mCanScan) {
                 mNumberField.addActionIcon(R.drawable.ic_photo_camera,
-                        R.string.autofill_scan_credit_card, new Runnable() {
-                            @Override
-                            public void run() {
-                                if (mIsScanning) return;
-                                mIsScanning = true;
-                                mCardScanner.scan();
-                            }
+                        R.string.autofill_scan_credit_card, (Runnable) () -> {
+                            if (mIsScanning) return;
+                            mIsScanning = true;
+                            mCardScanner.scan();
                         });
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
index 0ec3a51..8e98658 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
@@ -188,41 +188,33 @@
 
         // If the user clicks [Cancel], send |toEdit| contact back to the caller, which was the
         // original state (could be null, a complete contact, a partial contact).
-        editor.setCancelCallback(new Runnable() {
-            @Override
-            public void run() {
-                callback.onResult(toEdit);
+        editor.setCancelCallback(() -> callback.onResult(toEdit));
+
+        editor.setDoneCallback(() -> {
+            String name = null;
+            String phone = null;
+            String email = null;
+            AutofillProfile profile = contact.getProfile();
+
+            if (nameField != null) {
+                name = nameField.getValue().toString();
+                profile.setFullName(name);
             }
-        });
 
-        editor.setDoneCallback(new Runnable() {
-            @Override
-            public void run() {
-                String name = null;
-                String phone = null;
-                String email = null;
-                AutofillProfile profile = contact.getProfile();
-
-                if (nameField != null) {
-                    name = nameField.getValue().toString();
-                    profile.setFullName(name);
-                }
-
-                if (phoneField != null) {
-                    phone = phoneField.getValue().toString();
-                    profile.setPhoneNumber(phone);
-                }
-
-                if (emailField != null) {
-                    email = emailField.getValue().toString();
-                    profile.setEmailAddress(email);
-                }
-
-                profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(profile));
-                profile.setIsLocal(true);
-                contact.completeContact(profile.getGUID(), name, phone, email);
-                callback.onResult(contact);
+            if (phoneField != null) {
+                phone = phoneField.getValue().toString();
+                profile.setPhoneNumber(phone);
             }
+
+            if (emailField != null) {
+                email = emailField.getValue().toString();
+                profile.setEmailAddress(email);
+            }
+
+            profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(profile));
+            profile.setIsLocal(true);
+            contact.completeContact(profile.getGUID(), name, phone, email);
+            callback.onResult(contact);
         });
 
         mEditorDialog.show(editor);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
index 249b9378..39f47c15 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
@@ -115,6 +115,20 @@
                 mJourneyLoggerAndroid, requestShipping, requestEmail, requestPhone, requestName);
     }
 
+    /*
+     * Records what types of payment methods were requested by the merchant in the Payment Request.
+     *
+     * @param requestedBasicCard    Whether the merchant requested basic-card.
+     * @param requestedMethodGoogle Whether the merchant requested a Google payment method.
+     * @param requestedMethodOther  Whether the merchant requested a non basic-card, non-Google
+     *                              payment method.
+     */
+    public void setRequestedPaymentMethodTypes(boolean requestedBasicCard,
+            boolean requestedMethodGoogle, boolean requestedMethodOther) {
+        nativeSetRequestedPaymentMethodTypes(mJourneyLoggerAndroid, requestedBasicCard,
+                requestedMethodGoogle, requestedMethodOther);
+    }
+
     /**
      * Records the payment method that was selected by the user.
      *
@@ -189,7 +203,10 @@
     private native void nativeSetRequestedInformation(long nativeJourneyLoggerAndroid,
             boolean requestShipping, boolean requestEmail, boolean requestPhone,
             boolean requestName);
+    private native void nativeSetRequestedPaymentMethodTypes(long nativeJourneyLoggerAndroid,
+            boolean requestedBasicCard, boolean requestedMethodGoogle,
+            boolean requestedMethodOther);
     private native void nativeSetCompleted(long nativeJourneyLoggerAndroid);
     private native void nativeSetAborted(long nativeJourneyLoggerAndroid, int reason);
     private native void nativeSetNotShown(long nativeJourneyLoggerAndroid, int reason);
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index c42f510..e218b43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -7,7 +7,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Bitmap;
 import android.os.Handler;
 import android.support.v4.util.ArrayMap;
 import android.text.TextUtils;
@@ -30,7 +29,8 @@
 import org.chromium.chrome.browser.payments.ui.LineItem;
 import org.chromium.chrome.browser.payments.ui.PaymentInformation;
 import org.chromium.chrome.browser.payments.ui.PaymentOption;
-import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection.FocusChangedObserver;
+import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection
+        .FocusChangedObserver;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestUI;
 import org.chromium.chrome.browser.payments.ui.SectionInformation;
 import org.chromium.chrome.browser.payments.ui.ShoppingCart;
@@ -190,12 +190,7 @@
     private static final String ANDROID_PAY_METHOD_NAME = "https://android.com/pay";
     private static final String PAY_WITH_GOOGLE_METHOD_NAME = "https://google.com/pay";
     private static final Comparator<Completable> COMPLETENESS_COMPARATOR =
-            new Comparator<Completable>() {
-                @Override
-                public int compare(Completable a, Completable b) {
-                    return (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0);
-                }
-            };
+            (a, b) -> (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0);
 
     /**
      * Sorts the payment instruments by several rules:
@@ -207,34 +202,31 @@
      *         instruments.
      */
     private static final Comparator<PaymentInstrument> PAYMENT_INSTRUMENT_COMPARATOR =
-            new Comparator<PaymentInstrument>() {
-                @Override
-                public int compare(PaymentInstrument a, PaymentInstrument b) {
-                    // Payment apps (not autofill) first.
-                    int autofill =
-                            (a.isAutofillInstrument() ? 1 : 0) - (b.isAutofillInstrument() ? 1 : 0);
-                    if (autofill != 0) return autofill;
+            (a, b) -> {
+                // Payment apps (not autofill) first.
+                int autofill =
+                        (a.isAutofillInstrument() ? 1 : 0) - (b.isAutofillInstrument() ? 1 : 0);
+                if (autofill != 0) return autofill;
 
-                    // Complete cards before cards with missing information.
-                    int completeness = (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0);
-                    if (completeness != 0) return completeness;
+                // Complete cards before cards with missing information.
+                int completeness = (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0);
+                if (completeness != 0) return completeness;
 
-                    // Cards with matching type before unknown type cards.
-                    int typeMatch = (b.isExactlyMatchingMerchantRequest() ? 1 : 0)
-                            - (a.isExactlyMatchingMerchantRequest() ? 1 : 0);
-                    if (typeMatch != 0) return typeMatch;
+                // Cards with matching type before unknown type cards.
+                int typeMatch = (b.isExactlyMatchingMerchantRequest() ? 1 : 0)
+                        - (a.isExactlyMatchingMerchantRequest() ? 1 : 0);
+                if (typeMatch != 0) return typeMatch;
 
-                    // Preselectable instruments before non-preselectable instruments.
-                    // Note that this only affects service worker payment apps' instruments for now
-                    // since autofill payment instruments have already been sorted by preselect
-                    // after sorting by completeness and typeMatch. And the other payment apps'
-                    // instruments can always be preselected.
-                    int canPreselect = (b.canPreselect() ? 1 : 0) - (a.canPreselect() ? 1 : 0);
-                    if (canPreselect != 0) return canPreselect;
+                // Preselectable instruments before non-preselectable instruments.
+                // Note that this only affects service worker payment apps' instruments for now
+                // since autofill payment instruments have already been sorted by preselect
+                // after sorting by completeness and typeMatch. And the other payment apps'
+                // instruments can always be preselected.
+                int canPreselect = (b.canPreselect() ? 1 : 0) - (a.canPreselect() ? 1 : 0);
+                if (canPreselect != 0) return canPreselect;
 
-                    // More frequently and recently used instruments first.
-                    return compareInstrumentsByFrecency(b, a);
-                }
+                // More frequently and recently used instruments first.
+                return compareInstrumentsByFrecency(b, a);
             };
 
     /** Every origin can call canMakePayment() every 30 minutes. */
@@ -497,6 +489,22 @@
         PaymentAppFactory.getInstance().create(mWebContents,
                 Collections.unmodifiableSet(mMethodData.keySet()), this /* callback */);
 
+        // Log the various types of payment methods that were requested by the merchant.
+        boolean requestedMethodGoogle = false;
+        boolean requestedMethodOther = false;
+        for (String methodName : mMethodData.keySet()) {
+            if (methodName.equals(ANDROID_PAY_METHOD_NAME)
+                    || methodName.equals(PAY_WITH_GOOGLE_METHOD_NAME)) {
+                requestedMethodGoogle = true;
+            } else if (methodName.startsWith(UrlConstants.HTTPS_URL_PREFIX)) {
+                // Any method that starts with https and is not Android pay or Google pay is in the
+                // "other" category.
+                requestedMethodOther = true;
+            }
+        }
+        mJourneyLogger.setRequestedPaymentMethodTypes(mMerchantSupportsAutofillPaymentInstruments,
+                requestedMethodGoogle, requestedMethodOther);
+
         // If there is a single payment method and the merchant has not requested any other
         // information, we can safely go directly to the payment app instead of showing
         // Payment Request UI.
@@ -544,13 +552,10 @@
         faviconHelper.getLocalFaviconImageForURL(Profile.getLastUsedProfile(),
                 mWebContents.getLastCommittedUrl(),
                 activity.getResources().getDimensionPixelSize(R.dimen.payments_favicon_size),
-                new FaviconHelper.FaviconImageCallback() {
-                    @Override
-                    public void onFaviconAvailable(Bitmap bitmap, String iconUrl) {
-                        if (mClient != null && bitmap == null) mClient.warnNoFavicon();
-                        if (mUI != null && bitmap != null) mUI.setTitleBitmap(bitmap);
-                        faviconHelper.destroy();
-                    }
+                (bitmap, iconUrl) -> {
+                    if (mClient != null && bitmap == null) mClient.warnNoFavicon();
+                    if (mUI != null && bitmap != null) mUI.setTitleBitmap(bitmap);
+                    faviconHelper.destroy();
                 });
 
         // Add the callback to change the label of shipping addresses depending on the focus.
@@ -1054,11 +1059,8 @@
 
         if (mPaymentMethodsSection == null) return;
 
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mUI != null) providePaymentInformation();
-            }
+        mHandler.post(() -> {
+            if (mUI != null) providePaymentInformation();
         });
     }
 
@@ -1077,30 +1079,22 @@
 
     @Override
     public void getShoppingCart(final Callback<ShoppingCart> callback) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                callback.onResult(mUiShoppingCart);
-            }
-        });
+        mHandler.post(() -> callback.onResult(mUiShoppingCart));
     }
 
     @Override
     public void getSectionInformation(@PaymentRequestUI.DataType final int optionType,
             final Callback<SectionInformation> callback) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) {
-                    callback.onResult(mShippingAddressesSection);
-                } else if (optionType == PaymentRequestUI.TYPE_SHIPPING_OPTIONS) {
-                    callback.onResult(mUiShippingOptions);
-                } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) {
-                    callback.onResult(mContactSection);
-                } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) {
-                    assert mPaymentMethodsSection != null;
-                    callback.onResult(mPaymentMethodsSection);
-                }
+        mHandler.post(() -> {
+            if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) {
+                callback.onResult(mShippingAddressesSection);
+            } else if (optionType == PaymentRequestUI.TYPE_SHIPPING_OPTIONS) {
+                callback.onResult(mUiShippingOptions);
+            } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) {
+                callback.onResult(mContactSection);
+            } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) {
+                assert mPaymentMethodsSection != null;
+                callback.onResult(mPaymentMethodsSection);
             }
         });
     }
@@ -1462,12 +1456,8 @@
             // period expires.
             query = new CanMakePaymentQuery(Collections.unmodifiableMap(mMethodData));
             sCanMakePaymentQueries.put(canMakePaymentId, query);
-            mHandler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    sCanMakePaymentQueries.remove(canMakePaymentId);
-                }
-            }, CAN_MAKE_PAYMENT_QUERY_PERIOD_MS);
+            mHandler.postDelayed(() -> sCanMakePaymentQueries.remove(canMakePaymentId),
+                    CAN_MAKE_PAYMENT_QUERY_PERIOD_MS);
         } else if (shouldEnforceCanMakePaymentQueryQuota()
                 && !query.matchesPaymentMethods(Collections.unmodifiableMap(mMethodData))) {
             // If there has been a canMakePayment() query in the last 30 minutes, but the previous
@@ -1810,12 +1800,9 @@
      */
     private void closeUI(boolean immediateClose) {
         if (mUI != null) {
-            mUI.close(immediateClose, new Runnable() {
-                @Override
-                public void run() {
-                    if (mClient != null) mClient.onComplete();
-                    closeClient();
-                }
+            mUI.close(immediateClose, () -> {
+                if (mClient != null) mClient.onComplete();
+                closeClient();
             });
             mUI = null;
             mIsCurrentPaymentRequestShowing = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
index 8dac78e..2d36d86 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
@@ -78,13 +78,10 @@
     public void getInstruments(Map<String, PaymentMethodData> unusedMethodDataMap,
             String unusedOrigin, String unusedIFrameOrigin, byte[][] unusedCertificateChain,
             final InstrumentsCallback callback) {
-        new Handler().post(new Runnable() {
-            @Override
-            public void run() {
-                List<PaymentInstrument> instruments = new ArrayList();
-                instruments.add(ServiceWorkerPaymentApp.this);
-                callback.onInstrumentsReady(ServiceWorkerPaymentApp.this, instruments);
-            }
+        new Handler().post(() -> {
+            List<PaymentInstrument> instruments = new ArrayList();
+            instruments.add(ServiceWorkerPaymentApp.this);
+            callback.onInstrumentsReady(ServiceWorkerPaymentApp.this, instruments);
         });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java
index b4916d58..7cecc91d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java
@@ -22,8 +22,6 @@
 import org.chromium.chrome.browser.accessibility.FontSizePrefs.FontSizePrefsObserver;
 import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
 
-import java.util.concurrent.Callable;
-
 /**
  * Tests for {@link FontSizePrefs}.
  */
@@ -175,89 +173,54 @@
         }
 
         private void assertConsistent() {
-            ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-                @Override
-                public void run() {
-                    Assert.assertEquals(getUserFontScaleFactor(), mUserFontScaleFactor, EPSILON);
-                    Assert.assertEquals(getFontScaleFactor(), mFontScaleFactor, EPSILON);
-                    Assert.assertEquals(getForceEnableZoom(), mForceEnableZoom);
-                }
+            ThreadUtils.runOnUiThreadBlocking(() -> {
+                Assert.assertEquals(getUserFontScaleFactor(), mUserFontScaleFactor, EPSILON);
+                Assert.assertEquals(getFontScaleFactor(), mFontScaleFactor, EPSILON);
+                Assert.assertEquals(getForceEnableZoom(), mForceEnableZoom);
             });
         }
     }
 
     private FontSizePrefs getFontSizePrefs(final Context context) {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<FontSizePrefs>() {
-            @Override
-            public FontSizePrefs call() {
-                return FontSizePrefs.getInstance(context);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlockingNoException(
+                () -> FontSizePrefs.getInstance(context));
     }
 
     private TestingObserver createAndAddFontSizePrefsObserver() {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<TestingObserver>() {
-            @Override
-            public TestingObserver call() {
-                TestingObserver observer = new TestingObserver();
-                mFontSizePrefs.addObserver(observer);
-                return observer;
-            }
+        return ThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            TestingObserver observer = new TestingObserver();
+            mFontSizePrefs.addObserver(observer);
+            return observer;
         });
     }
 
     private void setUserFontScaleFactor(final float fontsize) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mFontSizePrefs.setUserFontScaleFactor(fontsize);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> mFontSizePrefs.setUserFontScaleFactor(fontsize));
     }
 
     private float getUserFontScaleFactor() {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Float>() {
-            @Override
-            public Float call() {
-                return mFontSizePrefs.getUserFontScaleFactor();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mFontSizePrefs.getUserFontScaleFactor());
     }
 
     private float getFontScaleFactor() {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Float>() {
-            @Override
-            public Float call() {
-                return mFontSizePrefs.getFontScaleFactor();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mFontSizePrefs.getFontScaleFactor());
     }
 
     private void setForceEnableZoomFromUser(final boolean enabled) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mFontSizePrefs.setForceEnableZoomFromUser(enabled);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> mFontSizePrefs.setForceEnableZoomFromUser(enabled));
     }
 
     private boolean getForceEnableZoom() {
-        return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() {
-            @Override
-            public Boolean call() {
-                return mFontSizePrefs.getForceEnableZoom();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mFontSizePrefs.getForceEnableZoom());
     }
 
     private void setSystemFontScaleForTest(final float systemFontScale) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mFontSizePrefs.setSystemFontScaleForTest(systemFontScale);
-                mFontSizePrefs.onSystemFontScaleChanged();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mFontSizePrefs.setSystemFontScaleForTest(systemFontScale);
+            mFontSizePrefs.onSystemFontScaleChanged();
         });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
index fd791d0..37feb71 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
@@ -35,8 +35,6 @@
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
 
-import java.util.concurrent.Callable;
-
 /**
  * Tests AppMenu popup
  */
@@ -81,32 +79,18 @@
         InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
 
         ChromeActivity.setAppMenuHandlerFactoryForTesting(
-                new ChromeActivity.AppMenuHandlerFactory() {
-                    @Override
-                    public AppMenuHandler get(Activity activity, AppMenuPropertiesDelegate delegate,
-                            int menuResourceId) {
-                        mAppMenuHandler =
-                                new AppMenuHandlerForTest(activity, delegate, menuResourceId);
-                        return mAppMenuHandler;
-                    }
+                (activity, delegate, menuResourceId) -> {
+                    mAppMenuHandler =
+                            new AppMenuHandlerForTest(activity, delegate, menuResourceId);
+                    return mAppMenuHandler;
                 });
 
         mActivityTestRule.startMainActivityWithURL(TEST_URL);
 
         showAppMenuAndAssertMenuShown();
         mAppMenu = mActivityTestRule.getActivity().getAppMenuHandler().getAppMenu();
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mAppMenu.getListView().setSelection(0);
-            }
-        });
-        CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return getCurrentFocusedRow();
-            }
-        }));
+        ThreadUtils.runOnUiThread(() -> mAppMenu.getListView().setSelection(0));
+        CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, () -> getCurrentFocusedRow()));
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
@@ -231,12 +215,7 @@
     }
 
     private void showAppMenuAndAssertMenuShown() {
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mAppMenuHandler.showAppMenu(null, false);
-            }
-        });
+        ThreadUtils.runOnUiThread((Runnable) () -> mAppMenuHandler.showAppMenu(null, false));
         CriteriaHelper.pollInstrumentationThread(new Criteria("AppMenu did not show") {
             @Override
             public boolean isSatisfied() {
@@ -264,23 +243,14 @@
             pressKey(towardsTop ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN);
             final int expectedPosition = index + increment;
             CriteriaHelper.pollInstrumentationThread(
-                    Criteria.equals(expectedPosition, new Callable<Integer>() {
-                        @Override
-                        public Integer call() {
-                            return getCurrentFocusedRow();
-                        }
-                    }));
+                    Criteria.equals(expectedPosition, () -> getCurrentFocusedRow()));
         }
 
         // Try moving past it by one.
         if (movePast) {
             pressKey(towardsTop ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN);
-            CriteriaHelper.pollInstrumentationThread(Criteria.equals(end, new Callable<Integer>() {
-                @Override
-                public Integer call() {
-                    return getCurrentFocusedRow();
-                }
-            }));
+            CriteriaHelper.pollInstrumentationThread(Criteria.equals(end,
+                    () -> getCurrentFocusedRow()));
         }
 
         // The menu should stay open.
@@ -289,12 +259,9 @@
 
     private void pressKey(final int keycode) {
         final View view = mAppMenu.getListView();
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keycode));
-                view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keycode));
-            }
+        ThreadUtils.runOnUiThread(() -> {
+            view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keycode));
+            view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keycode));
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/ChromeHomeAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/ChromeHomeAppMenuTest.java
index ae62965..298000e1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/ChromeHomeAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/ChromeHomeAppMenuTest.java
@@ -60,12 +60,9 @@
         assertTrue(iconRow.getReloadButtonForTests().isEnabled());
 
         // Navigate backward, open the menu and assert forward button is enabled.
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mAppMenuHandler.hideAppMenu();
-                mBottomSheetTestRule.getActivity().getActivityTab().goBack();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mAppMenuHandler.hideAppMenu();
+            mBottomSheetTestRule.getActivity().getActivityTab().goBack();
         });
 
         showAppMenuAndAssertMenuShown();
@@ -76,12 +73,8 @@
     @Test
     @SmallTest
     public void testTabSwitcherMenu() throws IllegalArgumentException {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mBottomSheetTestRule.getActivity().getLayoutManager().showOverview(false);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mBottomSheetTestRule.getActivity().getLayoutManager().showOverview(false));
 
         showAppMenuAndAssertMenuShown();
         AppMenu appMenu = mAppMenuHandler.getAppMenu();
@@ -90,12 +83,7 @@
     }
 
     private void showAppMenuAndAssertMenuShown() {
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mAppMenuHandler.showAppMenu(null, false);
-            }
-        });
+        ThreadUtils.runOnUiThread((Runnable) () -> mAppMenuHandler.showAppMenu(null, false));
         CriteriaHelper.pollUiThread(new Criteria("AppMenu did not show") {
             @Override
             public boolean isSatisfied() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java
index 69f05afe..5be8175 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/DataSaverAppMenuTest.java
@@ -62,14 +62,10 @@
     @Before
     public void setUp() throws Exception {
         ChromeTabbedActivity.setAppMenuHandlerFactoryForTesting(
-                new ChromeTabbedActivity.AppMenuHandlerFactory() {
-                    @Override
-                    public AppMenuHandler get(Activity activity, AppMenuPropertiesDelegate delegate,
-                            int menuResourceId) {
-                        mAppMenuHandler =
-                                new AppMenuHandlerForTest(activity, delegate, menuResourceId);
-                        return mAppMenuHandler;
-                    }
+                (activity, delegate, menuResourceId) -> {
+                    mAppMenuHandler =
+                            new AppMenuHandlerForTest(activity, delegate, menuResourceId);
+                    return mAppMenuHandler;
                 });
 
         mActivityTestRule.startMainActivityOnBlankPage();
@@ -83,15 +79,12 @@
     @CommandLineFlags.Add("disable-field-trial-config")
     @Feature({"Browser", "Main"})
     public void testMenuDataSaverNoFeature() throws Throwable {
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                ContextUtils.getAppSharedPreferences().edit().clear().apply();
-                Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
-                DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                        mActivityTestRule.getActivity().getApplicationContext(), true);
-                Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
-            }
+        mActivityTestRule.runOnUiThread((Runnable) () -> {
+            ContextUtils.getAppSharedPreferences().edit().clear().apply();
+            Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
+            DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
+                    mActivityTestRule.getActivity().getApplicationContext(), true);
+            Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
         });
     }
 
@@ -104,24 +97,21 @@
             "disable-field-trial-config"})
     @Feature({"Browser", "Main"})
     public void testMenuDataSaver() throws Throwable {
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                ContextUtils.getAppSharedPreferences().edit().clear().apply();
-                // Data Saver hasn't been turned on, the footer shouldn't show.
-                Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
+        mActivityTestRule.runOnUiThread((Runnable) () -> {
+            ContextUtils.getAppSharedPreferences().edit().clear().apply();
+            // Data Saver hasn't been turned on, the footer shouldn't show.
+            Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
 
-                // Turn Data Saver on, the footer should show.
-                DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                        mActivityTestRule.getActivity().getApplicationContext(), true);
-                Assert.assertEquals(R.layout.data_reduction_main_menu_footer,
-                        mAppMenuHandler.getDelegate().getFooterResourceId());
+            // Turn Data Saver on, the footer should show.
+            DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
+                    mActivityTestRule.getActivity().getApplicationContext(), true);
+            Assert.assertEquals(R.layout.data_reduction_main_menu_footer,
+                    mAppMenuHandler.getDelegate().getFooterResourceId());
 
-                // Ensure the footer is removed if the proxy is turned off.
-                DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                        mActivityTestRule.getActivity().getApplicationContext(), false);
-                Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
-            }
+            // Ensure the footer is removed if the proxy is turned off.
+            DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
+                    mActivityTestRule.getActivity().getApplicationContext(), false);
+            Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
         });
     }
 
@@ -138,25 +128,22 @@
             "disable-field-trial-config"})
     @Feature({"Browser", "Main"})
     public void testMenuDataSaverPersistent() throws Throwable {
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                ContextUtils.getAppSharedPreferences().edit().clear().apply();
-                // Data Saver hasn't been turned on, the footer shouldn't show.
-                Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
+        mActivityTestRule.runOnUiThread((Runnable) () -> {
+            ContextUtils.getAppSharedPreferences().edit().clear().apply();
+            // Data Saver hasn't been turned on, the footer shouldn't show.
+            Assert.assertEquals(0, mAppMenuHandler.getDelegate().getFooterResourceId());
 
-                // Turn Data Saver on, the footer should show.
-                DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                        mActivityTestRule.getActivity().getApplicationContext(), true);
-                Assert.assertEquals(R.layout.data_reduction_main_menu_footer,
-                        mAppMenuHandler.getDelegate().getFooterResourceId());
+            // Turn Data Saver on, the footer should show.
+            DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
+                    mActivityTestRule.getActivity().getApplicationContext(), true);
+            Assert.assertEquals(R.layout.data_reduction_main_menu_footer,
+                    mAppMenuHandler.getDelegate().getFooterResourceId());
 
-                // Ensure the footer remains if the proxy is turned off.
-                DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                        mActivityTestRule.getActivity().getApplicationContext(), false);
-                Assert.assertEquals(R.layout.data_reduction_main_menu_footer,
-                        mAppMenuHandler.getDelegate().getFooterResourceId());
-            }
+            // Ensure the footer remains if the proxy is turned off.
+            DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
+                    mActivityTestRule.getActivity().getApplicationContext(), false);
+            Assert.assertEquals(R.layout.data_reduction_main_menu_footer,
+                    mAppMenuHandler.getDelegate().getFooterResourceId());
         });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryTest.java
index 6895523e..08afdb0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryTest.java
@@ -33,7 +33,6 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.UiUtils;
 
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
@@ -91,16 +90,13 @@
                         + "Doooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooe",
                 "Acme Inc", "1 Main\nApt A", "CA", "San Francisco", "", "94102", "", "US",
                 "(415) 999-0000", "jane@acme.inc", "en"));
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mViewCoreRef.set(mActivityTestRule.getActivity().getCurrentContentViewCore());
-                mWebContentsRef.set(mViewCoreRef.get().getWebContents());
-                mContainerRef.set(mViewCoreRef.get().getContainerView());
-                mKeyboardAccessoryRef.set(mActivityTestRule.getActivity()
-                                                  .getWindowAndroid()
-                                                  .getKeyboardAccessoryView());
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mViewCoreRef.set(mActivityTestRule.getActivity().getCurrentContentViewCore());
+            mWebContentsRef.set(mViewCoreRef.get().getWebContents());
+            mContainerRef.set(mViewCoreRef.get().getContainerView());
+            mKeyboardAccessoryRef.set(mActivityTestRule.getActivity()
+                    .getWindowAndroid()
+                    .getKeyboardAccessoryView());
         });
         DOMUtils.waitForNonZeroNodeBounds(mWebContentsRef.get(), "fn");
     }
@@ -116,12 +112,8 @@
         loadTestPage(false);
         Assert.assertTrue("Keyboard accessory should be hidden.",
                 ThreadUtils
-                        .runOnUiThreadBlocking(new Callable<Boolean>() {
-                            @Override
-                            public Boolean call() {
-                                return mKeyboardAccessoryRef.get().getVisibility() == View.GONE;
-                            }
-                        })
+                        .runOnUiThreadBlocking(
+                                () -> mKeyboardAccessoryRef.get().getVisibility() == View.GONE)
                         .booleanValue());
     }
 
@@ -144,12 +136,8 @@
         });
         Assert.assertTrue("Keyboard accessory should be showing.",
                 ThreadUtils
-                        .runOnUiThreadBlocking(new Callable<Boolean>() {
-                            @Override
-                            public Boolean call() {
-                                return mKeyboardAccessoryRef.get().getVisibility() == View.VISIBLE;
-                            }
-                        })
+                        .runOnUiThreadBlocking(
+                                () -> mKeyboardAccessoryRef.get().getVisibility() == View.VISIBLE)
                         .booleanValue());
     }
 
@@ -170,12 +158,8 @@
                         mActivityTestRule.getActivity(), mContainerRef.get());
             }
         });
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((HorizontalScrollView) mKeyboardAccessoryRef.get()).scrollTo(2000, 0);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> ((HorizontalScrollView) mKeyboardAccessoryRef.get()).scrollTo(2000, 0));
         CriteriaHelper.pollUiThread(
                 new Criteria("First suggestion should be off the screen after manual scroll.") {
                     @Override
@@ -223,12 +207,8 @@
                         mActivityTestRule.getActivity(), mContainerRef.get());
             }
         });
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((HorizontalScrollView) mKeyboardAccessoryRef.get()).scrollTo(0, 0);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> ((HorizontalScrollView) mKeyboardAccessoryRef.get()).scrollTo(0, 0));
         CriteriaHelper.pollUiThread(
                 new Criteria("Last suggestion should be on the screen after manual scroll.") {
                     @Override
@@ -278,13 +258,10 @@
                         mActivityTestRule.getActivity(), mContainerRef.get());
             }
         });
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                View suggestion = getSuggestionAt(0);
-                if (suggestion != null) {
-                    suggestion.performClick();
-                }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            View suggestion = getSuggestionAt(0);
+            if (suggestion != null) {
+                suggestion.performClick();
             }
         });
         CriteriaHelper.pollUiThread(new Criteria("Keyboard should be hidden.") {
@@ -296,12 +273,8 @@
         });
         Assert.assertTrue("Keyboard accessory should be hidden.",
                 ThreadUtils
-                        .runOnUiThreadBlocking(new Callable<Boolean>() {
-                            @Override
-                            public Boolean call() {
-                                return mKeyboardAccessoryRef.get().getVisibility() == View.GONE;
-                            }
-                        })
+                        .runOnUiThreadBlocking(
+                                () -> mKeyboardAccessoryRef.get().getVisibility() == View.GONE)
                         .booleanValue());
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java
index 9c4540d..a41275d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupTest.java
@@ -37,7 +37,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -141,12 +140,7 @@
     public void setUp() throws Exception {
         mAutofillLoggedEntries = new ArrayList<AutofillLogger.LogEntry>();
         AutofillLogger.setLoggerForTesting(
-                new AutofillLogger.Logger() {
-                    @Override
-                    public void didFillField(AutofillLogger.LogEntry logEntry) {
-                        mAutofillLoggedEntries.add(logEntry);
-                    }
-                }
+                logEntry -> mAutofillLoggedEntries.add(logEntry)
         );
     }
 
@@ -184,12 +178,7 @@
 
         final ChromiumBaseInputConnection inputConnection =
                 viewCore.getImeAdapterForTest().getInputConnectionForTest();
-        inputConnection.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-                inputConnection.setComposingText(inputText, 1);
-            }
-        });
+        inputConnection.getHandler().post(() -> inputConnection.setComposingText(inputText, 1));
 
         waitForAnchorViewAdd(view);
         View anchorView = view.findViewById(R.id.dropdown_popup_window);
@@ -317,12 +306,7 @@
     private void waitForKeyboardShowRequest(final TestInputMethodManagerWrapper immw,
             final int count) {
         CriteriaHelper.pollUiThread(
-                Criteria.equals(count, new Callable<Integer>() {
-                    @Override
-                    public Integer call() {
-                        return immw.getShowSoftInputCounter();
-                    }
-                }));
+                Criteria.equals(count, () -> immw.getShowSoftInputCounter()));
     }
 
     private void waitForAnchorViewAdd(final ViewGroup view) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java
index 448aa1cc..32ac0b8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillPopupWithKeyboardTest.java
@@ -31,7 +31,6 @@
 import org.chromium.ui.R;
 import org.chromium.ui.UiUtils;
 
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
@@ -82,13 +81,10 @@
         final AtomicReference<ContentViewCore> viewCoreRef = new AtomicReference<ContentViewCore>();
         final AtomicReference<WebContents> webContentsRef = new AtomicReference<WebContents>();
         final AtomicReference<ViewGroup> viewRef = new AtomicReference<ViewGroup>();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                viewCoreRef.set(mActivityTestRule.getActivity().getCurrentContentViewCore());
-                webContentsRef.set(viewCoreRef.get().getWebContents());
-                viewRef.set(viewCoreRef.get().getContainerView());
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            viewCoreRef.set(mActivityTestRule.getActivity().getCurrentContentViewCore());
+            webContentsRef.set(viewCoreRef.get().getWebContents());
+            viewRef.set(viewCoreRef.get().getContainerView());
         });
         DOMUtils.waitForNonZeroNodeBounds(webContentsRef.get(), "fn");
 
@@ -116,12 +112,8 @@
                         return viewRef.get().findViewById(R.id.dropdown_popup_window) != null;
                     }
                 });
-        Object popupObject = ThreadUtils.runOnUiThreadBlocking(new Callable<Object>() {
-            @Override
-            public Object call() {
-                return viewRef.get().findViewById(R.id.dropdown_popup_window).getTag();
-            }
-        });
+        Object popupObject = ThreadUtils.runOnUiThreadBlocking(
+                () -> viewRef.get().findViewById(R.id.dropdown_popup_window).getTag());
         Assert.assertTrue(popupObject instanceof AutofillPopup);
         final AutofillPopup popup = (AutofillPopup) popupObject;
         CriteriaHelper.pollUiThread(new Criteria("Autofill Popup was never shown.") {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
index 8a7d9e1..f8a44bce 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTest.java
@@ -66,19 +66,16 @@
                 ViewAndroidDelegate.createBasicDelegate(
                         activity.getCurrentContentViewCore().getContainerView());
 
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                View anchorView = viewDelegate.acquireView();
-                viewDelegate.setViewPosition(anchorView, 50f, 500f, 500f, 500f, 1f, 10, 10);
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            View anchorView = viewDelegate.acquireView();
+            viewDelegate.setViewPosition(anchorView, 50f, 500f, 500f, 500f, 1f, 10, 10);
 
-                mWindowAndroid = new ActivityWindowAndroid(activity);
-                mAutofillPopup = new AutofillPopup(activity, anchorView, mMockAutofillCallback);
-                mAutofillPopup.filterAndShow(new AutofillSuggestion[0], false /* isRtl */,
-                        Color.TRANSPARENT /* backgroundColor */,
-                        Color.TRANSPARENT /* dividerColor */, 0 /* dropdownItemHeight */,
-                        0 /* margin */);
-            }
+            mWindowAndroid = new ActivityWindowAndroid(activity);
+            mAutofillPopup = new AutofillPopup(activity, anchorView, mMockAutofillCallback);
+            mAutofillPopup.filterAndShow(new AutofillSuggestion[0], false /* isRtl */,
+                    Color.TRANSPARENT /* backgroundColor */,
+                    Color.TRANSPARENT /* dividerColor */, 0 /* dropdownItemHeight */,
+                    0 /* margin */);
         });
     }
 
@@ -143,15 +140,11 @@
     }
 
     public void openAutofillPopupAndWaitUntilReady(final AutofillSuggestion[] suggestions) {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mAutofillPopup.filterAndShow(suggestions, false /* isRtl */,
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mAutofillPopup.filterAndShow(suggestions, false /* isRtl */,
                         Color.TRANSPARENT /* backgroundColor */,
                         Color.TRANSPARENT /* dividerColor */, 0 /* dropdownItemHeight */,
-                        0 /* margin */);
-            }
-        });
+                        0 /* margin */));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
index ef11eb9..ac32f3d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java
@@ -8,10 +8,8 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.PersonalDataManagerObserver;
 
 import java.util.List;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -28,40 +26,24 @@
     }
 
     void setRequestTimeoutForTesting() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().setRequestTimeoutForTesting(0);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().setRequestTimeoutForTesting(0));
     }
 
     AutofillProfile getProfile(final String guid) throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<AutofillProfile>() {
-            @Override
-            public AutofillProfile call() {
-                return PersonalDataManager.getInstance().getProfile(guid);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getProfile(guid));
     }
 
     List<AutofillProfile> getProfilesToSuggest(final boolean includeNameInLabel) throws
             ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<List<AutofillProfile>>() {
-            @Override
-            public List<AutofillProfile> call() {
-                return PersonalDataManager.getInstance().getProfilesToSuggest(includeNameInLabel);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getProfilesToSuggest(includeNameInLabel));
     }
 
     List<AutofillProfile> getProfilesForSettings() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<List<AutofillProfile>>() {
-            @Override
-            public List<AutofillProfile> call() {
-                return PersonalDataManager.getInstance().getProfilesForSettings();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getProfilesForSettings());
     }
 
     int getNumberOfProfilesToSuggest() throws ExecutionException {
@@ -75,52 +57,32 @@
     public String setProfile(final AutofillProfile profile) throws InterruptedException,
             ExecutionException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        String guid = ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return PersonalDataManager.getInstance().setProfile(profile);
-            }
-        });
+        String guid = ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().setProfile(profile));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
         return guid;
     }
 
     public void deleteProfile(final String guid) throws InterruptedException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().deleteProfile(guid);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().deleteProfile(guid));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
     public CreditCard getCreditCard(final String guid) throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<CreditCard>() {
-            @Override
-            public CreditCard call() {
-                return PersonalDataManager.getInstance().getCreditCard(guid);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getCreditCard(guid));
     }
 
     List<CreditCard> getCreditCardsToSuggest() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<List<CreditCard>>() {
-            @Override
-            public List<CreditCard> call() {
-                return PersonalDataManager.getInstance().getCreditCardsToSuggest();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getCreditCardsToSuggest());
     }
 
     List<CreditCard> getCreditCardsForSettings() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<List<CreditCard>>() {
-            @Override
-            public List<CreditCard> call() {
-                return PersonalDataManager.getInstance().getCreditCardsForSettings();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getCreditCardsForSettings());
     }
 
     int getNumberOfCreditCardsToSuggest() throws ExecutionException {
@@ -134,12 +96,8 @@
     public String setCreditCard(final CreditCard card) throws InterruptedException,
             ExecutionException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        String guid = ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return PersonalDataManager.getInstance().setCreditCard(card);
-            }
-        });
+        String guid = ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().setCreditCard(card));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
         return guid;
     }
@@ -147,23 +105,15 @@
     public void addServerCreditCard(final CreditCard card)
             throws InterruptedException, ExecutionException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().addServerCreditCardForTest(card);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().addServerCreditCardForTest(card));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
     void deleteCreditCard(final String guid) throws InterruptedException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().deleteCreditCard(guid);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().deleteCreditCard(guid));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -176,12 +126,8 @@
      */
     void recordAndLogProfileUse(final String guid) throws InterruptedException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().recordAndLogProfileUse(guid);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().recordAndLogProfileUse(guid));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -199,12 +145,9 @@
     public void setProfileUseStatsForTesting(final String guid, final int count, final long date)
             throws InterruptedException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().setProfileUseStatsForTesting(guid, count, date);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().setProfileUseStatsForTesting(guid, count,
+                        date));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -216,12 +159,8 @@
      */
     public int getProfileUseCountForTesting(final String guid) throws InterruptedException,
             ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return PersonalDataManager.getInstance().getProfileUseCountForTesting(guid);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getProfileUseCountForTesting(guid));
     }
 
     /**
@@ -234,12 +173,8 @@
      */
     public long getProfileUseDateForTesting(final String guid) throws InterruptedException,
             ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Long>() {
-            @Override
-            public Long call() {
-                return PersonalDataManager.getInstance().getProfileUseDateForTesting(guid);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getProfileUseDateForTesting(guid));
     }
 
     /**
@@ -252,12 +187,8 @@
     public void recordAndLogCreditCardUse(final String guid) throws InterruptedException,
             TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().recordAndLogCreditCardUse(guid);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().recordAndLogCreditCardUse(guid));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -275,13 +206,9 @@
     public void setCreditCardUseStatsForTesting(final String guid, final int count, final long date)
             throws InterruptedException, TimeoutException {
         int callCount = mOnPersonalDataChangedHelper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                PersonalDataManager.getInstance().setCreditCardUseStatsForTesting(
-                        guid, count, date);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().setCreditCardUseStatsForTesting(
+                        guid, count, date));
         mOnPersonalDataChangedHelper.waitForCallback(callCount);
     }
 
@@ -293,12 +220,8 @@
      */
     public int getCreditCardUseCountForTesting(final String guid) throws InterruptedException,
             ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return PersonalDataManager.getInstance().getCreditCardUseCountForTesting(guid);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getCreditCardUseCountForTesting(guid));
     }
 
     /**
@@ -311,12 +234,8 @@
      */
     public long getCreditCardUseDateForTesting(final String guid) throws InterruptedException,
             ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Long>() {
-            @Override
-            public Long call() {
-                return PersonalDataManager.getInstance().getCreditCardUseDateForTesting(guid);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getCreditCardUseDateForTesting(guid));
     }
 
     /**
@@ -327,29 +246,16 @@
      *         For more details see the comment header in time.h.
      */
     public long getCurrentDateForTesting() throws InterruptedException, ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Long>() {
-            @Override
-            public Long call() {
-                return PersonalDataManager.getInstance().getCurrentDateForTesting();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> PersonalDataManager.getInstance().getCurrentDateForTesting());
     }
 
     private void registerDataObserver() {
         try {
             int callCount = mOnPersonalDataChangedHelper.getCallCount();
-            boolean isDataLoaded = ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
-                @Override
-                public Boolean call() {
-                    return PersonalDataManager.getInstance().registerDataObserver(
-                            new PersonalDataManagerObserver() {
-                                @Override
-                                public void onPersonalDataChanged() {
-                                    mOnPersonalDataChangedHelper.notifyCalled();
-                                }
-                            });
-                }
-            });
+            boolean isDataLoaded = ThreadUtils.runOnUiThreadBlocking(
+                    () -> PersonalDataManager.getInstance().registerDataObserver(
+                            () -> mOnPersonalDataChangedHelper.notifyCalled()));
             if (isDataLoaded) return;
             mOnPersonalDataChangedHelper.waitForCallback(callCount);
         } catch (TimeoutException | InterruptedException | ExecutionException e) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index f9d50e2..b7e33118 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -3113,7 +3113,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    public void testLongPressHidesFindInPageOverlay() throws Exception {
+    public void testTriggeringContextualSearchHidesFindInPageOverlay() throws Exception {
         MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
                 mActivityTestRule.getActivity(), R.id.find_in_page_id);
 
@@ -3133,7 +3133,7 @@
         View findToolbar = mActivityTestRule.getActivity().findViewById(R.id.find_toolbar);
         Assert.assertTrue(findToolbar.isShown());
 
-        longPressNode("states");
+        simulateTapSearch("search");
 
         waitForPanelToPeek();
         Assert.assertFalse(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
index dc4da257..8539873 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -28,13 +28,13 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.EnormousTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.base.test.util.parameter.CommandLineParameter;
+import org.chromium.base.test.util.parameter.SkipCommandLineParameterization;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -597,8 +597,8 @@
      * Test to verify that the security icon is present when visiting http:// URLs.
      */
     @Test
-    //@MediumTest
-    @DisabledTest(message = "crbug.com/754723")
+    @MediumTest
+    @SkipCommandLineParameterization
     public void testSecurityIconOnHTTP() throws InterruptedException {
         EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
                 InstrumentationRegistry.getInstrumentation().getContext());
@@ -613,8 +613,15 @@
                     (LocationBarLayout) mActivityTestRule.getActivity().findViewById(
                             R.id.location_bar);
             boolean securityIcon = locationBar.isSecurityButtonShown();
-            Assert.assertFalse("Omnibox should not have a Security icon", securityIcon);
-            Assert.assertFalse(securityButton.isShown());
+            if (DeviceFormFactor.isTablet()) {
+                Assert.assertTrue("Omnibox should have a Security icon", securityIcon);
+                Assert.assertTrue(securityButton.isShown());
+                Assert.assertEquals(
+                        R.drawable.omnibox_info, locationBar.getSecurityIconResourceId());
+            } else {
+                Assert.assertFalse("Omnibox should not have a Security icon", securityIcon);
+                Assert.assertFalse(securityButton.isShown());
+            }
         } finally {
             testServer.stopAndDestroyServer();
         }
@@ -625,6 +632,7 @@
      */
     @Test
     @MediumTest
+    @SkipCommandLineParameterization
     public void testSecurityIconOnHTTPS() throws InterruptedException {
         EmbeddedTestServer httpsTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
                 InstrumentationRegistry.getInstrumentation().getContext(),
@@ -645,6 +653,8 @@
             Assert.assertEquals("security_button with wrong resource-id", R.id.security_button,
                     securityButton.getId());
             Assert.assertTrue(securityButton.isShown());
+            Assert.assertEquals(
+                    R.drawable.omnibox_https_valid, locationBar.getSecurityIconResourceId());
         } finally {
             httpsTestServer.stopAndDestroyServer();
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
index f793e32..0318a131 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java
@@ -1077,15 +1077,10 @@
     }
 
     private void findApps(final Set<String> methodNames) throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                AndroidPaymentAppFinder.find(
-                        mRule.getActivity().getCurrentContentViewCore().getWebContents(),
-                        methodNames, new PaymentManifestWebDataService(), mDownloader, mParser,
-                        mPackageManager, AndroidPaymentAppFinderTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> AndroidPaymentAppFinder.find(
+                mRule.getActivity().getCurrentContentViewCore().getWebContents(),
+                methodNames, new PaymentManifestWebDataService(), mDownloader, mParser,
+                mPackageManager, AndroidPaymentAppFinderTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java
index 55868a9..592e8283 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java
@@ -94,13 +94,10 @@
         mRule.startMainActivityOnBlankPage();
         mServer = EmbeddedTestServer.createAndStartServer(
                 InstrumentationRegistry.getInstrumentation().getContext());
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mDownloader.initialize(
-                        mRule.getActivity().getCurrentContentViewCore().getWebContents());
-                mDownloader.allowHttpForTest();
-            }
+        mRule.runOnUiThread((Runnable) () -> {
+            mDownloader.initialize(
+                    mRule.getActivity().getCurrentContentViewCore().getWebContents());
+            mDownloader.allowHttpForTest();
         });
         mDownloadComplete = false;
         mDownloadPaymentMethodManifestSuccess = false;
@@ -112,12 +109,7 @@
 
     @After
     public void tearDown() throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mDownloader.destroy();
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mDownloader.destroy());
         mServer.stopAndDestroyServer();
     }
 
@@ -126,12 +118,8 @@
     public void testDownloadWebAppManifest() throws Throwable {
         final URI uri =
                 new URI(mServer.getURL("/components/test/data/payments/bobpay.com/app.json"));
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mDownloader.downloadWebAppManifest(uri, PaymentManifestDownloaderTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mDownloader.downloadWebAppManifest(uri,
+                PaymentManifestDownloaderTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -148,12 +136,8 @@
     @Feature({"Payments"})
     public void testUnableToDownloadWebAppManifest() throws Throwable {
         final URI uri = new URI(mServer.getURL("/no-such-app.json"));
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mDownloader.downloadWebAppManifest(uri, PaymentManifestDownloaderTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mDownloader.downloadWebAppManifest(uri,
+                PaymentManifestDownloaderTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -168,12 +152,8 @@
     @Feature({"Payments"})
     public void testDownloadPaymentMethodManifest() throws Throwable {
         final URI uri = new URI(mServer.getURL("/components/test/data/payments/bobpay.com/webpay"));
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mDownloader.downloadPaymentMethodManifest(uri, PaymentManifestDownloaderTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mDownloader.downloadPaymentMethodManifest(uri,
+                PaymentManifestDownloaderTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -190,12 +170,8 @@
     @Feature({"Payments"})
     public void testUnableToDownloadPaymentMethodManifest() throws Throwable {
         final URI uri = new URI(mServer.getURL("/no-such-payment-method-name"));
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mDownloader.downloadPaymentMethodManifest(uri, PaymentManifestDownloaderTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mDownloader.downloadPaymentMethodManifest(uri,
+                PaymentManifestDownloaderTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -216,16 +192,13 @@
         final URI webAppUri1 = new URI(mServer.getURL("/no-such-app.json"));
         final URI webAppUri2 =
                 new URI(mServer.getURL("/components/test/data/payments/bobpay.com/app.json"));
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mDownloader.downloadPaymentMethodManifest(
-                        paymentMethodUri1, PaymentManifestDownloaderTest.this);
-                mDownloader.downloadPaymentMethodManifest(
-                        paymentMethodUri2, PaymentManifestDownloaderTest.this);
-                mDownloader.downloadWebAppManifest(webAppUri1, PaymentManifestDownloaderTest.this);
-                mDownloader.downloadWebAppManifest(webAppUri2, PaymentManifestDownloaderTest.this);
-            }
+        mRule.runOnUiThread((Runnable) () -> {
+            mDownloader.downloadPaymentMethodManifest(
+                    paymentMethodUri1, PaymentManifestDownloaderTest.this);
+            mDownloader.downloadPaymentMethodManifest(
+                    paymentMethodUri2, PaymentManifestDownloaderTest.this);
+            mDownloader.downloadWebAppManifest(webAppUri1, PaymentManifestDownloaderTest.this);
+            mDownloader.downloadWebAppManifest(webAppUri2, PaymentManifestDownloaderTest.this);
         });
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
index 2e41989..4116aa2f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java
@@ -70,12 +70,7 @@
     @Before
     public void setUp() throws Throwable {
         mRule.startMainActivityOnBlankPage();
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mParser.startUtilityProcess();
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mParser.startUtilityProcess());
         mWebAppManifestUris = null;
         mSupportedOrigins = null;
         mAllOriginsSupported = false;
@@ -87,25 +82,15 @@
 
     @After
     public void tearDown() throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mParser.stopUtilityProcess();
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mParser.stopUtilityProcess());
     }
 
     @Test
     @MediumTest
     @Feature({"Payments"})
     public void testParseInvalidPaymentMethodManifest() throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mParser.parsePaymentMethodManifest(
-                        "invalid payment method manifest", PaymentManifestParserTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mParser.parsePaymentMethodManifest(
+                "invalid payment method manifest", PaymentManifestParserTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -118,22 +103,17 @@
     @MediumTest
     @Feature({"Payments"})
     public void testParsePaymentMethodManifest() throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mParser.parsePaymentMethodManifest("{"
-                                + "  \"default_applications\": ["
-                                + "    \"https://bobpay.com/app.json\","
-                                + "    \"https://alicepay.com/app.json\""
-                                + "  ],"
-                                + "  \"supported_origins\": ["
-                                + "    \"https://charliepay.com\","
-                                + "    \"https://evepay.com\""
-                                + "  ]"
-                                + "}",
-                        PaymentManifestParserTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mParser.parsePaymentMethodManifest("{"
+                        + "  \"default_applications\": ["
+                        + "    \"https://bobpay.com/app.json\","
+                        + "    \"https://alicepay.com/app.json\""
+                        + "  ],"
+                        + "  \"supported_origins\": ["
+                        + "    \"https://charliepay.com\","
+                        + "    \"https://evepay.com\""
+                        + "  ]"
+                        + "}",
+                PaymentManifestParserTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -155,13 +135,8 @@
     @MediumTest
     @Feature({"Payments"})
     public void testParsePaymentMethodManifestWithAllOriginsSupported() throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mParser.parsePaymentMethodManifest(
-                        "{\"supported_origins\": \"*\"}", PaymentManifestParserTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mParser.parsePaymentMethodManifest(
+                "{\"supported_origins\": \"*\"}", PaymentManifestParserTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -179,13 +154,8 @@
     @MediumTest
     @Feature({"Payments"})
     public void testParseInvalidWebAppManifest() throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mParser.parseWebAppManifest(
-                        "invalid web app manifest", PaymentManifestParserTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mParser.parseWebAppManifest(
+                "invalid web app manifest", PaymentManifestParserTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
@@ -198,26 +168,21 @@
     @MediumTest
     @Feature({"Payments"})
     public void testParseWebAppManifest() throws Throwable {
-        mRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mParser.parseWebAppManifest("{"
-                                + "  \"related_applications\": [{"
-                                + "    \"platform\": \"play\", "
-                                + "    \"id\": \"com.bobpay.app\", "
-                                + "    \"min_version\": \"1\", "
-                                + "    \"fingerprints\": [{"
-                                + "      \"type\": \"sha256_cert\", "
-                                + "      \"value\": \""
-                                + "00:01:02:03:04:05:06:07:08:09:"
-                                + "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
-                                + "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
-                                + "    }]"
-                                + "  }]"
-                                + "}",
-                        PaymentManifestParserTest.this);
-            }
-        });
+        mRule.runOnUiThread((Runnable) () -> mParser.parseWebAppManifest("{"
+                        + "  \"related_applications\": [{"
+                        + "    \"platform\": \"play\", "
+                        + "    \"id\": \"com.bobpay.app\", "
+                        + "    \"min_version\": \"1\", "
+                        + "    \"fingerprints\": [{"
+                        + "      \"type\": \"sha256_cert\", "
+                        + "      \"value\": \""
+                        + "00:01:02:03:04:05:06:07:08:09:"
+                        + "A0:A1:A2:A3:A4:A5:A6:A7:A8:A9:"
+                        + "B0:B1:B2:B3:B4:B5:B6:B7:B8:B9:C0:C1\""
+                        + "    }]"
+                        + "  }]"
+                        + "}",
+                PaymentManifestParserTest.this));
         CriteriaHelper.pollInstrumentationThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
index d79613c8..4f020bf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java
@@ -68,17 +68,15 @@
 
         // Press the back button.
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest()
+                        .onBackPressed());
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         // Make sure the canMakePayment events were logged correctly.
-        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.CAN_MAKE_PAYMENT_FALSE;
+        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.CAN_MAKE_PAYMENT_FALSE
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -118,7 +116,8 @@
 
         // Make sure the canMakePayment events were logged correctly.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
-                | Event.COMPLETED | Event.CAN_MAKE_PAYMENT_FALSE;
+                | Event.COMPLETED | Event.CAN_MAKE_PAYMENT_FALSE | Event.REQUEST_METHOD_BASIC_CARD
+                | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -147,7 +146,8 @@
 
         // Make sure the canMakePayment events were logged correctly.
         int expectedSample = Event.SHOWN | Event.OTHER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.CAN_MAKE_PAYMENT_TRUE;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.CAN_MAKE_PAYMENT_TRUE
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -175,7 +175,8 @@
         // Make sure the canMakePayment events were logged correctly.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.CAN_MAKE_PAYMENT_TRUE;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.CAN_MAKE_PAYMENT_TRUE
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -196,17 +197,15 @@
 
         // Press the back button.
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest()
+                        .onBackPressed());
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         // Make sure no canMakePayment events were logged.
-        int expectedSample = Event.SHOWN | Event.USER_ABORTED;
+        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_METHOD_BASIC_CARD
+                | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -233,7 +232,8 @@
         // Make sure no canMakePayment events were logged.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_METHOD_BASIC_CARD
+                | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCardEditorAutoAdvanceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCardEditorAutoAdvanceTest.java
index 54bf1ae..1c138d5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCardEditorAutoAdvanceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCardEditorAutoAdvanceTest.java
@@ -87,12 +87,7 @@
         Assert.assertTrue(mPaymentRequestTestRule.getCardEditorFocusedView() != focusedChildView);
 
         // Request focus to card number field after auto advancing above.
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                focusedChildView.requestFocus();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking((Runnable) () -> focusedChildView.requestFocus());
         mPaymentRequestTestRule.setTextInCardEditorAndWait(
                 new String[] {"3056 9309 0259 041"}, mPaymentRequestTestRule.getEditorTextUpdate());
         Assert.assertTrue(mPaymentRequestTestRule.getCardEditorFocusedView() == focusedChildView);
@@ -127,12 +122,7 @@
         Assert.assertTrue(mPaymentRequestTestRule.getCardEditorFocusedView() != focusedChildView);
 
         // Request focus to card number field after auto advancing above.
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                focusedChildView.requestFocus();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking((Runnable) () -> focusedChildView.requestFocus());
         mPaymentRequestTestRule.setTextInCardEditorAndWait(new String[] {"3782 822463 10005 1"},
                 mPaymentRequestTestRule.getEditorTextUpdate());
         Assert.assertTrue(mPaymentRequestTestRule.getCardEditorFocusedView() == focusedChildView);
@@ -177,12 +167,7 @@
         Assert.assertTrue(mPaymentRequestTestRule.getCardEditorFocusedView() != focusedChildView);
 
         // Request focus to card number field after auto advancing above.
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                focusedChildView.requestFocus();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking((Runnable) () -> focusedChildView.requestFocus());
         mPaymentRequestTestRule.setTextInCardEditorAndWait(new String[] {"4012 8888 8888 1881 1"},
                 mPaymentRequestTestRule.getEditorTextUpdate());
         Assert.assertTrue(mPaymentRequestTestRule.getCardEditorFocusedView() == focusedChildView);
@@ -226,12 +211,7 @@
         Assert.assertTrue(mPaymentRequestTestRule.getCardEditorFocusedView() != focusedChildView);
 
         // Request focus to card number field after auto advancing above.
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                focusedChildView.requestFocus();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking((Runnable) () -> focusedChildView.requestFocus());
         mPaymentRequestTestRule.setTextInCardEditorAndWait(
                 new String[] {"6212 3456 7890 0000 0031"},
                 mPaymentRequestTestRule.getEditorTextUpdate());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java
index a2138b2..ed619ff 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsAndFreeShippingTest.java
@@ -103,7 +103,8 @@
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.HAD_INITIAL_FORM_OF_PAYMENT
                 | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_PHONE
-                | Event.REQUEST_PAYER_EMAIL | Event.REQUEST_PAYER_NAME | Event.REQUEST_SHIPPING;
+                | Event.REQUEST_PAYER_EMAIL | Event.REQUEST_PAYER_NAME | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsTest.java
index df69312..210f44f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestContactDetailsTest.java
@@ -143,18 +143,15 @@
 
         // Quickly press on "add contact info" and then [X].
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getContactDetailsSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getContactDetailsSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -177,18 +174,15 @@
 
         // Quickly press on [X] and then "add contact info."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getContactDetailsSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getContactDetailsSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -249,18 +243,15 @@
 
         // Quickly press on "add contact info" and then "cancel."
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getContactDetailsSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getContactDetailsSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -283,18 +274,15 @@
 
         // Quickly press on "cancel" and then "add contact info."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getContactDetailsSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getContactDetailsSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -335,7 +323,8 @@
         int expectedSample = Event.SHOWN | Event.COMPLETED | Event.PAY_CLICKED
                 | Event.HAD_INITIAL_FORM_OF_PAYMENT | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS
                 | Event.RECEIVED_INSTRUMENT_DETAILS | Event.REQUEST_PAYER_EMAIL
-                | Event.REQUEST_PAYER_PHONE | Event.REQUEST_PAYER_NAME;
+                | Event.REQUEST_PAYER_PHONE | Event.REQUEST_PAYER_NAME
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java
index a8cca016..3a60612 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDynamicShippingSingleAddressTest.java
@@ -229,18 +229,15 @@
 
         // Quickly press "add address" and then [X].
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -263,18 +260,15 @@
 
         // Quickly press [X] and then "add address."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -293,18 +287,15 @@
 
         // Quickly press "add address" and then "cancel."
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -327,18 +318,15 @@
 
         // Quickly press on "cancel" and then "add address."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java
index ea2454771..d356eaa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndFreeShippingTest.java
@@ -89,7 +89,7 @@
 
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
                 | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_EMAIL
-                | Event.REQUEST_SHIPPING;
+                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndPhoneTest.java
index 3a07cee..9d991b6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndPhoneTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailAndPhoneTest.java
@@ -161,7 +161,8 @@
 
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
                 | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_EMAIL
-                | Event.REQUEST_PAYER_PHONE;
+                | Event.REQUEST_PAYER_PHONE | Event.REQUEST_METHOD_BASIC_CARD
+                | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailTest.java
index 275e152..4fc1a0b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmailTest.java
@@ -159,7 +159,8 @@
 
         int expectedSample = Event.SHOWN | Event.COMPLETED | Event.PAY_CLICKED
                 | Event.HAD_INITIAL_FORM_OF_PAYMENT | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS
-                | Event.RECEIVED_INSTRUMENT_DETAILS | Event.REQUEST_PAYER_EMAIL;
+                | Event.RECEIVED_INSTRUMENT_DETAILS | Event.REQUEST_PAYER_EMAIL
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
index 0973925..2677700 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestFreeShippingTest.java
@@ -169,18 +169,15 @@
 
         // Quickly press on "add address" and then [X].
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -203,18 +200,15 @@
 
         // Quickly press on [X] and then "add address."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -234,18 +228,15 @@
 
         // Quickly press on "add address" and then "cancel."
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -268,18 +259,15 @@
 
         // Quickly press on "cancel" and then "add address."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getShippingAddressSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getShippingAddressSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -302,7 +290,8 @@
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
index 774b983..d901f34 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java
@@ -578,7 +578,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -611,7 +612,7 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.REQUEST_SHIPPING;
+                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -644,7 +645,7 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.REQUEST_SHIPPING;
+                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -676,7 +677,8 @@
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         // Make sure the events were logged correctly.
-        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING;
+        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -706,7 +708,8 @@
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         // Make sure the events were logged correctly.
-        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING;
+        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -736,7 +739,8 @@
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         // Make sure the events were logged correctly.
-        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING;
+        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -767,7 +771,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -800,7 +805,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.REQUEST_SHIPPING;
+                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD
+                | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -834,7 +840,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -866,7 +873,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -901,7 +909,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -930,7 +939,8 @@
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         // Make sure the events were logged correctly.
-        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING;
+        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -1030,10 +1040,20 @@
         Assert.assertEquals(2,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.NumberOfSelectionEdits.ShippingAddress.Completed", 0));
+
+        // Make sure the events were logged correctly.
+        int expectedSample = Event.SHOWN | Event.COMPLETED | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.HAD_INITIAL_FORM_OF_PAYMENT
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.RECEIVED_INSTRUMENT_DETAILS
+                | Event.PAY_CLICKED;
+        Assert.assertEquals(2,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.Events", expectedSample));
     }
 
     /**
-     * Expect that no journey metrics are logged if the payment request was not shown to the user.
+     * Expect that only some journey metrics are logged if the payment request was not shown to the
+     * user.
      */
     @Test
     @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
index 1021786e..ccfea79 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
@@ -90,7 +90,8 @@
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -112,15 +113,11 @@
 
         // Cancel the Payment Request.
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mPaymentRequestTestRule.getPaymentRequestUI()
                         .getDialogForTest()
                         .findViewById(R.id.button_secondary)
-                        .performClick();
-            }
-        });
+                        .performClick());
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
@@ -128,7 +125,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -157,7 +155,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -179,12 +178,9 @@
 
         // Press the back button.
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest()
+                        .onBackPressed());
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
@@ -192,7 +188,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -221,7 +218,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.OTHER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -248,7 +246,8 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.OTHER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -382,7 +381,8 @@
         // Make sure the events were logged correctly.
         int expectedSample = Event.SKIPPED_SHOW | Event.PAY_CLICKED
                 | Event.RECEIVED_INSTRUMENT_DETAILS | Event.COMPLETED
-                | Event.HAD_INITIAL_FORM_OF_PAYMENT | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS;
+                | Event.HAD_INITIAL_FORM_OF_PAYMENT | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS
+                | Event.REQUEST_METHOD_GOOGLE;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -412,7 +412,7 @@
 
         // Make sure the events were logged correctly.
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_METHOD_GOOGLE;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
@@ -447,9 +447,12 @@
                 R.id.close_button, mPaymentRequestTestRule.getDismissed());
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
-        // Make sure only one set of events was logged.
-        Assert.assertEquals(
-                1, RecordHistogram.getHistogramTotalCountForTesting("PaymentRequest.Events"));
+        // Make sure the events were logged correctly.
+        int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_METHOD_BASIC_CARD;
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        "PaymentRequest.Events", expectedSample));
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java
index 7bd2ab2..bfe8611 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameAndFreeShippingTest.java
@@ -89,7 +89,7 @@
 
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
                 | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_SHIPPING
-                | Event.REQUEST_PAYER_NAME;
+                | Event.REQUEST_PAYER_NAME | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java
index 469b9ed..ed0c70f0a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNameTest.java
@@ -171,7 +171,8 @@
 
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_NAME;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_NAME
+                | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java
index 6c55b0db..4c5e7463bd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java
@@ -283,18 +283,15 @@
 
         // Quickly press on "add card" and then [X].
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getPaymentMethodSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getPaymentMethodSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -317,18 +314,15 @@
 
         // Quickly press on [X] and then "add card."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getPaymentMethodSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getPaymentMethodSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -347,18 +341,15 @@
 
         // Quickly press on "add card" and then "cancel."
         int callCount = mPaymentRequestTestRule.getReadyToEdit().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getPaymentMethodSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getPaymentMethodSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
         });
         mPaymentRequestTestRule.getReadyToEdit().waitForCallback(callCount);
 
@@ -381,18 +372,15 @@
 
         // Quickly press on "cancel" and then "add card."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_secondary)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getPaymentMethodSectionForTest()
-                        .findViewById(R.id.payments_add_option_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_secondary)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getPaymentMethodSectionForTest()
+                    .findViewById(R.id.payments_add_option_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -412,15 +400,12 @@
 
         // Quickly dismiss and then press on "pay."
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.button_primary)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.button_primary)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -440,15 +425,12 @@
 
         // Quickly dismiss and then press on [X].
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -468,15 +450,12 @@
 
         // Quickly press on [X] and then dismiss.
         int callCount = mPaymentRequestTestRule.getDismissed().getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getPaymentRequestUI()
-                        .getDialogForTest()
-                        .findViewById(R.id.close_button)
-                        .performClick();
-                mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mPaymentRequestTestRule.getPaymentRequestUI()
+                    .getDialogForTest()
+                    .findViewById(R.id.close_button)
+                    .performClick();
+            mPaymentRequestTestRule.getPaymentRequestUI().getDialogForTest().onBackPressed();
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(callCount);
 
@@ -500,7 +479,7 @@
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppTest.java
index 556da561..afa071a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppTest.java
@@ -21,14 +21,10 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedCallback;
-import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition;
 import org.chromium.chrome.browser.payments.PaymentRequestTestCommon.TestPay;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.content_public.browser.WebContents;
 
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -96,23 +92,15 @@
     public void testPaymentWithInstrumentsAppResponseAfterDismissShouldNotCrash()
             throws InterruptedException, ExecutionException, TimeoutException {
         final TestPay app = new TestPay("https://bobpay.com", HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
-        PaymentAppFactory.getInstance().addAdditionalFactory(new PaymentAppFactoryAddition() {
-            @Override
-            public void create(WebContents webContents, Set<String> methodNames,
-                    PaymentAppFactory.PaymentAppCreatedCallback callback) {
-                callback.onPaymentAppCreated(app);
-                callback.onAllPaymentAppsCreated();
-            }
-        });
+        PaymentAppFactory.getInstance().addAdditionalFactory(
+                (webContents, methodNames, callback) -> {
+                    callback.onPaymentAppCreated(app);
+                    callback.onAllPaymentAppsCreated();
+                });
         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
         mPaymentRequestTestRule.clickAndWait(
                 R.id.close_button, mPaymentRequestTestRule.getDismissed());
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                app.respond();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> app.respond());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"show() rejected", "Request cancelled"});
     }
@@ -126,21 +114,13 @@
     public void testPaymentAppNoInstrumentsResponseAfterDismissShouldNotCrash()
             throws InterruptedException, ExecutionException, TimeoutException {
         final TestPay app = new TestPay("https://bobpay.com", NO_INSTRUMENTS, IMMEDIATE_RESPONSE);
-        PaymentAppFactory.getInstance().addAdditionalFactory(new PaymentAppFactoryAddition() {
-            @Override
-            public void create(WebContents webContents, Set<String> methodNames,
-                    PaymentAppCreatedCallback callback) {
-                callback.onPaymentAppCreated(app);
-                callback.onAllPaymentAppsCreated();
-            }
-        });
+        PaymentAppFactory.getInstance().addAdditionalFactory(
+                (webContents, methodNames, callback) -> {
+                    callback.onPaymentAppCreated(app);
+                    callback.onAllPaymentAppsCreated();
+                });
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(mPaymentRequestTestRule.getShowFailed());
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                app.respond();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> app.respond());
         mPaymentRequestTestRule.expectResultContains(
                 new String[] {"show() rejected", "The payment method is not supported"});
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppsSortingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppsSortingTest.java
index 54ea441..61e2038 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppsSortingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppsSortingTest.java
@@ -22,15 +22,11 @@
 import org.chromium.chrome.browser.autofill.CardType;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppCreatedCallback;
-import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition;
 import org.chromium.chrome.browser.payments.PaymentRequestTestCommon.TestPay;
 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.content_public.browser.WebContents;
 
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -72,16 +68,13 @@
                 new TestPay("https://bobpay.com", HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
         final TestPay appC =
                 new TestPay("https://charliepay.com", HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
-        PaymentAppFactory.getInstance().addAdditionalFactory(new PaymentAppFactoryAddition() {
-            @Override
-            public void create(WebContents webContents, Set<String> methodNames,
-                    PaymentAppCreatedCallback callback) {
-                callback.onPaymentAppCreated(appA);
-                callback.onPaymentAppCreated(appB);
-                callback.onPaymentAppCreated(appC);
-                callback.onAllPaymentAppsCreated();
-            }
-        });
+        PaymentAppFactory.getInstance().addAdditionalFactory(
+                (webContents, methodNames, callback) -> {
+                    callback.onPaymentAppCreated(appA);
+                    callback.onPaymentAppCreated(appB);
+                    callback.onPaymentAppCreated(appC);
+                    callback.onAllPaymentAppsCreated();
+                });
         String alicePayId = appA.getAppIdentifier() + "https://alicepay.com";
         String bobPayId = appB.getAppIdentifier() + "https://bobpay.com";
         String charliePayId = appC.getAppIdentifier() + "https://charliepay.com";
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java
index 1fe1867..de300066 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java
@@ -96,7 +96,7 @@
         int expectedSample = Event.SHOWN | Event.PAY_CLICKED | Event.RECEIVED_INSTRUMENT_DETAILS
                 | Event.COMPLETED | Event.HAD_INITIAL_FORM_OF_PAYMENT
                 | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_PHONE
-                | Event.REQUEST_SHIPPING;
+                | Event.REQUEST_SHIPPING | Event.REQUEST_METHOD_BASIC_CARD;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneTest.java
index a49b0384..cee5599 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneTest.java
@@ -158,7 +158,8 @@
         mPaymentRequestTestRule.expectResultContains(new String[] {"Request cancelled"});
 
         int expectedSample = Event.SHOWN | Event.USER_ABORTED | Event.HAD_INITIAL_FORM_OF_PAYMENT
-                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_PHONE;
+                | Event.HAD_NECESSARY_COMPLETE_SUGGESTIONS | Event.REQUEST_PAYER_PHONE
+                | Event.REQUEST_METHOD_BASIC_CARD | Event.REQUEST_METHOD_OTHER;
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "PaymentRequest.Events", expectedSample));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
index 97b9a03..59addaa9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
@@ -15,9 +15,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.content_public.browser.WebContents;
 
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -41,19 +39,15 @@
      */
     private void installMockServiceWorkerPaymentApp(final boolean hasSupportedMethods) {
         PaymentAppFactory.getInstance().addAdditionalFactory(
-                new PaymentAppFactory.PaymentAppFactoryAddition() {
-                    @Override
-                    public void create(WebContents webContents, Set<String> methodNames,
-                            PaymentAppFactory.PaymentAppCreatedCallback callback) {
-                        String[] supportedMethodNames = {"https://bobpay.com", "basic-card"};
-                        callback.onPaymentAppCreated(new ServiceWorkerPaymentApp(webContents,
-                                0 /* registrationId */, "BobPay" /* label */,
-                                "https://bobpay.com" /* sublabel */, null /* icon */,
-                                hasSupportedMethods ? supportedMethodNames
-                                                    : new String[0] /* methodNames */,
-                                new String[0] /* preferredRelatedApplicationIds */));
-                        callback.onAllPaymentAppsCreated();
-                    }
+                (webContents, methodNames, callback) -> {
+                    String[] supportedMethodNames = {"https://bobpay.com", "basic-card"};
+                    callback.onPaymentAppCreated(new ServiceWorkerPaymentApp(webContents,
+                            0 /* registrationId */, "BobPay" /* label */,
+                            "https://bobpay.com" /* sublabel */, null /* icon */,
+                            hasSupportedMethods ? supportedMethodNames
+                                    : new String[0] /* methodNames */,
+                            new String[0] /* preferredRelatedApplicationIds */));
+                    callback.onAllPaymentAppsCreated();
                 });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTabTest.java
index b51c625e..3dae6f24 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTabTest.java
@@ -66,13 +66,10 @@
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
         Assert.assertEquals(0, mPaymentRequestTestRule.getDismissed().getCallCount());
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mPaymentRequestTestRule.getActivity().getTabCreator(false).createNewTab(
-                        new LoadUrlParams("about:blank"), TabLaunchType.FROM_CHROME_UI, null);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mPaymentRequestTestRule.getActivity().getTabCreator(
+                        false).createNewTab(
+                        new LoadUrlParams("about:blank"), TabLaunchType.FROM_CHROME_UI, null));
         mPaymentRequestTestRule.getDismissed().waitForCallback(0);
     }
 
@@ -86,12 +83,9 @@
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
         Assert.assertEquals(0, mPaymentRequestTestRule.getDismissed().getCallCount());
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                TabModel currentModel = mPaymentRequestTestRule.getActivity().getCurrentTabModel();
-                TabModelUtils.closeCurrentTab(currentModel);
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            TabModel currentModel = mPaymentRequestTestRule.getActivity().getCurrentTabModel();
+            TabModelUtils.closeCurrentTab(currentModel);
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(0);
     }
@@ -104,12 +98,9 @@
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
         Assert.assertEquals(0, mPaymentRequestTestRule.getDismissed().getCallCount());
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                TabModel currentModel = mPaymentRequestTestRule.getActivity().getCurrentTabModel();
-                TabModelUtils.getCurrentTab(currentModel).loadUrl(new LoadUrlParams("about:blank"));
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            TabModel currentModel = mPaymentRequestTestRule.getActivity().getCurrentTabModel();
+            TabModelUtils.getCurrentTab(currentModel).loadUrl(new LoadUrlParams("about:blank"));
         });
         mPaymentRequestTestRule.getDismissed().waitForCallback(0);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java
index 112218e..7e2d48a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestCommon.java
@@ -23,7 +23,6 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.autofill.CardUnmaskPrompt;
 import org.chromium.chrome.browser.autofill.CardUnmaskPrompt.CardUnmaskObserverForTest;
-import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition;
 import org.chromium.chrome.browser.payments.PaymentRequestImpl.PaymentRequestServiceObserverForTest;
 import org.chromium.chrome.browser.payments.ui.EditorTextField;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection;
@@ -141,15 +140,12 @@
 
     private void openPage() throws InterruptedException, ExecutionException, TimeoutException {
         mCallback.onMainActivityStarted();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mViewCoreRef.set(mCallback.getActivity().getCurrentContentViewCore());
-                mWebContentsRef.set(mViewCoreRef.get().getWebContents());
-                PaymentRequestUI.setObserverForTest(PaymentRequestTestCommon.this);
-                PaymentRequestImpl.setObserverForTest(PaymentRequestTestCommon.this);
-                CardUnmaskPrompt.setObserverForTest(PaymentRequestTestCommon.this);
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            mViewCoreRef.set(mCallback.getActivity().getCurrentContentViewCore());
+            mWebContentsRef.set(mViewCoreRef.get().getWebContents());
+            PaymentRequestUI.setObserverForTest(PaymentRequestTestCommon.this);
+            PaymentRequestImpl.setObserverForTest(PaymentRequestTestCommon.this);
+            CardUnmaskPrompt.setObserverForTest(PaymentRequestTestCommon.this);
         });
         mCallback.assertWaitForPageScaleFactorMatch(1);
     }
@@ -200,86 +196,62 @@
     protected void clickAndWait(final int resourceId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mUI.getDialogForTest().findViewById(resourceId).performClick();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mUI.getDialogForTest().findViewById(resourceId).performClick());
         helper.waitForCallback(callCount);
     }
 
     protected void clickInShippingAddressAndWait(final int resourceId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mUI.getShippingAddressSectionForTest().findViewById(resourceId).performClick();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mUI.getShippingAddressSectionForTest().findViewById(
+                        resourceId).performClick());
         helper.waitForCallback(callCount);
     }
 
     protected void clickInPaymentMethodAndWait(final int resourceId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mUI.getPaymentMethodSectionForTest().findViewById(resourceId).performClick();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mUI.getPaymentMethodSectionForTest().findViewById(
+                        resourceId).performClick());
         helper.waitForCallback(callCount);
     }
 
     protected void clickInContactInfoAndWait(final int resourceId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mUI.getContactDetailsSectionForTest().findViewById(resourceId).performClick();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mUI.getContactDetailsSectionForTest().findViewById(
+                        resourceId).performClick());
         helper.waitForCallback(callCount);
     }
 
     protected void clickInCardEditorAndWait(final int resourceId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mUI.getCardEditorDialog().findViewById(resourceId).performClick();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mUI.getCardEditorDialog().findViewById(resourceId).performClick());
         helper.waitForCallback(callCount);
     }
 
     protected void clickInEditorAndWait(final int resourceId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mUI.getEditorDialog().findViewById(resourceId).performClick();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mUI.getEditorDialog().findViewById(resourceId).performClick());
         helper.waitForCallback(callCount);
     }
 
     protected void clickAndroidBackButtonInEditorAndWait(CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mUI.getEditorDialog().dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
-                mUI.getEditorDialog().dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
-            }
+        ThreadUtils.runOnUiThread(() -> {
+            mUI.getEditorDialog().dispatchKeyEvent(
+                    new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
+            mUI.getEditorDialog().dispatchKeyEvent(
+                    new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
         });
         helper.waitForCallback(callCount);
     }
@@ -287,152 +259,98 @@
     protected void clickCardUnmaskButtonAndWait(final int dialogButtonId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mCardUnmaskPrompt.getDialogForTest().getButton(dialogButtonId).performClick();
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> mCardUnmaskPrompt.getDialogForTest().getButton(
+                        dialogButtonId).performClick());
         helper.waitForCallback(callCount);
     }
 
     protected int getShippingAddressSectionButtonState() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return mUI.getShippingAddressSectionForTest().getEditButtonState();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> mUI.getShippingAddressSectionForTest().getEditButtonState());
     }
 
     protected int getContactDetailsButtonState() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return mUI.getContactDetailsSectionForTest().getEditButtonState();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> mUI.getContactDetailsSectionForTest().getEditButtonState());
     }
 
     protected String getPaymentInstrumentLabel(final int index) throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return ((OptionSection) mUI.getPaymentMethodSectionForTest())
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> ((OptionSection) mUI.getPaymentMethodSectionForTest())
                         .getOptionLabelsForTest(index)
                         .getText()
-                        .toString();
-            }
-        });
+                        .toString());
     }
 
     protected String getSelectedPaymentInstrumentLabel() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                OptionSection section = ((OptionSection) mUI.getPaymentMethodSectionForTest());
-                int size = section.getNumberOfOptionLabelsForTest();
-                for (int i = 0; i < size; i++) {
-                    if (section.getOptionRowAtIndex(i).isChecked()) {
-                        return section.getOptionRowAtIndex(i).getLabelText().toString();
-                    }
+        return ThreadUtils.runOnUiThreadBlocking(() -> {
+            OptionSection section = ((OptionSection) mUI.getPaymentMethodSectionForTest());
+            int size = section.getNumberOfOptionLabelsForTest();
+            for (int i = 0; i < size; i++) {
+                if (section.getOptionRowAtIndex(i).isChecked()) {
+                    return section.getOptionRowAtIndex(i).getLabelText().toString();
                 }
-                return null;
             }
+            return null;
         });
     }
 
     protected String getOrderSummaryTotal() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return mUI.getOrderSummaryTotalTextViewForTest().getText().toString();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> mUI.getOrderSummaryTotalTextViewForTest().getText().toString());
     }
 
     protected String getContactDetailsSuggestionLabel(final int suggestionIndex)
             throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return ((OptionSection) mUI.getContactDetailsSectionForTest())
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> ((OptionSection) mUI.getContactDetailsSectionForTest())
                         .getOptionLabelsForTest(suggestionIndex)
                         .getText()
-                        .toString();
-            }
-        });
+                        .toString());
     }
 
     protected int getNumberOfPaymentInstruments() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return ((OptionSection) mUI.getPaymentMethodSectionForTest())
-                        .getNumberOfOptionLabelsForTest();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> ((OptionSection) mUI.getPaymentMethodSectionForTest())
+                        .getNumberOfOptionLabelsForTest());
     }
 
     protected int getNumberOfContactDetailSuggestions() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return ((OptionSection) mUI.getContactDetailsSectionForTest())
-                        .getNumberOfOptionLabelsForTest();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> ((OptionSection) mUI.getContactDetailsSectionForTest())
+                        .getNumberOfOptionLabelsForTest());
     }
 
     protected String getShippingAddressSuggestionLabel(final int suggestionIndex)
             throws ExecutionException {
         Assert.assertTrue(suggestionIndex < getNumberOfShippingAddressSuggestions());
 
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return mUI.getShippingAddressSectionForTest()
-                        .getOptionLabelsForTest(suggestionIndex)
-                        .getText()
-                        .toString();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(() -> mUI.getShippingAddressSectionForTest()
+                .getOptionLabelsForTest(suggestionIndex)
+                .getText()
+                .toString());
     }
 
     protected String getShippingAddressSummary() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return mUI.getShippingAddressSectionForTest()
-                        .getLeftSummaryLabelForTest()
-                        .getText()
-                        .toString();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(() -> mUI.getShippingAddressSectionForTest()
+                .getLeftSummaryLabelForTest()
+                .getText()
+                .toString());
     }
 
     protected String getShippingOptionSummary() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return mUI.getShippingOptionSectionForTest()
-                        .getLeftSummaryLabelForTest()
-                        .getText()
-                        .toString();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(() -> mUI.getShippingOptionSectionForTest()
+                .getLeftSummaryLabelForTest()
+                .getText()
+                .toString());
     }
 
     protected String getShippingOptionCostSummaryOnBottomSheet() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return mUI.getShippingOptionSectionForTest()
-                        .getRightSummaryLabelForTest()
-                        .getText()
-                        .toString();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(() -> mUI.getShippingOptionSectionForTest()
+                .getRightSummaryLabelForTest()
+                .getText()
+                .toString());
     }
 
     protected View getCardEditorFocusedView() {
@@ -445,14 +363,10 @@
         Assert.assertTrue(suggestionIndex < getNumberOfShippingAddressSuggestions());
 
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((OptionSection) mUI.getShippingAddressSectionForTest())
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> ((OptionSection) mUI.getShippingAddressSectionForTest())
                         .getOptionLabelsForTest(suggestionIndex)
-                        .performClick();
-            }
-        });
+                        .performClick());
         helper.waitForCallback(callCount);
     }
 
@@ -462,14 +376,10 @@
         Assert.assertTrue(suggestionIndex < getNumberOfPaymentInstruments());
 
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((OptionSection) mUI.getPaymentMethodSectionForTest())
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> ((OptionSection) mUI.getPaymentMethodSectionForTest())
                         .getOptionLabelsForTest(suggestionIndex)
-                        .performClick();
-            }
-        });
+                        .performClick());
         helper.waitForCallback(callCount);
     }
 
@@ -479,14 +389,10 @@
         Assert.assertTrue(suggestionIndex < getNumberOfContactDetailSuggestions());
 
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((OptionSection) mUI.getContactDetailsSectionForTest())
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> ((OptionSection) mUI.getContactDetailsSectionForTest())
                         .getOptionLabelsForTest(suggestionIndex)
-                        .performClick();
-            }
-        });
+                        .performClick());
         helper.waitForCallback(callCount);
     }
 
@@ -496,78 +402,51 @@
         Assert.assertTrue(suggestionIndex < getNumberOfPaymentInstruments());
 
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((OptionSection) mUI.getPaymentMethodSectionForTest())
+        ThreadUtils.runOnUiThreadBlocking(
+                (Runnable) () -> ((OptionSection) mUI.getPaymentMethodSectionForTest())
                         .getOptionRowAtIndex(suggestionIndex)
                         .getEditIconForTest()
-                        .performClick();
-            }
-        });
+                        .performClick());
         helper.waitForCallback(callCount);
     }
 
     protected int getNumberOfShippingAddressSuggestions() throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return ((OptionSection) mUI.getShippingAddressSectionForTest())
-                        .getNumberOfOptionLabelsForTest();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> ((OptionSection) mUI.getShippingAddressSectionForTest())
+                        .getNumberOfOptionLabelsForTest());
     }
 
     protected OptionRow getShippingAddressOptionRowAtIndex(final int index)
             throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<OptionRow>() {
-            @Override
-            public OptionRow call() {
-                return ((OptionSection) mUI.getShippingAddressSectionForTest())
-                        .getOptionRowAtIndex(index);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> ((OptionSection) mUI.getShippingAddressSectionForTest())
+                        .getOptionRowAtIndex(index));
     }
 
     protected String getSpinnerSelectionTextInCardEditor(final int dropdownIndex)
             throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return mUI.getCardEditorDialog()
-                        .getDropdownFieldsForTest()
-                        .get(dropdownIndex)
-                        .getSelectedItem()
-                        .toString();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(() -> mUI.getCardEditorDialog()
+                .getDropdownFieldsForTest()
+                .get(dropdownIndex)
+                .getSelectedItem()
+                .toString());
     }
 
     protected String getSpinnerTextAtPositionInCardEditor(
             final int dropdownIndex, final int itemPosition) throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
-            @Override
-            public String call() {
-                return mUI.getCardEditorDialog()
-                        .getDropdownFieldsForTest()
-                        .get(dropdownIndex)
-                        .getItemAtPosition(itemPosition)
-                        .toString();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(() -> mUI.getCardEditorDialog()
+                .getDropdownFieldsForTest()
+                .get(dropdownIndex)
+                .getItemAtPosition(itemPosition)
+                .toString());
     }
 
     protected int getSpinnerItemCountInCardEditor(final int dropdownIndex)
             throws ExecutionException {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return mUI.getCardEditorDialog()
-                        .getDropdownFieldsForTest()
-                        .get(dropdownIndex)
-                        .getCount();
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(() -> mUI.getCardEditorDialog()
+                .getDropdownFieldsForTest()
+                .get(dropdownIndex)
+                .getCount());
     }
 
     protected String getUnmaskPromptErrorMessage() {
@@ -577,13 +456,10 @@
     protected void setSpinnerSelectionsInCardEditorAndWait(final int[] selections,
             CallbackHelper helper) throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                List<Spinner> fields = mUI.getCardEditorDialog().getDropdownFieldsForTest();
-                for (int i = 0; i < selections.length && i < fields.size(); i++) {
-                    fields.get(i).setSelection(selections[i]);
-                }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            List<Spinner> fields = mUI.getCardEditorDialog().getDropdownFieldsForTest();
+            for (int i = 0; i < selections.length && i < fields.size(); i++) {
+                fields.get(i).setSelection(selections[i]);
             }
         });
         helper.waitForCallback(callCount);
@@ -592,30 +468,23 @@
     protected void setSpinnerSelectionInEditorAndWait(final int selection, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((Spinner) mUI.getEditorDialog().findViewById(R.id.spinner))
-                        .setSelection(selection);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> ((Spinner) mUI.getEditorDialog().findViewById(
+                R.id.spinner))
+                .setSelection(selection));
         helper.waitForCallback(callCount);
     }
 
     protected void setTextInCardEditorAndWait(final String[] values, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ViewGroup contents =
-                        (ViewGroup) mUI.getCardEditorDialog().findViewById(R.id.contents);
-                Assert.assertNotNull(contents);
-                for (int i = 0, j = 0; i < contents.getChildCount() && j < values.length; i++) {
-                    View view = contents.getChildAt(i);
-                    if (view instanceof EditorTextField) {
-                        ((EditorTextField) view).getEditText().setText(values[j++]);
-                    }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            ViewGroup contents =
+                    (ViewGroup) mUI.getCardEditorDialog().findViewById(R.id.contents);
+            Assert.assertNotNull(contents);
+            for (int i = 0, j = 0; i < contents.getChildCount() && j < values.length; i++) {
+                View view = contents.getChildAt(i);
+                if (view instanceof EditorTextField) {
+                    ((EditorTextField) view).getEditText().setText(values[j++]);
                 }
             }
         });
@@ -625,13 +494,10 @@
     protected void setTextInEditorAndWait(final String[] values, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                List<EditText> fields = mUI.getEditorDialog().getEditableTextFieldsForTest();
-                for (int i = 0; i < values.length; i++) {
-                    fields.get(i).setText(values[i]);
-                }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            List<EditText> fields = mUI.getEditorDialog().getEditableTextFieldsForTest();
+            for (int i = 0; i < values.length; i++) {
+                fields.get(i).setText(values[i]);
             }
         });
         helper.waitForCallback(callCount);
@@ -640,27 +506,20 @@
     protected void selectCheckboxAndWait(final int resourceId, final boolean isChecked,
             CallbackHelper helper) throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ((CheckBox) mUI.getCardEditorDialog().findViewById(resourceId))
-                        .setChecked(isChecked);
-            }
-        });
+        ThreadUtils.runOnUiThreadBlocking(() -> ((CheckBox) mUI.getCardEditorDialog().findViewById(
+                resourceId))
+                .setChecked(isChecked));
         helper.waitForCallback(callCount);
     }
 
     protected void setTextInCardUnmaskDialogAndWait(final int resourceId, final String input,
             CallbackHelper helper) throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                EditText editText =
-                        ((EditText) mCardUnmaskPrompt.getDialogForTest().findViewById(resourceId));
-                editText.setText(input);
-                editText.getOnFocusChangeListener().onFocusChange(null, false);
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            EditText editText =
+                    ((EditText) mCardUnmaskPrompt.getDialogForTest().findViewById(resourceId));
+            editText.setText(input);
+            editText.getOnFocusChangeListener().onFocusChange(null, false);
         });
         helper.waitForCallback(callCount);
     }
@@ -670,16 +529,13 @@
             throws InterruptedException, TimeoutException {
         assert resourceIds.length == values.length;
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                for (int i = 0; i < resourceIds.length; ++i) {
-                    EditText editText =
-                            ((EditText) mCardUnmaskPrompt.getDialogForTest().findViewById(
-                                    resourceIds[i]));
-                    editText.setText(values[i]);
-                    editText.getOnFocusChangeListener().onFocusChange(null, false);
-                }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            for (int i = 0; i < resourceIds.length; ++i) {
+                EditText editText =
+                        ((EditText) mCardUnmaskPrompt.getDialogForTest().findViewById(
+                                resourceIds[i]));
+                editText.setText(values[i]);
+                editText.getOnFocusChangeListener().onFocusChange(null, false);
             }
         });
         helper.waitForCallback(callCount);
@@ -688,14 +544,11 @@
     /* package */ void hitSoftwareKeyboardSubmitButtonAndWait(final int resourceId,
             CallbackHelper helper) throws InterruptedException, TimeoutException {
         int callCount = helper.getCallCount();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                EditText editText =
-                        (EditText) mCardUnmaskPrompt.getDialogForTest().findViewById(resourceId);
-                editText.requestFocus();
-                editText.onEditorAction(EditorInfo.IME_ACTION_DONE);
-            }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            EditText editText =
+                    (EditText) mCardUnmaskPrompt.getDialogForTest().findViewById(resourceId);
+            editText.requestFocus();
+            editText.onEditorAction(EditorInfo.IME_ACTION_DONE);
         });
         helper.waitForCallback(callCount);
     }
@@ -794,13 +647,9 @@
     }
 
     /* package */ View getCardUnmaskView() throws Throwable {
-        return ThreadUtils.runOnUiThreadBlocking(new Callable<View>() {
-            @Override
-            public View call() {
-                return mCardUnmaskPrompt.getDialogForTest().findViewById(
-                        R.id.autofill_card_unmask_prompt);
-            }
-        });
+        return ThreadUtils.runOnUiThreadBlocking(
+                () -> mCardUnmaskPrompt.getDialogForTest().findViewById(
+                        R.id.autofill_card_unmask_prompt));
     }
 
     @Override
@@ -937,25 +786,20 @@
 
     void installPaymentApp(final String appMethodName, final int instrumentPresence,
             final int responseSpeed, final int creationSpeed) {
-        PaymentAppFactory.getInstance().addAdditionalFactory(new PaymentAppFactoryAddition() {
-            @Override
-            public void create(WebContents webContents, Set<String> methodNames,
-                    final PaymentAppFactory.PaymentAppCreatedCallback callback) {
-                final TestPay app = new TestPay(appMethodName, instrumentPresence, responseSpeed);
-                if (creationSpeed == IMMEDIATE_CREATION) {
-                    callback.onPaymentAppCreated(app);
-                    callback.onAllPaymentAppsCreated();
-                } else {
-                    new Handler().postDelayed(new Runnable() {
-                        @Override
-                        public void run() {
+        PaymentAppFactory.getInstance().addAdditionalFactory(
+                (webContents, methodNames, callback) -> {
+                    final TestPay app = new TestPay(appMethodName, instrumentPresence,
+                            responseSpeed);
+                    if (creationSpeed == IMMEDIATE_CREATION) {
+                        callback.onPaymentAppCreated(app);
+                        callback.onAllPaymentAppsCreated();
+                    } else {
+                        new Handler().postDelayed(() -> {
                             callback.onPaymentAppCreated(app);
                             callback.onAllPaymentAppsCreated();
-                        }
-                    }, 100);
-                }
-            }
-        });
+                        }, 100);
+                    }
+                });
     }
 
     /** A payment app implementation for test. */
@@ -985,12 +829,9 @@
                 instruments.add(new TestPayInstrument(
                         getAppIdentifier(), mDefaultMethodName, mDefaultMethodName));
             }
-            Runnable instrumentsReady = new Runnable() {
-                @Override
-                public void run() {
-                    ThreadUtils.assertOnUiThread();
-                    mCallback.onInstrumentsReady(TestPay.this, instruments);
-                }
+            Runnable instrumentsReady = () -> {
+                ThreadUtils.assertOnUiThread();
+                mCallback.onInstrumentsReady(TestPay.this, instruments);
             };
             if (mResponseSpeed == IMMEDIATE_RESPONSE) {
                 instrumentsReady.run();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java
index c9051fa3..ac306b7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java
@@ -59,6 +59,23 @@
     }
 
     /**
+     * Tests that WebVR is not exposed if the flag is not on and the page does
+     * not have an origin trial token.
+     */
+    @Test
+    @MediumTest
+    @CommandLineFlags.Remove({"enable-webvr"})
+    public void testWebVrDisabledWithoutFlagSet() throws InterruptedException {
+        // TODO(bsheedy): Remove this test once WebVR is on by default without
+        // requiring an origin trial.
+        mVrTestRule.loadUrlAndAwaitInitialization(
+                VrTestRule.getHtmlTestFile("test_webvr_disabled_without_flag_set"),
+                PAGE_LOAD_TIMEOUT_S);
+        mVrTestRule.waitOnJavaScriptStep(mVrTestRule.getFirstTabWebContents());
+        mVrTestRule.endTest(mVrTestRule.getFirstTabWebContents());
+    }
+
+    /**
      * Tests that scanning the Daydream View NFC tag on supported devices fires the
      * vrdisplayactivate event.
      */
diff --git a/chrome/app/media_router_strings.grdp b/chrome/app/media_router_strings.grdp
index 6f6b689..78db4368 100644
--- a/chrome/app/media_router_strings.grdp
+++ b/chrome/app/media_router_strings.grdp
@@ -169,6 +169,12 @@
   <message name="IDS_MEDIA_ROUTER_ROUTE_DETAILS_VOLUME_TITLE" desc="The title label for a volume control bar for media contents.">
     Volume
   </message>
+  <message name="IDS_MEDIA_ROUTER_ROUTE_DETAILS_CURRENT_TIME_LABEL" desc="The label for the current time position of media contents.">
+    Current time
+  </message>
+  <message name="IDS_MEDIA_ROUTER_ROUTE_DETAILS_DURATION_LABEL" desc="The label for the total duration of media contents.">
+    Duration
+  </message>
 
   <!-- Sink List -->
   <message name="IDS_MEDIA_ROUTER_DESTINATION_MISSING" desc="Link to display when no Cast destinations are found which, on click, opens a page to the Chromecast help center explaining possible reasons why none are detected.">
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 509d1d97..ed0a6ef 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -924,9 +924,6 @@
     <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_PRINTER" desc="In CUPS printing settings subpage, text for the link adding a new CUPS printer.">
       Add Printer
     </message>
-    <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTERS_DETAILS" desc="Text for the drop down menu which allows the user to see the printer details.">
-      Details
-    </message>
     <message name="IDS_SETTINGS_PRINTING_CUPS_PRINTERS_EDIT" desc="Text for the drop down menu which allows the user to edit the printer details.">
       Edit
     </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b1f3d01..f8f04b38 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3693,10 +3693,16 @@
 
   if ((is_linux && !is_chromeos) || is_win) {
     sources += [
+      "feature_engagement/feature_tracker.cc",
+      "feature_engagement/feature_tracker.h",
       "feature_engagement/new_tab/new_tab_tracker.cc",
       "feature_engagement/new_tab/new_tab_tracker.h",
       "feature_engagement/new_tab/new_tab_tracker_factory.cc",
       "feature_engagement/new_tab/new_tab_tracker_factory.h",
+      "feature_engagement/session_duration_updater.cc",
+      "feature_engagement/session_duration_updater.h",
+      "feature_engagement/session_duration_updater_factory.cc",
+      "feature_engagement/session_duration_updater_factory.h",
     ]
   }
 
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 1251b2d..2d8234d 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -151,7 +151,29 @@
   # are commonly referenced by renderer-side and browser-side code, and
   # does not use Blink types like WTF.
   "+third_party/WebKit/common",
+
+  # The following restrictions are for ChromeOS and in particular mus/mash where
+  # Ozone does not run in process. If the linux build transitions to Ozone then
+  # we likely need to make these restrictions applicable only to ChromeOS files.
+  "-ui/events/ozone",
+  "-ui/ozone",
+  "+ui/ozone/public/ozone_switches.h",
+  # ui/events/devices is tied with ozone, which is controlled by mus, and
+  # shouldn't be used by Chrome directly.
+  "-ui/events/devices",
+  # Enums and supporting classes or observers that are safe (should be moved to
+  # services/ui/public/cpp). http://crbug.com/747544.
+  "+ui/events/devices/device_hotplug_event_observer.h",
+  "+ui/events/devices/input_device.h",
+  "+ui/events/devices/input_device_event_observer.h",
+  "+ui/events/devices/stylus_state.h",
+  "+ui/events/devices/touchscreen_device.h",
+  "+ui/events/devices/input_device_manager.h",
+
+  # Only used by X11 (non-ChromeOS) code, which can use X directly.
+  "+ui/events/devices/x11",
 ]
+
 specific_include_rules = {
   # TODO(mash): Remove. http://crbug.com/678705
   "fullscreen_chromeos\.cc": [
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0d6c1c459..c30ae496 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2973,7 +2973,7 @@
      FEATURE_VALUE_TYPE(omnibox::kOmniboxTailSuggestions)},
     {"enable-new-app-menu-icon", flag_descriptions::kEnableNewAppMenuIconName,
      flag_descriptions::kEnableNewAppMenuIconDescription, kOsDesktop,
-     SINGLE_VALUE_TYPE(switches::kEnableNewAppMenuIcon)},
+     FEATURE_VALUE_TYPE(features::kAnimatedAppMenuIcon)},
 #endif  // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
 
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/android/instantapps/instant_apps_infobar_delegate.cc b/chrome/browser/android/instantapps/instant_apps_infobar_delegate.cc
index 4d3fc0c..459795c 100644
--- a/chrome/browser/android/instantapps/instant_apps_infobar_delegate.cc
+++ b/chrome/browser/android/instantapps/instant_apps_infobar_delegate.cc
@@ -78,6 +78,17 @@
   }
 }
 
+bool InstantAppsInfoBarDelegate::ShouldExpire(
+    const NavigationDetails& details) const {
+  content::WebContents* web_contents =
+      InfoBarService::WebContentsFromInfoBar(infobar());
+  bool navigation_url_is_launch_url =
+      web_contents != NULL &&
+      web_contents->GetURL().EqualsIgnoringRef(GURL(url_));
+  return !navigation_url_is_launch_url &&
+         ConfirmInfoBarDelegate::ShouldExpire(details);
+}
+
 void Launch(JNIEnv* env,
             const base::android::JavaParamRef<jclass>& clazz,
             const base::android::JavaParamRef<jobject>& jweb_contents,
diff --git a/chrome/browser/android/instantapps/instant_apps_infobar_delegate.h b/chrome/browser/android/instantapps/instant_apps_infobar_delegate.h
index c9933a9..cdf20ea8 100644
--- a/chrome/browser/android/instantapps/instant_apps_infobar_delegate.h
+++ b/chrome/browser/android/instantapps/instant_apps_infobar_delegate.h
@@ -24,6 +24,9 @@
 
   base::android::ScopedJavaGlobalRef<jobject> data() { return data_; }
 
+  // ConfirmInfoBarDelegate:
+  bool ShouldExpire(const NavigationDetails& details) const override;
+
   // WebContentsObserver:
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 3358114..e011d41 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -62,6 +62,7 @@
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/fake_speech_recognition_manager.h"
@@ -79,6 +80,7 @@
 #include "extensions/common/extensions_client.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "media/base/media_switches.h"
+#include "net/dns/mock_host_resolver.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"
@@ -4222,3 +4224,164 @@
   chrome::CloseTab(browser());
 }
 #endif
+
+// This test class makes "isolated.com" an isolated origin, to be used in
+// testing isolated origins inside of a WebView.
+class IsolatedOriginWebViewTest : public WebViewTest {
+ public:
+  IsolatedOriginWebViewTest() {}
+  ~IsolatedOriginWebViewTest() override {}
+
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+
+    std::string origin =
+        embedded_test_server()->GetURL("isolated.com", "/").spec();
+    command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin);
+    WebViewTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    embedded_test_server()->StartAcceptingConnections();
+    WebViewTest::SetUpOnMainThread();
+  }
+};
+INSTANTIATE_TEST_CASE_P(WebViewTests,
+                        IsolatedOriginWebViewTest,
+                        testing::Bool());
+
+// Test isolated origins inside a WebView, and make sure that loading an
+// isolated origin in a regular tab's subframe doesn't reuse a WebView process
+// that had loaded it previously, which would result in renderer kills. See
+// https://crbug.com/751916 and https://crbug.com/751920.
+IN_PROC_BROWSER_TEST_P(IsolatedOriginWebViewTest, IsolatedOriginInWebview) {
+  LoadAppWithGuest("web_view/simple");
+  content::WebContents* guest = GetGuestWebContents();
+
+  // Navigate <webview> to an isolated origin.
+  GURL isolated_url(
+      embedded_test_server()->GetURL("isolated.com", "/title1.html"));
+  {
+    content::TestNavigationObserver load_observer(guest);
+    EXPECT_TRUE(
+        ExecuteScript(guest, "location.href = '" + isolated_url.spec() + "';"));
+    load_observer.Wait();
+  }
+
+  // TODO(alexmos, creis): The isolated origin currently has to use the
+  // chrome-guest:// SiteInstance, rather than a SiteInstance with its own
+  // meaningful site URL.  This should be fixed as part of
+  // https://crbug.com/734722.
+  EXPECT_TRUE(guest->GetMainFrame()->GetSiteInstance()->GetSiteURL().SchemeIs(
+      content::kGuestScheme));
+
+  // Now, navigate <webview> to a regular page with a subframe.
+  GURL foo_url(embedded_test_server()->GetURL("foo.com", "/iframe.html"));
+  {
+    content::TestNavigationObserver load_observer(guest);
+    EXPECT_TRUE(
+        ExecuteScript(guest, "location.href = '" + foo_url.spec() + "';"));
+    load_observer.Wait();
+  }
+
+  // Navigate subframe in <webview> to an isolated origin.
+  EXPECT_TRUE(NavigateIframeToURL(guest, "test", isolated_url));
+
+  // TODO(alexmos, creis): Unfortunately, the subframe currently has to stay in
+  // the guest process.  The expectations here should change once WebViews
+  // can support OOPIFs.  See https://crbug.com/614463.
+  content::RenderFrameHost* webview_subframe =
+      ChildFrameAt(guest->GetMainFrame(), 0);
+  EXPECT_EQ(webview_subframe->GetProcess(),
+            guest->GetMainFrame()->GetProcess());
+  EXPECT_EQ(webview_subframe->GetSiteInstance(),
+            guest->GetMainFrame()->GetSiteInstance());
+
+  // Load a page with subframe in a regular tab.
+  AddTabAtIndex(0, foo_url, ui::PAGE_TRANSITION_TYPED);
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Navigate that subframe to an isolated origin.  This should not join the
+  // WebView process, which has isolated.foo.com committed in a different
+  // storage partition.
+  EXPECT_TRUE(NavigateIframeToURL(tab, "test", isolated_url));
+  content::RenderFrameHost* subframe = ChildFrameAt(tab->GetMainFrame(), 0);
+  EXPECT_NE(guest->GetMainFrame()->GetProcess(), subframe->GetProcess());
+
+  // Check that the guest process hasn't crashed.
+  EXPECT_TRUE(guest->GetMainFrame()->IsRenderFrameLive());
+
+  // Check that accessing a foo.com cookie from the WebView doesn't result in a
+  // renderer kill. This might happen if we erroneously applied an isolated.com
+  // origin lock to the WebView process when committing isolated.com.
+  bool cookie_is_correct = false;
+  EXPECT_TRUE(ExecuteScriptAndExtractBool(
+      guest,
+      "document.cookie = 'foo=bar';\n"
+      "window.domAutomationController.send(document.cookie == 'foo=bar');\n",
+      &cookie_is_correct));
+  EXPECT_TRUE(cookie_is_correct);
+}
+
+// This test is similar to IsolatedOriginInWebview above, but loads an isolated
+// origin in a <webview> subframe *after* loading the same isolated origin in a
+// regular tab's subframe.  The isolated origin's subframe in the <webview>
+// subframe should not reuse the regular tab's subframe process.  See
+// https://crbug.com/751916 and https://crbug.com/751920.
+IN_PROC_BROWSER_TEST_P(IsolatedOriginWebViewTest,
+                       LoadIsolatedOriginInWebviewAfterLoadingInRegularTab) {
+  LoadAppWithGuest("web_view/simple");
+  content::WebContents* guest = GetGuestWebContents();
+
+  // Load a page with subframe in a regular tab.
+  GURL foo_url(embedded_test_server()->GetURL("foo.com", "/iframe.html"));
+  AddTabAtIndex(0, foo_url, ui::PAGE_TRANSITION_TYPED);
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Navigate that subframe to an isolated origin.
+  GURL isolated_url(
+      embedded_test_server()->GetURL("isolated.com", "/title1.html"));
+  EXPECT_TRUE(NavigateIframeToURL(tab, "test", isolated_url));
+  content::RenderFrameHost* subframe = ChildFrameAt(tab->GetMainFrame(), 0);
+  EXPECT_NE(tab->GetMainFrame()->GetProcess(), subframe->GetProcess());
+
+  // Navigate <webview> to a regular page with an isolated origin subframe.
+  {
+    content::TestNavigationObserver load_observer(guest);
+    EXPECT_TRUE(
+        ExecuteScript(guest, "location.href = '" + foo_url.spec() + "';"));
+    load_observer.Wait();
+  }
+  EXPECT_TRUE(NavigateIframeToURL(guest, "test", isolated_url));
+
+  // TODO(alexmos, creis): The subframe currently has to stay in the guest
+  // process.  The expectations here should change once WebViews can support
+  // OOPIFs.  See https://crbug.com/614463.
+  content::RenderFrameHost* webview_subframe =
+      ChildFrameAt(guest->GetMainFrame(), 0);
+  EXPECT_EQ(webview_subframe->GetProcess(),
+            guest->GetMainFrame()->GetProcess());
+  EXPECT_EQ(webview_subframe->GetSiteInstance(),
+            guest->GetMainFrame()->GetSiteInstance());
+  EXPECT_NE(webview_subframe->GetProcess(), subframe->GetProcess());
+
+  // Check that the guest and regular tab processes haven't crashed.
+  EXPECT_TRUE(guest->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_TRUE(tab->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_TRUE(subframe->IsRenderFrameLive());
+
+  // Check that accessing a foo.com cookie from the WebView doesn't result in a
+  // renderer kill. This might happen if we erroneously applied an isolated.com
+  // origin lock to the WebView process when committing isolated.com.
+  bool cookie_is_correct = false;
+  EXPECT_TRUE(ExecuteScriptAndExtractBool(
+      guest,
+      "document.cookie = 'foo=bar';\n"
+      "window.domAutomationController.send(document.cookie == 'foo=bar');\n",
+      &cookie_is_correct));
+  EXPECT_TRUE(cookie_is_correct);
+}
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index 6ec3b8e..943de7f 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -47,7 +47,7 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/composition_underline.h"
+#include "ui/base/ime/ime_text_span.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/base/test/ui_controls.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -1724,7 +1724,7 @@
       target_web_contents->GetRenderWidgetHostView()->GetRenderWidgetHost();
   content::SendImeCommitTextToWidget(
       target_rwh_for_input, base::UTF8ToUTF16("C"),
-      std::vector<ui::CompositionUnderline>(), gfx::Range(4, 5), 0);
+      std::vector<ui::ImeTextSpan>(), gfx::Range(4, 5), 0);
   input_listener.WaitUntilSatisfied();
 
   // Get the input value from the guest.
@@ -1790,7 +1790,7 @@
   CompositionRangeUpdateObserver observer(embedder_web_contents);
   content::SendImeSetCompositionTextToWidget(
       target_web_contents->GetRenderWidgetHostView()->GetRenderWidgetHost(),
-      base::UTF8ToUTF16("ABC"), std::vector<ui::CompositionUnderline>(),
+      base::UTF8ToUTF16("ABC"), std::vector<ui::ImeTextSpan>(),
       gfx::Range::InvalidRange(), 0, 3);
   observer.WaitForCompositionRangeLength(3U);
 }
diff --git a/chrome/browser/chrome_service.cc b/chrome/browser/chrome_service.cc
index 372235e..e90a954 100644
--- a/chrome/browser/chrome_service.cc
+++ b/chrome/browser/chrome_service.cc
@@ -12,10 +12,10 @@
 }
 
 ChromeService::ChromeService() {
+#if defined(OS_CHROMEOS)
 #if defined(USE_OZONE)
   input_device_controller_.AddInterface(&registry_);
 #endif
-#if defined(OS_CHROMEOS)
   registry_.AddInterface(
       base::Bind(&chromeos::Launchable::Bind, base::Unretained(&launchable_)));
 #endif
diff --git a/chrome/browser/chrome_service.h b/chrome/browser/chrome_service.h
index ffc1f9f..2c7e8eb 100644
--- a/chrome/browser/chrome_service.h
+++ b/chrome/browser/chrome_service.h
@@ -10,10 +10,10 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/launchable.h"
-#endif
 #if defined(USE_OZONE)
 #include "services/ui/public/cpp/input_devices/input_device_controller.h"
 #endif
+#endif
 
 class ChromeService : public service_manager::Service {
  public:
@@ -32,10 +32,10 @@
 
 #if defined(OS_CHROMEOS)
   chromeos::Launchable launchable_;
-#endif
 #if defined(USE_OZONE)
   ui::InputDeviceController input_device_controller_;
 #endif
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(ChromeService);
 };
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index b31a4a0..dcd48c4e 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -782,6 +782,8 @@
     "lock_screen_apps/app_manager.h",
     "lock_screen_apps/app_manager_impl.cc",
     "lock_screen_apps/app_manager_impl.h",
+    "lock_screen_apps/app_window_metrics_tracker.cc",
+    "lock_screen_apps/app_window_metrics_tracker.h",
     "lock_screen_apps/focus_cycler_delegate.h",
     "lock_screen_apps/state_controller.cc",
     "lock_screen_apps/state_controller.h",
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
index a2a2c17..ebeb068 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
@@ -283,7 +283,16 @@
 
 void ArcVoiceInteractionFrameworkService::SetVoiceInteractionRunning(
     bool running) {
-  ash::Shell::Get()->NotifyVoiceInteractionStatusChanged(running);
+  ash::Shell::Get()->NotifyVoiceInteractionStatusChanged(
+      running ? ash::VoiceInteractionState::RUNNING
+              : ash::VoiceInteractionState::STOPPED);
+}
+
+void ArcVoiceInteractionFrameworkService::SetVoiceInteractionState(
+    ash::VoiceInteractionState state) {
+  DCHECK_NE(state_, state);
+  state_ = state;
+  ash::Shell::Get()->NotifyVoiceInteractionStatusChanged(state);
 }
 
 void ArcVoiceInteractionFrameworkService::OnMetalayerClosed() {
@@ -414,6 +423,12 @@
     return;
   }
 
+  if (state_ == ash::VoiceInteractionState::NOT_READY) {
+    // If the container side is not ready, we will be waiting for a while.
+    ash::Shell::Get()->NotifyVoiceInteractionStatusChanged(
+        ash::VoiceInteractionState::NOT_READY);
+  }
+
   if (!arc_bridge_service_->voice_interaction_framework()->has_instance()) {
     VLOG(1) << "Instance not ready.";
     SetArcCpuRestriction(false);
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
index 7335e00a..507e264 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
@@ -62,6 +62,7 @@
   void OnMetalayerClosed() override;
   void SetMetalayerEnabled(bool enabled) override;
   void SetVoiceInteractionRunning(bool running) override;
+  void SetVoiceInteractionState(ash::VoiceInteractionState state) override;
 
   bool IsMetalayerSupported();
   void ShowMetalayer(const base::Closure& closed);
@@ -117,6 +118,12 @@
   // Whether there is a pending request to start voice interaction.
   bool is_request_pending_ = false;
 
+  // The current state voice interaction service is. There is usually a long
+  // delay after boot before the service is ready. We wait for the container
+  // to tell us if it is ready to quickly serve voice interaction requests.
+  // We also give user proper feedback based on the state.
+  ash::VoiceInteractionState state_ = ash::VoiceInteractionState::NOT_READY;
+
   // The time when a user initated an interaction.
   base::TimeTicks user_interaction_start_time_;
 
diff --git a/chrome/browser/chromeos/feedback_util.cc b/chrome/browser/chromeos/feedback_util.cc
index c629cd6..4c8eb3b 100644
--- a/chrome/browser/chromeos/feedback_util.cc
+++ b/chrome/browser/chromeos/feedback_util.cc
@@ -6,10 +6,11 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/logging.h"
 #include "chrome/browser/extensions/api/feedback_private/feedback_private_api.h"
 #include "chrome/browser/extensions/api/feedback_private/feedback_service.h"
+#include "chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 
 using feedback::FeedbackData;
 
@@ -34,7 +35,7 @@
   feedback_data->set_description(description);
   feedback_data->SetAndCompressSystemInfo(std::move(sys_info));
 
-  GetFeedbackService(profile)->SendFeedback(profile, feedback_data, callback);
+  GetFeedbackService(profile)->SendFeedback(feedback_data, callback);
 }
 
 }  // namespace
@@ -42,7 +43,10 @@
 void SendSysLogFeedback(Profile* profile,
                         const std::string& description,
                         const SendSysLogFeedbackCallback& callback) {
-  GetFeedbackService(profile)->GetSystemInformation(
+  // |fetcher| deletes itself after calling its callback.
+  system_logs::SystemLogsFetcher* fetcher =
+      system_logs::BuildChromeSystemLogsFetcher();
+  fetcher->Fetch(
       base::Bind(&OnGetSystemInformation, profile, description, callback));
 }
 
diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
index 150f3500..928d1c5 100644
--- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
@@ -467,9 +467,12 @@
   // DriveIntegrationService factory function for this test.
   drive::DriveIntegrationService* CreateDriveIntegrationService(
       Profile* profile) {
-    // Ignore signin profile.
-    if (profile->GetPath() == chromeos::ProfileHelper::GetSigninProfileDir())
+    // Ignore signin and lock screen apps profile.
+    if (profile->GetPath() == chromeos::ProfileHelper::GetSigninProfileDir() ||
+        profile->GetPath() ==
+            chromeos::ProfileHelper::GetLockScreenAppProfilePath()) {
       return nullptr;
+    }
 
     // DriveFileSystemExtensionApiTest doesn't expect that several user profiles
     // could exist simultaneously.
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index 9b211159..c288005 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -14,6 +14,8 @@
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
@@ -72,6 +74,10 @@
     return;
   }
 
+  if (policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+          ->IsManaged())
+    return;
+
   if (command_line->HasSwitch(::switches::kTestType))
     return;
 
diff --git a/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc b/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
index e19be6e..1915915b 100644
--- a/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
+++ b/chrome/browser/chromeos/first_run/goodies_displayer_browsertest.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/chromeos/first_run/goodies_displayer.h"
+
 #include "base/command_line.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/first_run/goodies_displayer.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -84,6 +85,13 @@
   // InProcessBrowserTest overrides.
   void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
     base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM);
+    if (command_line->HasSwitch(switches::kMus)) {
+      default_command_line.AppendSwitch(switches::kMus);
+    } else if (command_line->HasSwitch(switches::kMusConfig)) {
+      default_command_line.AppendSwitchASCII(
+          switches::kMusConfig,
+          command_line->GetSwitchValueASCII(switches::kMusConfig));
+    }
     InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line);
     if (NoFirstRunSpecified()) {  // --no-first-run is present by default.
       *command_line = default_command_line;
diff --git a/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc b/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
index 37740abc..2ccc952 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine_browsertests.cc
@@ -563,20 +563,20 @@
     const ui::CompositionText& composition_text =
         mock_input_context->last_update_composition_arg().composition_text;
     EXPECT_EQ(base::UTF8ToUTF16("COMPOSITION_TEXT"), composition_text.text);
-    const ui::CompositionUnderlines underlines = composition_text.underlines;
+    const ui::ImeTextSpans ime_text_spans = composition_text.ime_text_spans;
 
-    ASSERT_EQ(2U, underlines.size());
+    ASSERT_EQ(2U, ime_text_spans.size());
     // single underline
-    EXPECT_EQ(SK_ColorBLACK, underlines[0].color);
-    EXPECT_FALSE(underlines[0].thick);
-    EXPECT_EQ(0U, underlines[0].start_offset);
-    EXPECT_EQ(5U, underlines[0].end_offset);
+    EXPECT_EQ(SK_ColorBLACK, ime_text_spans[0].color);
+    EXPECT_FALSE(ime_text_spans[0].thick);
+    EXPECT_EQ(0U, ime_text_spans[0].start_offset);
+    EXPECT_EQ(5U, ime_text_spans[0].end_offset);
 
     // double underline
-    EXPECT_EQ(SK_ColorBLACK, underlines[1].color);
-    EXPECT_TRUE(underlines[1].thick);
-    EXPECT_EQ(6U, underlines[1].start_offset);
-    EXPECT_EQ(10U, underlines[1].end_offset);
+    EXPECT_EQ(SK_ColorBLACK, ime_text_spans[1].color);
+    EXPECT_TRUE(ime_text_spans[1].thick);
+    EXPECT_EQ(6U, ime_text_spans[1].start_offset);
+    EXPECT_EQ(10U, ime_text_spans[1].end_offset);
   }
   {
     SCOPED_TRACE("clearComposition test");
@@ -1027,14 +1027,14 @@
     const ui::CompositionText& composition_text =
         mock_input_context->last_update_composition_arg().composition_text;
     EXPECT_EQ(base::UTF8ToUTF16("us"), composition_text.text);
-    const ui::CompositionUnderlines underlines = composition_text.underlines;
+    const ui::ImeTextSpans ime_text_spans = composition_text.ime_text_spans;
 
-    ASSERT_EQ(1U, underlines.size());
+    ASSERT_EQ(1U, ime_text_spans.size());
     // single underline
-    EXPECT_EQ(SK_ColorBLACK, underlines[0].color);
-    EXPECT_FALSE(underlines[0].thick);
-    EXPECT_EQ(0U, underlines[0].start_offset);
-    EXPECT_EQ(1U, underlines[0].end_offset);
+    EXPECT_EQ(SK_ColorBLACK, ime_text_spans[0].color);
+    EXPECT_FALSE(ime_text_spans[0].thick);
+    EXPECT_EQ(0U, ime_text_spans[0].start_offset);
+    EXPECT_EQ(1U, ime_text_spans[0].end_offset);
     EXPECT_TRUE(mock_input_context->last_commit_text().empty());
 
     InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
diff --git a/chrome/browser/chromeos/input_method/textinput_surroundingtext_browsertest.cc b/chrome/browser/chromeos/input_method/textinput_surroundingtext_browsertest.cc
index d8ac3f04..f03a9a1 100644
--- a/chrome/browser/chromeos/input_method/textinput_surroundingtext_browsertest.cc
+++ b/chrome/browser/chromeos/input_method/textinput_surroundingtext_browsertest.cc
@@ -10,7 +10,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ime/composition_underline.h"
 
 namespace chromeos {
 
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc
index 92181d0..5c96c03 100644
--- a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.cc
@@ -15,9 +15,11 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string16.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/tick_clock.h"
 #include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/extension_assets_manager.h"
@@ -40,6 +42,38 @@
 using ExtensionCallback = base::Callback<void(
     const scoped_refptr<const extensions::Extension>& extension)>;
 
+// The lock screen note taking availability state.
+// Used to report UMA histograms - the values should map to
+// LockScreenActionAvailability UMA enum values, and the values assigned to
+// enum states should NOT be changed.
+enum class ActionAvailability {
+  kAvailable = 0,
+  kNoActionHandlerApp = 1,
+  kAppNotSupportingLockScreen = 2,
+  kActionNotEnabledOnLockScreen = 3,
+  kDisallowedByPolicy = 4,
+  kCount,
+};
+
+ActionAvailability GetLockScreenNoteTakingAvailability(
+    chromeos::NoteTakingAppInfo* app_info) {
+  if (!app_info || !app_info->preferred)
+    return ActionAvailability::kNoActionHandlerApp;
+
+  switch (app_info->lock_screen_support) {
+    case chromeos::NoteTakingLockScreenSupport::kNotSupported:
+      return ActionAvailability::kAppNotSupportingLockScreen;
+    case chromeos::NoteTakingLockScreenSupport::kSupported:
+      return ActionAvailability::kActionNotEnabledOnLockScreen;
+    case chromeos::NoteTakingLockScreenSupport::kNotAllowedByPolicy:
+      return ActionAvailability::kDisallowedByPolicy;
+    case chromeos::NoteTakingLockScreenSupport::kEnabled:
+      return ActionAvailability::kAvailable;
+  }
+
+  return ActionAvailability::kAppNotSupportingLockScreen;
+}
+
 void InvokeCallbackOnTaskRunner(
     const ExtensionCallback& callback,
     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
@@ -113,8 +147,9 @@
 
 }  // namespace
 
-AppManagerImpl::AppManagerImpl()
-    : extensions_observer_(this),
+AppManagerImpl::AppManagerImpl(base::TickClock* tick_clock)
+    : tick_clock_(tick_clock),
+      extensions_observer_(this),
       note_taking_helper_observer_(this),
       weak_ptr_factory_(this) {}
 
@@ -264,12 +299,15 @@
   std::unique_ptr<chromeos::NoteTakingAppInfo> note_taking_app =
       chromeos::NoteTakingHelper::Get()->GetPreferredChromeAppInfo(
           primary_profile_);
+  ActionAvailability availability =
+      GetLockScreenNoteTakingAvailability(note_taking_app.get());
 
-  if (!note_taking_app || !note_taking_app->preferred ||
-      note_taking_app->lock_screen_support !=
-          chromeos::NoteTakingLockScreenSupport::kEnabled) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "Apps.LockScreen.NoteTakingApp.AvailabilityOnScreenLock", availability,
+      ActionAvailability::kCount);
+
+  if (availability != ActionAvailability::kAvailable)
     return std::string();
-  }
 
   return note_taking_app->app_id;
 }
@@ -325,14 +363,20 @@
           lock_screen_service->install_directory(), lock_screen_profile_,
           base::Bind(&InvokeCallbackOnTaskRunner,
                      base::Bind(&AppManagerImpl::CompleteLockScreenAppInstall,
-                                weak_ptr_factory_.GetWeakPtr(), install_count_),
+                                weak_ptr_factory_.GetWeakPtr(), install_count_,
+                                tick_clock_->NowTicks()),
                      base::ThreadTaskRunnerHandle::Get())));
   return State::kActivating;
 }
 
 void AppManagerImpl::CompleteLockScreenAppInstall(
     int install_id,
+    base::TimeTicks install_start_time,
     const scoped_refptr<const extensions::Extension>& app) {
+  UMA_HISTOGRAM_TIMES(
+      "Apps.LockScreen.NoteTakingApp.LockScreenInstallationDuration",
+      tick_clock_->NowTicks() - install_start_time);
+
   // Bail out if the app manager is no longer waiting for this app's
   // installation - the copied resources will be cleaned up when the (ephemeral)
   // lock screen profile is destroyed.
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h
index 4f658cd..61a230e 100644
--- a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h
+++ b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h
@@ -11,12 +11,17 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
+#include "base/time/time.h"
 #include "chrome/browser/chromeos/lock_screen_apps/app_manager.h"
 #include "chrome/browser/chromeos/note_taking_helper.h"
 #include "extensions/browser/extension_registry_observer.h"
 
 class Profile;
 
+namespace base {
+class TickClock;
+}
+
 namespace extensions {
 class Extension;
 class ExtensionRegistry;
@@ -29,7 +34,7 @@
                        public chromeos::NoteTakingHelper::Observer,
                        public extensions::ExtensionRegistryObserver {
  public:
-  AppManagerImpl();
+  explicit AppManagerImpl(base::TickClock* tick_clock);
   ~AppManagerImpl() override;
 
   // AppManager implementation:
@@ -77,6 +82,7 @@
   //     installation failed.
   void CompleteLockScreenAppInstall(
       int install_id,
+      base::TimeTicks install_start_time,
       const scoped_refptr<const extensions::Extension>& app);
 
   // Installs app to the lock screen profile's extension service and enables
@@ -106,6 +112,8 @@
   State state_ = State::kNotInitialized;
   std::string lock_screen_app_id_;
 
+  base::TickClock* tick_clock_;
+
   ScopedObserver<extensions::ExtensionRegistry,
                  extensions::ExtensionRegistryObserver>
       extensions_observer_;
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
index 4ff90d6e..d459dd5c 100644
--- a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/json/json_file_value_serializer.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/scoped_command_line.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
@@ -358,7 +359,9 @@
 
   AppManager* app_manager() { return app_manager_.get(); }
 
-  void ResetAppManager() { app_manager_ = base::MakeUnique<AppManagerImpl>(); }
+  void ResetAppManager() {
+    app_manager_ = base::MakeUnique<AppManagerImpl>(&tick_clock_);
+  }
 
   int note_taking_changed_count() const { return note_taking_changed_count_; }
 
@@ -378,6 +381,9 @@
 
   int NoteTakingChangedCountOnStart() { return IsInstallAsync() ? 1 : 0; }
 
+ protected:
+  base::SimpleTestTickClock tick_clock_;
+
  private:
   void OnNoteTakingChanged() { ++note_taking_changed_count_; }
 
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_window_metrics_tracker.cc b/chrome/browser/chromeos/lock_screen_apps/app_window_metrics_tracker.cc
new file mode 100644
index 0000000..2f82e60
--- /dev/null
+++ b/chrome/browser/chromeos/lock_screen_apps/app_window_metrics_tracker.cc
@@ -0,0 +1,172 @@
+// 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/chromeos/lock_screen_apps/app_window_metrics_tracker.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "extensions/browser/app_window/app_window.h"
+
+namespace {
+
+// Name of the count histogram used to keep track of (1-indexed) app launch
+// request ordinal number in a lock screen session.
+const char kAppLaunchOrdinalNumberInLockSession[] =
+    "Apps.LockScreen.NoteTakingApp.LaunchRequestOrdinalNumber";
+
+// Name of the histogram that tracks total amount of time an app window was
+// active for a single launch event - i.e. time from app window being shown to
+// app window closure.
+const char kTotalAppWindowSessionTime[] =
+    "Apps.LockScreen.NoteTakingApp.AppWindowLifeTime.TotalActive";
+
+// Name of the histogram that tracks amount of time an app window was in
+// foreground, on top the lock screen.
+const char kTimeAppWindowInForeground[] =
+    "Apps.LockScreen.NoteTakingApp.AppWindowLifeTime.Foreground";
+
+// Name of the histogram that track amount of time an app window was in
+// background, behind the lock screen.
+const char kTimeAppWindowInBackground[] =
+    "Apps.LockScreen.NoteTakingApp.AppWindowLifeTime.Background";
+
+// Name of the histogram that tracks the amount time needed to show an app
+// window (the window render view being created) after the app launch was
+// requested.
+const char kTimeFromLaunchToWindowBeingShown[] =
+    "Apps.LockScreen.NoteTakingApp.TimeToShowWindow";
+
+// Name of the histogram that tracks the amount of time needed to load the an
+// app window contents after the app launch was requested.
+const char kTimeToLoadAppWindowContents[] =
+    "Apps.LockScreen.NoteTakingApp.TimeToLoadAppWindowContents";
+
+// The name of the histogram that tracks the amount of time the app was in
+// launching state when the launch got canceled.
+const char kLaunchDurationAtLaunchCancel[] =
+    "Apps.LockScreen.NoteTakingApp.LaunchDurationAtLaunchCancel";
+
+// The name of the histogram that track the app state when the app lock screen
+// ends.
+const char kAppWindowStateOnRemoval[] =
+    "Apps.LockScreen.NoteTakingApp.FinalAppSessionState";
+
+}  // namespace
+
+namespace lock_screen_apps {
+
+AppWindowMetricsTracker::AppWindowMetricsTracker(base::TickClock* clock)
+    : clock_(clock) {}
+
+AppWindowMetricsTracker::~AppWindowMetricsTracker() = default;
+
+void AppWindowMetricsTracker::AppLaunchRequested() {
+  DCHECK_EQ(State::kInitial, state_);
+  SetState(State::kLaunchRequested);
+
+  ++app_launch_count_;
+  UMA_HISTOGRAM_COUNTS_100(kAppLaunchOrdinalNumberInLockSession,
+                           app_launch_count_);
+}
+
+void AppWindowMetricsTracker::MovedToForeground() {
+  DCHECK_NE(State::kInitial, state_);
+
+  if (state_ == State::kForeground)
+    return;
+
+  if (state_ == State::kBackground) {
+    SetState(State::kForeground);
+    return;
+  }
+
+  // Not expected to be in a state different than foreground or background
+  // after |state_after_window_contents_load_| is reset.
+  DCHECK(state_after_window_contents_load_.has_value());
+  state_after_window_contents_load_ = State::kForeground;
+}
+
+void AppWindowMetricsTracker::MovedToBackground() {
+  DCHECK_NE(State::kInitial, state_);
+
+  if (state_ == State::kBackground)
+    return;
+
+  if (state_ == State::kForeground) {
+    SetState(State::kBackground);
+    return;
+  }
+
+  // Not expected to be in a state different than foreground or background
+  // after |state_after_window_contents_load_| is reset.
+  DCHECK(state_after_window_contents_load_.has_value());
+  state_after_window_contents_load_ = State::kBackground;
+}
+
+void AppWindowMetricsTracker::AppWindowCreated(
+    extensions::AppWindow* app_window) {
+  Observe(app_window->web_contents());
+
+  SetState(State::kWindowCreated);
+}
+
+void AppWindowMetricsTracker::Reset() {
+  if (state_ == State::kInitial)
+    return;
+
+  UMA_HISTOGRAM_ENUMERATION(kAppWindowStateOnRemoval, state_, State::kCount);
+
+  if (state_ != State::kLaunchRequested && state_ != State::kWindowCreated) {
+    UMA_HISTOGRAM_LONG_TIMES(
+        kTotalAppWindowSessionTime,
+        clock_->NowTicks() - time_stamps_[State::kWindowShown]);
+  } else {
+    UMA_HISTOGRAM_TIMES(
+        kLaunchDurationAtLaunchCancel,
+        clock_->NowTicks() - time_stamps_[State::kLaunchRequested]);
+  }
+
+  SetState(State::kInitial);
+
+  state_after_window_contents_load_ = State::kForeground;
+
+  time_stamps_.clear();
+}
+
+void AppWindowMetricsTracker::RenderViewCreated(
+    content::RenderViewHost* render_view_host) {
+  SetState(State::kWindowShown);
+
+  UMA_HISTOGRAM_TIMES(
+      kTimeFromLaunchToWindowBeingShown,
+      clock_->NowTicks() - time_stamps_[State::kLaunchRequested]);
+}
+
+void AppWindowMetricsTracker::DocumentOnLoadCompletedInMainFrame() {
+  State next_state = state_after_window_contents_load_.value();
+  state_after_window_contents_load_.reset();
+  SetState(next_state);
+
+  UMA_HISTOGRAM_TIMES(kTimeToLoadAppWindowContents,
+                      clock_->NowTicks() - time_stamps_[State::kWindowShown]);
+}
+
+void AppWindowMetricsTracker::SetState(State state) {
+  if (state_ == state)
+    return;
+
+  if (state_ == State::kForeground) {
+    UMA_HISTOGRAM_MEDIUM_TIMES(kTimeAppWindowInForeground,
+                               clock_->NowTicks() - time_stamps_[state_]);
+  } else if (state_ == State::kBackground) {
+    UMA_HISTOGRAM_MEDIUM_TIMES(kTimeAppWindowInBackground,
+                               clock_->NowTicks() - time_stamps_[state_]);
+  }
+
+  state_ = state;
+  time_stamps_[state] = clock_->NowTicks();
+}
+
+}  // namespace lock_screen_apps
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_window_metrics_tracker.h b/chrome/browser/chromeos/lock_screen_apps/app_window_metrics_tracker.h
new file mode 100644
index 0000000..a5c95ab
--- /dev/null
+++ b/chrome/browser/chromeos/lock_screen_apps/app_window_metrics_tracker.h
@@ -0,0 +1,86 @@
+// 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_CHROMEOS_LOCK_SCREEN_APPS_APP_WINDOW_METRICS_TRACKER_H_
+#define CHROME_BROWSER_CHROMEOS_LOCK_SCREEN_APPS_APP_WINDOW_METRICS_TRACKER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace extensions {
+class AppWindow;
+}
+
+namespace lock_screen_apps {
+
+// Helper for tracking metrics for lock screen app window launches.
+class AppWindowMetricsTracker : public content::WebContentsObserver {
+ public:
+  explicit AppWindowMetricsTracker(base::TickClock* clock);
+  ~AppWindowMetricsTracker() override;
+
+  // Register app launch request.
+  void AppLaunchRequested();
+
+  // Registers the app window created for lock screen action - the class
+  // will begin observing the app window state as a result.
+  void AppWindowCreated(extensions::AppWindow* app_window);
+
+  // Updates metrics state for app window being moved to foreground.
+  void MovedToForeground();
+
+  // Updates metrics state for app window being moved to background.
+  void MovedToBackground();
+
+  // Stops tracking current app window state, and resets collected timestamps.
+  void Reset();
+
+  // content::WebContentsObserver:
+  void RenderViewCreated(content::RenderViewHost* render_view_host) override;
+  void DocumentOnLoadCompletedInMainFrame() override;
+
+ private:
+  // NOTE: Used in histograms - do not change order, or remove entries.
+  // Also, update LockScreenAppSessionState enum.
+  enum class State {
+    kInitial = 0,
+    kLaunchRequested = 1,
+    kWindowCreated = 2,
+    kWindowShown = 3,
+    kForeground = 4,
+    kBackground = 5,
+    kCount
+  };
+
+  void SetState(State state);
+
+  base::TickClock* clock_;
+
+  State state_ = State::kInitial;
+
+  // Maps states to their last occurrence time.
+  std::map<State, base::TimeTicks> time_stamps_;
+
+  // Number of times app launch was requested during the
+  int app_launch_count_ = 0;
+
+  // The state to which the metrics tracker should move after
+  // the window contents is loaded.
+  // Should be either kForeground or kBackground.
+  base::Optional<State> state_after_window_contents_load_ = State::kForeground;
+
+  DISALLOW_COPY_AND_ASSIGN(AppWindowMetricsTracker);
+};
+
+}  // namespace lock_screen_apps
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOCK_SCREEN_APPS_APP_WINDOW_METRICS_TRACKER_H_
diff --git a/chrome/browser/chromeos/lock_screen_apps/state_controller.cc b/chrome/browser/chromeos/lock_screen_apps/state_controller.cc
index 843225e6..18b8f214 100644
--- a/chrome/browser/chromeos/lock_screen_apps/state_controller.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/state_controller.cc
@@ -11,10 +11,13 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/strings/string16.h"
+#include "base/time/default_tick_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/lock_screen_apps/app_manager_impl.h"
+#include "chrome/browser/chromeos/lock_screen_apps/app_window_metrics_tracker.h"
 #include "chrome/browser/chromeos/lock_screen_apps/focus_cycler_delegate.h"
 #include "chrome/browser/chromeos/note_taking_helper.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -25,6 +28,7 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/service_manager_connection.h"
@@ -118,6 +122,8 @@
 }
 
 void StateController::Initialize() {
+  tick_clock_ = base::MakeUnique<base::DefaultTickClock>();
+
   // The tray action ptr might be set previously if the client was being created
   // for testing.
   if (!tray_action_ptr_) {
@@ -144,7 +150,8 @@
   lock_screen_data_.reset();
   if (app_manager_) {
     app_manager_->Stop();
-    ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/);
+    ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/,
+                                            NoteTakingExitReason::kShutdown);
     app_manager_.reset();
   }
   focus_cycler_delegate_ = nullptr;
@@ -159,8 +166,15 @@
                                       Profile::CreateStatus status) {
   // Ignore CREATED status - wait for profile to be initialized before
   // continuing.
-  if (status == Profile::CREATE_STATUS_CREATED)
+  if (status == Profile::CREATE_STATUS_CREATED) {
+    // Disable safe browsing for the profile to avoid activating
+    // SafeBrowsingService when the user has safe browsing disabled (reasoning
+    // similar to http://crbug.com/461493).
+    // TODO(tbarzic): Revisit this if webviews get enabled for lock screen apps.
+    lock_screen_profile->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled,
+                                                false);
     return;
+  }
 
   // On error, bail out - this will cause the lock screen apps to remain
   // unavailable on the device.
@@ -219,7 +233,7 @@
 
   // App manager might have been set previously by a test.
   if (!app_manager_)
-    app_manager_ = base::MakeUnique<AppManagerImpl>();
+    app_manager_ = base::MakeUnique<AppManagerImpl>(tick_clock_.get());
   app_manager_->Initialize(profile, lock_screen_profile_->GetOriginalProfile());
 
   input_devices_observer_.Add(ui::InputDeviceManager::GetInstance());
@@ -262,23 +276,19 @@
 }
 
 void StateController::RequestNewLockScreenNote() {
-  if (lock_screen_note_state_ != TrayActionState::kAvailable)
-    return;
-
-  DCHECK(app_manager_->IsNoteTakingAppAvailable());
-
-  // Update state to launching even if app fails to launch - this is to notify
-  // listeners that a lock screen note request was handled.
-  UpdateLockScreenNoteState(TrayActionState::kLaunching);
-  if (!app_manager_->LaunchNoteTaking())
-    UpdateLockScreenNoteState(TrayActionState::kAvailable);
+  HandleNewNoteRequest(NewNoteRequestType::kTrayAction);
 }
 
 void StateController::OnSessionStateChanged() {
   if (!session_manager::SessionManager::Get()->IsScreenLocked()) {
     lock_screen_data_->SetSessionLocked(false);
     app_manager_->Stop();
-    ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/);
+    if (lock_screen_note_state_ == TrayActionState::kBackground) {
+      RecordLockScreenAppUnlockAction(LockScreenUnlockAction::kSessionUnlocked);
+    }
+    ResetNoteTakingWindowAndMoveToNextState(
+        true /*close_window*/, NoteTakingExitReason::kSessionUnlock);
+    note_app_window_metrics_.reset();
     return;
   }
 
@@ -288,14 +298,23 @@
   app_manager_->Start(
       base::Bind(&StateController::OnNoteTakingAvailabilityChanged,
                  base::Unretained(this)));
+  note_app_window_metrics_ =
+      base::MakeUnique<AppWindowMetricsTracker>(tick_clock_.get());
   lock_screen_data_->SetSessionLocked(true);
   OnNoteTakingAvailabilityChanged();
 }
 
+void StateController::OnAppWindowAdded(extensions::AppWindow* app_window) {
+  if (note_app_window_ != app_window)
+    return;
+  note_app_window_metrics_->AppWindowCreated(app_window);
+}
+
 void StateController::OnAppWindowRemoved(extensions::AppWindow* app_window) {
   if (note_app_window_ != app_window)
     return;
-  ResetNoteTakingWindowAndMoveToNextState(false /*close_window*/);
+  ResetNoteTakingWindowAndMoveToNextState(
+      false /*close_window*/, NoteTakingExitReason::kAppWindowClosed);
 }
 
 void StateController::OnStylusStateChanged(ui::StylusState state) {
@@ -303,16 +322,19 @@
     return;
 
   if (state == ui::StylusState::REMOVED)
-    RequestNewLockScreenNote();
+    HandleNewNoteRequest(NewNoteRequestType::kStylusEject);
 }
 
 void StateController::BrightnessChanged(int level, bool user_initiated) {
-  if (level == 0 && !user_initiated)
-    ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/);
+  if (level == 0 && !user_initiated) {
+    ResetNoteTakingWindowAndMoveToNextState(
+        true /*close_window*/, NoteTakingExitReason::kScreenDimmed);
+  }
 }
 
 void StateController::SuspendImminent() {
-  ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/);
+  ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/,
+                                          NoteTakingExitReason::kSuspend);
 }
 
 extensions::AppWindow* StateController::CreateAppWindowForLockScreenAction(
@@ -362,8 +384,10 @@
 
 void StateController::MoveToBackground() {
   if (GetLockScreenNoteState() == TrayActionState::kLaunching) {
+    note_app_window_metrics_->Reset();
     UpdateLockScreenNoteState(TrayActionState::kAvailable);
   } else if (GetLockScreenNoteState() == TrayActionState::kActive) {
+    note_app_window_metrics_->MovedToBackground();
     UpdateLockScreenNoteState(TrayActionState::kBackground);
   }
 }
@@ -371,14 +395,58 @@
 void StateController::MoveToForeground() {
   if (GetLockScreenNoteState() != TrayActionState::kBackground)
     return;
+
+  RecordLockScreenAppUnlockAction(LockScreenUnlockAction::kUnlockCancelled);
+
+  note_app_window_metrics_->MovedToForeground();
   UpdateLockScreenNoteState(TrayActionState::kActive);
 }
 
+void StateController::HandleNewNoteRequestFromLockScreen(
+    NewNoteRequestType type) {
+  DCHECK(type == NewNoteRequestType::kLockScreenUiTap ||
+         type == NewNoteRequestType::kLockScreenUiSwipe ||
+         type == NewNoteRequestType::kLockScreenUiKeyboard);
+
+  HandleNewNoteRequest(type);
+}
+
+void StateController::RecordLockScreenAppUnlockAction(
+    LockScreenUnlockAction action) {
+  if (lock_screen_note_state_ != TrayActionState::kBackground)
+    return;
+
+  UMA_HISTOGRAM_ENUMERATION("Apps.LockScreen.NoteTakingApp.UnlockUIAction",
+                            action, LockScreenUnlockAction::kCount);
+}
+
+void StateController::HandleNewNoteRequest(NewNoteRequestType type) {
+  if (lock_screen_note_state_ != TrayActionState::kAvailable)
+    return;
+
+  DCHECK(app_manager_->IsNoteTakingAppAvailable());
+
+  UMA_HISTOGRAM_ENUMERATION("Apps.LockScreen.NoteTakingApp.LaunchRequestReason",
+                            type, NewNoteRequestType::kCount);
+
+  // Update state to launching even if app fails to launch - this is to notify
+  // listeners that a lock screen note request was handled.
+  UpdateLockScreenNoteState(TrayActionState::kLaunching);
+  if (!app_manager_->LaunchNoteTaking()) {
+    UpdateLockScreenNoteState(TrayActionState::kAvailable);
+    return;
+  }
+
+  note_app_window_metrics_->AppLaunchRequested();
+}
+
 void StateController::OnNoteTakingAvailabilityChanged() {
   if (!app_manager_->IsNoteTakingAppAvailable() ||
       (note_app_window_ && note_app_window_->GetExtension()->id() !=
                                app_manager_->GetNoteTakingAppId())) {
-    ResetNoteTakingWindowAndMoveToNextState(true /*close_window*/);
+    ResetNoteTakingWindowAndMoveToNextState(
+        true /*close_window*/,
+        NoteTakingExitReason::kAppLockScreenSupportDisabled);
     return;
   }
 
@@ -407,9 +475,20 @@
 }
 
 void StateController::ResetNoteTakingWindowAndMoveToNextState(
-    bool close_window) {
+    bool close_window,
+    NoteTakingExitReason exit_reason) {
   app_window_observer_.RemoveAll();
 
+  if (note_app_window_metrics_)
+    note_app_window_metrics_->Reset();
+
+  if (lock_screen_note_state_ != TrayActionState::kAvailable &&
+      lock_screen_note_state_ != TrayActionState::kNotAvailable) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "Apps.LockScreen.NoteTakingApp.NoteTakingExitReason", exit_reason,
+        NoteTakingExitReason::kCount);
+  }
+
   if (note_app_window_) {
     if (focus_cycler_delegate_)
       focus_cycler_delegate_->UnregisterLockScreenAppFocusHandler();
diff --git a/chrome/browser/chromeos/lock_screen_apps/state_controller.h b/chrome/browser/chromeos/lock_screen_apps/state_controller.h
index a1ee49c..715366c 100644
--- a/chrome/browser/chromeos/lock_screen_apps/state_controller.h
+++ b/chrome/browser/chromeos/lock_screen_apps/state_controller.h
@@ -25,6 +25,10 @@
 
 class PrefRegistrySimple;
 
+namespace base {
+class TickClock;
+}
+
 namespace content {
 class BrowserContext;
 }
@@ -49,6 +53,7 @@
 
 namespace lock_screen_apps {
 
+class AppWindowMetricsTracker;
 class FocusCyclerDelegate;
 class StateObserver;
 
@@ -61,6 +66,47 @@
                         public ui::InputDeviceEventObserver,
                         public chromeos::PowerManagerClient::Observer {
  public:
+  // Type of action that triggered a request for new note.
+  // Used in histograms - should be kept in sync with
+  // NewLockScreenNoteRequestType histogram enum, and assigned values should
+  // never be changed.
+  enum class NewNoteRequestType {
+    kTrayAction = 0,
+    kLockScreenUiTap = 1,
+    kLockScreenUiSwipe = 2,
+    kLockScreenUiKeyboard = 3,
+    kStylusEject = 4,
+    kCount,
+  };
+
+  // Reason for resetting note taking app window, and exiting note taking app.
+  // Used primarily for metrics reporting.
+  // IMPORTANT: The values should be kept in sync with
+  // LockScreenNoteTakingExitReason histogram enum, and assigned values should
+  // never be changed.
+  enum class NoteTakingExitReason {
+    kSessionUnlock = 0,
+    kShutdown = 1,
+    kScreenDimmed = 2,
+    kSuspend = 3,
+    kAppWindowClosed = 4,
+    kAppLockScreenSupportDisabled = 5,
+    kCount
+  };
+
+  // Action taken by the user on lock screen when a lock screen app window was
+  // in the background - used primarily for metrics reporting.
+  // IMPORTANT: The values should be kept in sync with
+  // LockScreenNoteTakingUnlockUIAction, and assigned values should never be
+  // changed.
+  enum class LockScreenUnlockAction {
+    kSessionUnlocked = 0,
+    kUnlockCancelled = 1,
+    kShutdown = 2,
+    kSignOut = 3,
+    kCount
+  };
+
   // Returns whether the StateController is enabled - it is currently guarded by
   // a feature flag. If not enabled, |StateController| instance is not allowed
   // to be created. |Get| will still work, but it will return nullptr.
@@ -122,6 +168,7 @@
   void OnSessionStateChanged() override;
 
   // extensions::AppWindowRegistry::Observer:
+  void OnAppWindowAdded(extensions::AppWindow* app_window) override;
   void OnAppWindowRemoved(extensions::AppWindow* app_window) override;
 
   // ui::InputDeviceEventObserver:
@@ -157,6 +204,14 @@
   // windows back to foreground (i.e. visible over lock screen UI).
   void MoveToForeground();
 
+  // Handles new note requests that come from lock screen UI.
+  void HandleNewNoteRequestFromLockScreen(NewNoteRequestType type);
+
+  // Records the user action taken on lock screen when the lock screen app
+  // unlock UI is shown - i.e. when lock UI is shown on top of backgrounded
+  // lock screen app window.
+  void RecordLockScreenAppUnlockAction(LockScreenUnlockAction action);
+
  private:
   // Called when profiles needed to run lock screen apps are ready - i.e. when
   // primary user profile was set using |SetPrimaryProfile| and the profile in
@@ -178,6 +233,9 @@
   // lock screen context.
   void InitializeWithCryptoKey(Profile* profile, const std::string& crypto_key);
 
+  // Handles request to launch a new-note lock screen flow.
+  void HandleNewNoteRequest(NewNoteRequestType type);
+
   // Called when app manager reports that note taking availability has changed.
   void OnNoteTakingAvailabilityChanged();
 
@@ -185,7 +243,9 @@
   // on lock screen, unregisters the window, and closes is if |close_window| is
   // set. It changes the current state to kAvailable or kNotAvailable, depending
   // on whether lock screen note taking action can still be handled.
-  void ResetNoteTakingWindowAndMoveToNextState(bool close_window);
+  void ResetNoteTakingWindowAndMoveToNextState(
+      bool close_window,
+      NoteTakingExitReason exit_reason);
 
   // Requests lock screen note action state change to |state|.
   // Returns whether the action state has changed.
@@ -218,6 +278,11 @@
   FocusCyclerDelegate* focus_cycler_delegate_ = nullptr;
 
   extensions::AppWindow* note_app_window_ = nullptr;
+  // Used to track metrics for app window launches - it is set when the user
+  // session is locked (and reset on unlock). Note that a single instance
+  // should not be reused for different lock sessions - it tracks number of app
+  // launches per lock screen.
+  std::unique_ptr<AppWindowMetricsTracker> note_app_window_metrics_;
 
   ScopedObserver<extensions::AppWindowRegistry,
                  extensions::AppWindowRegistry::Observer>
@@ -237,6 +302,10 @@
   // to session / app manager changes.
   base::Closure ready_callback_;
 
+  // The clock used to keep track of time, for example to report app window
+  // lifetime metrics.
+  std::unique_ptr<base::TickClock> tick_clock_;
+
   base::WeakPtrFactory<StateController> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(StateController);
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 75f6af9..577eda73 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -855,6 +855,13 @@
   // for the first time. Tell the UserImageManager that this user is not new to
   // prevent the avatar from getting changed.
   GetUserImageManager(user->GetAccountId())->UserLoggedIn(false, true);
+
+  // For public account, it's possible that the user-policy controlled wallpaper
+  // was fetched/cleared at the login screen (while for a regular user it was
+  // always fetched/cleared inside a user session), in the case the user-policy
+  // controlled wallpaper was cached/cleared by not updated in the login screen,
+  // so we need to update the wallpaper after the public user logged in.
+  WallpaperManager::Get()->SetUserWallpaperNow(user->GetAccountId());
   WallpaperManager::Get()->EnsureLoggedInUserWallpaperLoaded();
 
   SetPublicAccountDelegates();
diff --git a/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc b/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc
index ff776ec0..96c0000 100644
--- a/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc
+++ b/chrome/browser/chromeos/login/users/default_user_image/default_user_images.cc
@@ -91,6 +91,14 @@
     IDR_LOGIN_DEFAULT_USER_55,
     IDR_LOGIN_DEFAULT_USER_56,
     IDR_LOGIN_DEFAULT_USER_57,
+    IDR_LOGIN_DEFAULT_USER_58,
+    IDR_LOGIN_DEFAULT_USER_59,
+    IDR_LOGIN_DEFAULT_USER_60,
+    IDR_LOGIN_DEFAULT_USER_61,
+    IDR_LOGIN_DEFAULT_USER_62,
+    IDR_LOGIN_DEFAULT_USER_63,
+    IDR_LOGIN_DEFAULT_USER_64,
+    IDR_LOGIN_DEFAULT_USER_65,
 };
 
 const int kDefaultImagesCount = arraysize(kDefaultImageResourceIDs);
@@ -250,6 +258,14 @@
     IDS_LOGIN_DEFAULT_USER_DESC_55,
     IDS_LOGIN_DEFAULT_USER_DESC_56,
     IDS_LOGIN_DEFAULT_USER_DESC_57,
+    IDS_LOGIN_DEFAULT_USER_DESC_58,
+    IDS_LOGIN_DEFAULT_USER_DESC_59,
+    IDS_LOGIN_DEFAULT_USER_DESC_60,
+    IDS_LOGIN_DEFAULT_USER_DESC_61,
+    IDS_LOGIN_DEFAULT_USER_DESC_62,
+    IDS_LOGIN_DEFAULT_USER_DESC_63,
+    IDS_LOGIN_DEFAULT_USER_DESC_64,
+    IDS_LOGIN_DEFAULT_USER_DESC_65,
 };
 
 const int kDefaultImageDescriptionsMaxID = arraysize(kDefaultImageDescriptions);
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index 8f376eb..82cda76 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -1011,10 +1011,14 @@
   if (!wallpaper_files_id.is_valid())
     LOG(FATAL) << "Wallpaper flies id if invalid!";
 
+  // If we're at the login screen, do not change the wallpaper to the user
+  // policy controlled wallpaper but only update the cache. It will be later
+  // updated after the user logs in.
   SetCustomWallpaper(account_id, wallpaper_files_id, "policy-controlled.jpeg",
                      wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED,
                      wallpaper::POLICY, user_image->image(),
-                     true /* update wallpaper */);
+                     user_manager::UserManager::Get()
+                         ->IsUserLoggedIn() /* update wallpaper */);
 }
 
 void WallpaperManager::OnDeviceWallpaperPolicyChanged() {
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
index 6dfaae3..41191b9b 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_policy_browsertest.cc
@@ -51,6 +51,7 @@
 #include "components/policy/proto/cloud_policy.pb.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_names.h"
 #include "content/public/test/browser_test_utils.h"
 #include "crypto/rsa_private_key.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -287,6 +288,27 @@
               store->validation_status());
   }
 
+  // Inject |filename| as the device wallpaper policy. Set empty |filename| to
+  // clear policy.
+  void InjectDevicePolicy(const std::string& filename) {
+    if (!filename.empty()) {
+      device_policy_.payload()
+          .mutable_device_wallpaper_image()
+          ->set_device_wallpaper_image(ConstructPolicy(filename));
+    } else {
+      device_policy_.payload().Clear();
+    }
+    device_policy_.Build();
+    fake_session_manager_client_->set_device_policy(device_policy_.GetBlob());
+    fake_session_manager_client_->OnPropertyChangeComplete(true /* success */);
+  }
+
+  bool ShouldSetDeviceWallpaper(const AccountId& account_id) {
+    std::string url, hash;
+    return WallpaperManager::Get()->ShouldSetDeviceWallpaper(account_id, &url,
+                                                             &hash);
+  }
+
   // Obtain WallpaperInfo for |user_number| from WallpaperManager.
   void GetUserWallpaperInfo(int user_number,
                             wallpaper::WallpaperInfo* wallpaper_info) {
@@ -441,4 +463,41 @@
   ASSERT_EQ(kRedImageColor, GetAverageWallpaperColor());
 }
 
+IN_PROC_BROWSER_TEST_F(WallpaperManagerPolicyTest, PRE_DevicePolicyTest) {
+  SetSystemSalt();
+  RegisterUser(testUsers_[0].GetUserEmail());
+  StartupUtils::MarkOobeCompleted();
+}
+
+// Test that if device policy wallpaper and user policy wallpaper are both
+// specified, the device policy wallpaper is used in the login screen and the
+// user policy wallpaper is used inside of a user session.
+IN_PROC_BROWSER_TEST_F(WallpaperManagerPolicyTest, DevicePolicyTest) {
+  SetSystemSalt();
+
+  // Wait until default wallpaper has been loaded in the login screen.
+  RunUntilWallpaperChangeCount(1);
+
+  // Set the device wallpaper policy. Test that the device policy controlled
+  // wallpaper shows up in the login screen.
+  InjectDevicePolicy(kRedImageFileName);
+  RunUntilWallpaperChangeCount(2);
+  EXPECT_TRUE(ShouldSetDeviceWallpaper(user_manager::SignInAccountId()));
+  EXPECT_EQ(kRedImageColor, GetAverageWallpaperColor());
+
+  // Log in a test user and set the user wallpaper policy. The user policy
+  // controlled wallpaper shows up in the user session.
+  LoginUser(testUsers_[0].GetUserEmail());
+  InjectPolicy(0, kGreenImageFileName);
+  RunUntilWallpaperChangeCount(3);
+  EXPECT_EQ(kGreenImageColor, GetAverageWallpaperColor());
+
+  // Set the device wallpaper policy inside the user session. That that the
+  // user wallpaper doesn't change.
+  InjectDevicePolicy(kBlueImageFileName);
+  EXPECT_FALSE(ShouldSetDeviceWallpaper(
+      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId()));
+  EXPECT_EQ(kGreenImageColor, GetAverageWallpaperColor());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
index 15a0484..aa9e1895 100644
--- a/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
+++ b/chrome/browser/chromeos/policy/blocking_login_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/chromeos/login/ui/webui_login_display.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/install_attributes.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chrome/common/chrome_switches.h"
@@ -124,6 +125,10 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override {
     ASSERT_EQ(chrome::NOTIFICATION_PROFILE_ADDED, type);
+    if (chromeos::ProfileHelper::IsLockScreenAppProfile(
+            content::Source<Profile>(source).ptr())) {
+      return;
+    }
     ASSERT_FALSE(profile_added_);
     profile_added_ = content::Source<Profile>(source).ptr();
   }
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index 66ec6d60..b3533f4 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -61,6 +61,7 @@
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h"
@@ -833,6 +834,9 @@
     DCHECK_EQ(chrome::NOTIFICATION_PROFILE_CREATED, type);
 
     Profile* profile = content::Source<Profile>(source).ptr();
+    // Ignore lock screen apps profile.
+    if (chromeos::ProfileHelper::IsLockScreenAppProfile(profile))
+      return;
     registry_ = extensions::ExtensionRegistry::Get(profile);
 
     // Check if extension is already installed with newly created profile.
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 52e9b6c..363f8ef 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -584,9 +584,9 @@
   if (reason != REASON_PREF_CHANGED ||
       pref_name == prefs::kUnifiedDesktopEnabledByDefault) {
     const bool enabled = unified_desktop_enabled_by_default_.GetValue();
-    // TODO: this needs to work in Config::MUS. http://crbug.com/705591.
+    // TODO: this needs to work in Config::MASH. http://crbug.com/705591.
     if (ash::Shell::HasInstance() &&
-        chromeos::GetAshConfig() == ash::Config::CLASSIC) {
+        chromeos::GetAshConfig() != ash::Config::MASH) {
       ash::Shell::Get()->display_manager()->SetUnifiedDesktopEnabled(enabled);
     }
   }
diff --git a/chrome/browser/chromeos/profiles/profile_helper.cc b/chrome/browser/chromeos/profiles/profile_helper.cc
index bd4bb1b..55a41b3 100644
--- a/chrome/browser/chromeos/profiles/profile_helper.cc
+++ b/chrome/browser/chromeos/profiles/profile_helper.cc
@@ -357,6 +357,11 @@
 
 const user_manager::User* ProfileHelper::GetUserByProfile(
     const Profile* profile) const {
+  if (ProfileHelper::IsSigninProfile(profile) ||
+      ProfileHelper::IsLockScreenAppProfile(profile)) {
+    return nullptr;
+  }
+
   // This map is non-empty only in tests.
   if (enable_profile_to_user_testing || !user_list_for_testing_.empty()) {
     if (always_return_primary_user_for_testing)
@@ -378,10 +383,6 @@
   DCHECK(!content::BrowserThread::IsThreadInitialized(
              content::BrowserThread::UI) ||
          content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  if (ProfileHelper::IsSigninProfile(profile) ||
-      ProfileHelper::IsLockScreenAppProfile(profile)) {
-    return nullptr;
-  }
 
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
 
diff --git a/chrome/browser/chromeos/profiles/profile_util.cc b/chrome/browser/chromeos/profiles/profile_util.cc
index ed4fa10..66550e4 100644
--- a/chrome/browser/chromeos/profiles/profile_util.cc
+++ b/chrome/browser/chromeos/profiles/profile_util.cc
@@ -25,6 +25,8 @@
   // not try to access the sign-in profile.
   if (profile->GetPath() == ProfileHelper::GetSigninProfileDir())
     return false;
+  if (profile->GetPath() == ProfileHelper::GetLockScreenAppProfilePath())
+    return false;
   return true;
 }
 
diff --git a/chrome/browser/conflicts/installed_programs_win.cc b/chrome/browser/conflicts/installed_programs_win.cc
index a6a8dbc37..0e82fce 100644
--- a/chrome/browser/conflicts/installed_programs_win.cc
+++ b/chrome/browser/conflicts/installed_programs_win.cc
@@ -8,6 +8,7 @@
 
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/win/registry.h"
@@ -113,7 +114,7 @@
   // Ignore Microsoft programs.
   base::string16 publisher;
   if (GetValue(candidate, L"Publisher", &publisher) &&
-      publisher == L"Microsoft Corporation") {
+      base::StartsWith(publisher, L"Microsoft", base::CompareCase::SENSITIVE)) {
     return;
   }
 
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index 53337dd..8d5bc7ca 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -365,9 +365,9 @@
   if (method == chrome::devtools::Page::disable::kName)
     TogglePageEnable(false /* enable */, agent_host);
 
-  if (agent_host->GetType() == DevToolsAgentHost::kTypeBrowser &&
-      method.find("Browser.") == 0)
-    return HandleBrowserCommand(id, method, params).release();
+  auto* result = HandleBrowserCommand(id, method, params).release();
+  if (result)
+    return result;
 
   if (method == chrome::devtools::Page::setAdBlockingEnabled::kName)
     return SetAdBlockingEnabled(agent_host, id, params).release();
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 9a2fedaa..c409fcc 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -226,6 +226,7 @@
   void ReadyForTest() override {}
   InfoBarService* GetInfoBarService() override;
   void RenderProcessGone(bool crashed) override {}
+  void ShowCertificateViewer(const std::string& cert_chain) override{};
 
   content::WebContents* web_contents_;
   DISALLOW_COPY_AND_ASSIGN(DefaultBindingsDelegate);
@@ -864,49 +865,7 @@
 }
 
 void DevToolsUIBindings::ShowCertificateViewer(const std::string& cert_chain) {
-  std::unique_ptr<base::Value> value =
-      base::JSONReader::Read(cert_chain);
-  if (!value || value->GetType() != base::Value::Type::LIST) {
-    NOTREACHED();
-    return;
-  }
-
-  std::unique_ptr<base::ListValue> list =
-      base::ListValue::From(std::move(value));
-  std::vector<std::string> decoded;
-  for (size_t i = 0; i < list->GetSize(); ++i) {
-    base::Value* item;
-    if (!list->Get(i, &item) || item->GetType() != base::Value::Type::STRING) {
-      NOTREACHED();
-      return;
-    }
-    std::string temp;
-    if (!item->GetAsString(&temp)) {
-      NOTREACHED();
-      return;
-    }
-    if (!base::Base64Decode(temp, &temp)) {
-      NOTREACHED();
-      return;
-    }
-    decoded.push_back(temp);
-  }
-
-  std::vector<base::StringPiece> cert_string_piece;
-  for (const auto& str : decoded)
-    cert_string_piece.push_back(str);
-  scoped_refptr<net::X509Certificate> cert =
-       net::X509Certificate::CreateFromDERCertChain(cert_string_piece);
-  if (!cert) {
-    NOTREACHED();
-    return;
-  }
-
-  if (!agent_host_ || !agent_host_->GetWebContents())
-    return;
-  content::WebContents* inspected_wc = agent_host_->GetWebContents();
-  web_contents_->GetDelegate()->ShowCertificateViewerInDevTools(
-      inspected_wc, cert.get());
+  delegate_->ShowCertificateViewer(cert_chain);
 }
 
 void DevToolsUIBindings::ZoomIn() {
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h
index 4dd51b7..827d8f07 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -65,6 +65,7 @@
     virtual void ReadyForTest() = 0;
     virtual InfoBarService* GetInfoBarService() = 0;
     virtual void RenderProcessGone(bool crashed) = 0;
+    virtual void ShowCertificateViewer(const std::string& cert_chain) = 0;
   };
 
   explicit DevToolsUIBindings(content::WebContents* web_contents);
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index dc0dfd6..3bda5f78 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/base64.h"
 #include "base/command_line.h"
 #include "base/json/json_reader.h"
 #include "base/macros.h"
@@ -997,18 +998,6 @@
   return main_web_contents_;
 }
 
-void DevToolsWindow::ShowCertificateViewer(
-    scoped_refptr<net::X509Certificate> certificate) {
-  WebContents* inspected_contents = is_docked_ ?
-      GetInspectedWebContents() : main_web_contents_;
-  Browser* browser = NULL;
-  int tab = 0;
-  if (!FindInspectedBrowserAndTabIndex(inspected_contents, &browser, &tab))
-    return;
-  gfx::NativeWindow parent = browser->window()->GetNativeWindow();
-  ::ShowCertificateViewer(inspected_contents, parent, certificate.get());
-}
-
 void DevToolsWindow::ActivateContents(WebContents* contents) {
   if (is_docked_) {
     WebContents* inspected_tab = GetInspectedWebContents();
@@ -1154,12 +1143,6 @@
   return blink::WebInputEvent::IsPinchGestureEventType(event.GetType());
 }
 
-void DevToolsWindow::ShowCertificateViewerInDevTools(
-    content::WebContents* web_contents,
-    scoped_refptr<net::X509Certificate> certificate) {
-  ShowCertificateViewer(certificate);
-}
-
 void DevToolsWindow::ActivateWindow() {
   if (life_stage_ != kLoadCompleted)
     return;
@@ -1297,6 +1280,54 @@
   }
 }
 
+void DevToolsWindow::ShowCertificateViewer(const std::string& cert_chain) {
+  std::unique_ptr<base::Value> value = base::JSONReader::Read(cert_chain);
+  if (!value || value->GetType() != base::Value::Type::LIST) {
+    NOTREACHED();
+    return;
+  }
+
+  std::unique_ptr<base::ListValue> list =
+      base::ListValue::From(std::move(value));
+  std::vector<std::string> decoded;
+  for (size_t i = 0; i < list->GetSize(); ++i) {
+    base::Value* item;
+    if (!list->Get(i, &item) || item->GetType() != base::Value::Type::STRING) {
+      NOTREACHED();
+      return;
+    }
+    std::string temp;
+    if (!item->GetAsString(&temp)) {
+      NOTREACHED();
+      return;
+    }
+    if (!base::Base64Decode(temp, &temp)) {
+      NOTREACHED();
+      return;
+    }
+    decoded.push_back(temp);
+  }
+
+  std::vector<base::StringPiece> cert_string_piece;
+  for (const auto& str : decoded)
+    cert_string_piece.push_back(str);
+  scoped_refptr<net::X509Certificate> cert =
+      net::X509Certificate::CreateFromDERCertChain(cert_string_piece);
+  if (!cert) {
+    NOTREACHED();
+    return;
+  }
+
+  WebContents* inspected_contents =
+      is_docked_ ? GetInspectedWebContents() : main_web_contents_;
+  Browser* browser = NULL;
+  int tab = 0;
+  if (!FindInspectedBrowserAndTabIndex(inspected_contents, &browser, &tab))
+    return;
+  gfx::NativeWindow parent = browser->window()->GetNativeWindow();
+  ::ShowCertificateViewer(inspected_contents, parent, cert.get());
+}
+
 void DevToolsWindow::OnLoadCompleted() {
   // First seed inspected tab id for extension APIs.
   WebContents* inspected_web_contents = GetInspectedWebContents();
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index ca3e0ef..c9c7121 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -127,8 +127,6 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
 
-  void ShowCertificateViewer(scoped_refptr<net::X509Certificate> certificate);
-
   // BeforeUnload interception ////////////////////////////////////////////////
 
   // In order to preserve any edits the user may have made in devtools, the
@@ -315,9 +313,6 @@
                       const content::FileChooserParams& params) override;
   bool PreHandleGestureEvent(content::WebContents* source,
                              const blink::WebGestureEvent& event) override;
-  void ShowCertificateViewerInDevTools(
-      content::WebContents* web_contents,
-      scoped_refptr<net::X509Certificate> certificate) override;
 
   // content::DevToolsUIBindings::Delegate overrides
   void ActivateWindow() override;
@@ -335,6 +330,7 @@
   void ReadyForTest() override;
   InfoBarService* GetInfoBarService() override;
   void RenderProcessGone(bool crashed) override;
+  void ShowCertificateViewer(const std::string& cert_viewer) override;
 
   void ColorPickedInEyeDropper(int r, int g, int b, int a);
   void CreateDevToolsBrowser();
diff --git a/chrome/browser/download/DEPS b/chrome/browser/download/DEPS
index dba1ce6..32d7dc9 100644
--- a/chrome/browser/download/DEPS
+++ b/chrome/browser/download/DEPS
@@ -1,4 +1,4 @@
 include_rules = [
   "+components/drive/drive_pref_names.h",
-  "+components/safe_browsing/csd.pb.h",
+  "+components/safe_browsing/proto/csd.pb.h",
 ]
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index b3d47ee..dc37e2a 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -83,7 +83,7 @@
 #include "components/infobars/core/infobar.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/download_danger_type.h"
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "content/public/browser/download_item.h"
@@ -502,6 +502,12 @@
     return GetDownloadDirectory(browser).Append(file.BaseName());
   }
 
+  base::FilePath TemporaryFile(const base::FilePath& file) {
+    base::FilePath tmp;
+    base::GetTempDir(&tmp);
+    return tmp.Append(file.BaseName());
+  }
+
   // Must be called after browser creation.  Creates a temporary
   // directory for downloads that is auto-deleted on destruction.
   // Returning false indicates a failure of the function, and should be asserted
@@ -2025,7 +2031,7 @@
 
   // As long as we're here, confirmed everything else is good.
   EXPECT_EQ(1, browser()->tab_strip_model()->count());
-  CheckDownload(browser(), file, file);
+  CheckDownloadFullPaths(browser(), TemporaryFile(file), OriginFile(file));
 }
 
 // Download an extension. Expect a dangerous download warning.
diff --git a/chrome/browser/download/download_commands.cc b/chrome/browser/download/download_commands.cc
index a4a6040e..40baa91 100644
--- a/chrome/browser/download/download_commands.cc
+++ b/chrome/browser/download/download_commands.cc
@@ -28,7 +28,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/google/core/browser/google_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "net/base/url_util.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/browser/download/download_danger_prompt.h b/chrome/browser/download/download_danger_prompt.h
index 55fa38e7a7..10331bed 100644
--- a/chrome/browser/download/download_danger_prompt.h
+++ b/chrome/browser/download/download_danger_prompt.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_DANGER_PROMPT_H_
 
 #include "base/callback_forward.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace content {
 class DownloadItem;
diff --git a/chrome/browser/download/download_danger_prompt_browsertest.cc b/chrome/browser/download/download_danger_prompt_browsertest.cc
index 82eab21..dbc0f6f 100644
--- a/chrome/browser/download/download_danger_prompt_browsertest.cc
+++ b/chrome/browser/download/download_danger_prompt_browsertest.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "content/public/test/mock_download_item.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index c6b31c58..f7a60c11 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -79,6 +79,7 @@
 class DefaultDownloadDirectory {
  public:
   const base::FilePath& path() const { return path_; }
+  const base::FilePath& temp_path() const { return temp_path_; }
 
  private:
   friend struct base::LazyInstanceTraitsBase<DefaultDownloadDirectory>;
@@ -87,6 +88,11 @@
     if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path_)) {
       NOTREACHED();
     }
+
+    if (!base::GetTempDir(&temp_path_)) {
+      NOTREACHED();
+    }
+
     if (DownloadPathIsDangerous(path_)) {
       // This is only useful on platforms that support
       // DIR_DEFAULT_DOWNLOADS_SAFE.
@@ -97,6 +103,7 @@
   }
 
   base::FilePath path_;
+  base::FilePath temp_path_;
 
   DISALLOW_COPY_AND_ASSIGN(DefaultDownloadDirectory);
 };
@@ -238,6 +245,11 @@
 }
 
 // static
+const base::FilePath& DownloadPrefs::GetTempDownloadDirectory() {
+  return g_default_download_directory.Get().temp_path();
+}
+
+// static
 DownloadPrefs* DownloadPrefs::FromDownloadManager(
     DownloadManager* download_manager) {
   ChromeDownloadManagerDelegate* delegate =
diff --git a/chrome/browser/download/download_prefs.h b/chrome/browser/download/download_prefs.h
index 0499d71..58855983 100644
--- a/chrome/browser/download/download_prefs.h
+++ b/chrome/browser/download/download_prefs.h
@@ -40,6 +40,9 @@
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+  // Returns the download directory for temporary files.
+  static const base::FilePath& GetTempDownloadDirectory();
+
   // Returns the default download directory.
   static const base::FilePath& GetDefaultDownloadDirectory();
 
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index d3677b9..faca8fe6 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -220,7 +220,14 @@
       // If the user is going to be prompted and the user has been prompted
       // before, then always prefer the last directory that the user selected.
       target_directory = download_prefs_->SaveFilePath();
+    } else if (download_prefs_->IsAutoOpenEnabledBasedOnExtension(
+                   generated_filename)) {
+      // If we do not need user confirmation and we are going to auto-open the
+      // file, just download it to the system temp directory.
+      target_directory = download_prefs_->GetTempDownloadDirectory();
     } else {
+      // The user has not configured this file to be auto-opened. Download it to
+      // the standard location.
       target_directory = download_prefs_->DownloadPath();
     }
     virtual_path_ = target_directory.Append(generated_filename);
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index 5398afe7..32476fe4 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/at_exit.h"
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/location.h"
 #include "base/macros.h"
@@ -123,6 +124,10 @@
                        // marked interrupted or cancelled.
 };
 
+// Used with DownloadTestCase. Whether the file is expected to be downloaded to
+// the default download directory or the system-wide temp directory.
+enum TestCaseExpectDirectory { DEFAULT_DIRECTORY, TEMP_DIRECTORY };
+
 // Typical download test case. Used with
 // DownloadTargetDeterminerTest::RunTestCase().
 struct DownloadTestCase {
@@ -154,6 +159,9 @@
 
   // Type of intermediate path to expect.
   TestCaseExpectIntermediate expected_intermediate;
+
+  // The destination directory where the file is expected to be downloaded to.
+  TestCaseExpectDirectory expected_directory;
 };
 
 class MockDownloadTargetDeterminerDelegate
@@ -238,10 +246,19 @@
   // Set the kPromptForDownload user preference to |prompt|.
   void SetPromptForDownload(bool prompt);
 
+  // Utility function to get the full path for |relative_path| within
+  // |base_path|.
+  base::FilePath GetPathInDir(const base::FilePath::StringType& relative_path,
+                              const base::FilePath& base_path);
+
   // Given the relative path |path|, returns the full path under the temporary
   // downloads directory.
   base::FilePath GetPathInDownloadDir(const base::FilePath::StringType& path);
 
+  // Given the relative path |path|, returns the full path under the system
+  // temporary directory where auto-open files are saved.
+  base::FilePath GetPathInTempDir(const base::FilePath::StringType& path);
+
   // Run |test_case| using |item|.
   void RunTestCase(const DownloadTestCase& test_case,
                    const base::FilePath& initial_virtual_path,
@@ -273,6 +290,8 @@
     return test_virtual_dir_;
   }
 
+  const base::FilePath& test_temp_dir() const { return test_temp_dir_; }
+
   MockDownloadTargetDeterminerDelegate* delegate() {
     return &delegate_;
   }
@@ -289,6 +308,7 @@
   NullWebContentsDelegate web_contents_delegate_;
   base::ScopedTempDir test_download_dir_;
   base::FilePath test_virtual_dir_;
+  base::FilePath test_temp_dir_;
   safe_browsing::FileTypePoliciesTestOverlay file_type_configuration_;
 };
 
@@ -299,6 +319,7 @@
   web_contents()->SetDelegate(&web_contents_delegate_);
   ASSERT_TRUE(test_download_dir_.CreateUniqueTempDir());
   test_virtual_dir_ = test_download_dir().Append(FILE_PATH_LITERAL("virtual"));
+  ASSERT_TRUE(base::GetTempDir(&test_temp_dir_));
   download_prefs_->SetDownloadPath(test_download_dir());
   delegate_.SetupDefaults();
   SetUpFileTypePolicies();
@@ -384,14 +405,25 @@
       SetBoolean(prefs::kPromptForDownload, prompt);
 }
 
-base::FilePath DownloadTargetDeterminerTest::GetPathInDownloadDir(
-    const base::FilePath::StringType& relative_path) {
+base::FilePath DownloadTargetDeterminerTest::GetPathInDir(
+    const base::FilePath::StringType& relative_path,
+    const base::FilePath& base_path) {
   if (relative_path.empty())
     return base::FilePath();
-  base::FilePath full_path(test_download_dir().Append(relative_path));
+  base::FilePath full_path(base_path.Append(relative_path));
   return full_path.NormalizePathSeparators();
 }
 
+base::FilePath DownloadTargetDeterminerTest::GetPathInDownloadDir(
+    const base::FilePath::StringType& relative_path) {
+  return GetPathInDir(relative_path, test_download_dir());
+}
+
+base::FilePath DownloadTargetDeterminerTest::GetPathInTempDir(
+    const base::FilePath::StringType& relative_path) {
+  return GetPathInDir(relative_path, test_temp_dir());
+}
+
 void DownloadTargetDeterminerTest::RunTestCase(
     const DownloadTestCase& test_case,
     const base::FilePath& initial_virtual_path,
@@ -439,8 +471,16 @@
 void DownloadTargetDeterminerTest::VerifyDownloadTarget(
     const DownloadTestCase& test_case,
     const DownloadTargetInfo* target_info) {
-  base::FilePath expected_local_path(
-      GetPathInDownloadDir(test_case.expected_local_path));
+  base::FilePath expected_local_path;
+  switch (test_case.expected_directory) {
+    case DEFAULT_DIRECTORY:
+      expected_local_path = GetPathInDownloadDir(test_case.expected_local_path);
+      break;
+    case TEMP_DIRECTORY:
+      expected_local_path = GetPathInTempDir(test_case.expected_local_path);
+      break;
+  }
+
   EXPECT_EQ(expected_local_path.value(), target_info->target_path.value());
   EXPECT_EQ(test_case.expected_disposition, target_info->target_disposition);
   EXPECT_EQ(test_case.expected_danger_type, target_info->danger_type);
@@ -561,7 +601,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// Save_As Safe
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -570,7 +610,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// Automatic Dangerous
        AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
@@ -580,7 +620,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// Forced Safe
        FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -590,7 +630,7 @@
        FILE_PATH_LITERAL("forced-foo.txt"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY},
   };
 
   // The test assumes that .kindabad files have a danger level of
@@ -610,7 +650,7 @@
 
        FILE_PATH_LITERAL(""), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_LOCAL_PATH}};
+       EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY}};
   ON_CALL(*delegate(), RequestConfirmation(_, _, _, _))
       .WillByDefault(WithArg<3>(ScheduleCallback2(
           DownloadConfirmationResult::CANCELED, base::FilePath())));
@@ -629,7 +669,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 1: Save As Dangerous URL
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
@@ -638,7 +678,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 2: Forced Dangerous URL
        FORCED, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
@@ -648,7 +688,7 @@
        FILE_PATH_LITERAL("forced-foo.txt"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 3: Automatic Dangerous URL + Dangerous file. Dangerous URL takes
        // precedence.
@@ -659,7 +699,7 @@
        FILE_PATH_LITERAL("foo.html"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 4: Save As Dangerous URL + Dangerous file
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
@@ -668,7 +708,7 @@
 
        FILE_PATH_LITERAL("foo.html"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 5: Forced Dangerous URL + Dangerous file
        FORCED, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
@@ -678,7 +718,7 @@
        FILE_PATH_LITERAL("forced-foo.html"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
   };
 
   ON_CALL(*delegate(), CheckDownloadUrl(_, _, _))
@@ -698,7 +738,8 @@
        "http://phishing.example.com/foo.kindabad", "", FILE_PATH_LITERAL(""),
 
        FILE_PATH_LITERAL("foo.kindabad"),
-       DownloadItem::TARGET_DISPOSITION_OVERWRITE, EXPECT_UNCONFIRMED},
+       DownloadItem::TARGET_DISPOSITION_OVERWRITE, EXPECT_UNCONFIRMED,
+       DEFAULT_DIRECTORY},
 
       {// 1: Automatic Maybe dangerous content with DANGEROUS type.
        AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
@@ -706,7 +747,7 @@
        FILE_PATH_LITERAL(""),
 
        FILE_PATH_LITERAL("foo.bad"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 2: Save As Maybe dangerous content
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
@@ -716,7 +757,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 3: Forced Maybe dangerous content
        FORCED, content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT,
@@ -727,7 +768,7 @@
        FILE_PATH_LITERAL("forced-foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED}};
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY}};
 
   // Test assumptions:
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
@@ -755,7 +796,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD}};
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY}};
 
   // These test cases are run with a last save path set to a non-emtpy local
   // download directory.
@@ -769,7 +810,7 @@
        FILE_PATH_LITERAL("foo/foo.txt"),
        DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 1: Start an automatic download. This should be saved to the user's
        //    default download directory and not the last used Save As directory.
@@ -779,7 +820,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
   };
 
   // This test case is run with the last save path set to a non-empty virtual
@@ -791,7 +832,7 @@
 
        FILE_PATH_LITERAL("bar.txt"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY},
   };
 
   {
@@ -857,7 +898,8 @@
         FILE_PATH_LITERAL("foo-local.txt"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_LOCAL_PATH};
+        EXPECT_LOCAL_PATH,
+        DEFAULT_DIRECTORY};
     EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _))
         .WillOnce(WithArg<2>(ScheduleCallback(
             GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")))));
@@ -877,7 +919,8 @@
         FILE_PATH_LITERAL("foo-local.txt"),
         DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-        EXPECT_LOCAL_PATH};
+        EXPECT_LOCAL_PATH,
+        DEFAULT_DIRECTORY};
     EXPECT_CALL(*delegate(), DetermineLocalPath(_, _, _))
         .WillOnce(WithArg<2>(ScheduleCallback(
             GetPathInDownloadDir(FILE_PATH_LITERAL("foo-local.txt")))));
@@ -904,7 +947,8 @@
         FILE_PATH_LITERAL("foo-x.txt"),
         DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-        EXPECT_CRDOWNLOAD};
+        EXPECT_CRDOWNLOAD,
+        DEFAULT_DIRECTORY};
     EXPECT_CALL(*delegate(), RequestConfirmation(
                                  _, test_virtual_dir().AppendASCII("bar.txt"),
                                  DownloadConfirmationReason::SAVE_AS, _))
@@ -927,7 +971,8 @@
         FILE_PATH_LITERAL("forced-foo.txt"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_LOCAL_PATH};
+        EXPECT_LOCAL_PATH,
+        DEFAULT_DIRECTORY};
     RunTestCasesWithActiveItem(&kForcedSafe, 1);
   }
 }
@@ -944,7 +989,8 @@
       FILE_PATH_LITERAL(""),
       FILE_PATH_LITERAL("foo.txt"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   const struct {
     TestCaseType type;
@@ -988,7 +1034,8 @@
       FILE_PATH_LITERAL("bar.txt"),
       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   struct TestCase {
     PathValidationResult result;
@@ -1039,7 +1086,7 @@
 
        FILE_PATH_LITERAL(""), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY},
   };
 
   // The default download directory is the virtual path.
@@ -1068,7 +1115,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 1: Dangerous due to not having visited referrer before.
        AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
@@ -1079,7 +1126,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 2: Safe because the user is being prompted.
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1090,7 +1137,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 3: Safe because of forced path.
        FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1101,7 +1148,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY},
   };
 
   // This test assumes that the danger level of .kindabad files is
@@ -1140,7 +1187,8 @@
       FILE_PATH_LITERAL("foo.txt"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase kAllowOnUserGesture = {
       AUTOMATIC,
@@ -1153,7 +1201,8 @@
       FILE_PATH_LITERAL("foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_UNCONFIRMED};
+      EXPECT_UNCONFIRMED,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase kDangerousFile = {
       AUTOMATIC,
@@ -1166,7 +1215,8 @@
       FILE_PATH_LITERAL("foo.bad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_UNCONFIRMED};
+      EXPECT_UNCONFIRMED,
+      DEFAULT_DIRECTORY};
 
   const struct {
     ui::PageTransition page_transition;
@@ -1263,7 +1313,8 @@
       FILE_PATH_LITERAL("automatic.txt"),
       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   SetPromptForDownload(true);
   EXPECT_CALL(*delegate(),
@@ -1286,7 +1337,8 @@
       FILE_PATH_LITERAL("save-as.txt"),
       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   SetPromptForDownload(true);
   EXPECT_CALL(*delegate(),
@@ -1309,7 +1361,8 @@
       FILE_PATH_LITERAL("foo.txt"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_LOCAL_PATH};
+      EXPECT_LOCAL_PATH,
+      DEFAULT_DIRECTORY};
 
   SetPromptForDownload(true);
   RunTestCasesWithActiveItem(&kSafeForced, 1);
@@ -1329,13 +1382,56 @@
       FILE_PATH_LITERAL("foo.dummy"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      TEMP_DIRECTORY};
   SetPromptForDownload(true);
   EnableAutoOpenBasedOnExtension(
       base::FilePath(FILE_PATH_LITERAL("dummy.dummy")));
   RunTestCasesWithActiveItem(&kAutoOpen, 1);
 }
 
+// In an automatic download, auto-opened files should be saved to a temp
+// directory.
+TEST_F(DownloadTargetDeterminerTest, AutoOpen_Automatic) {
+  const DownloadTestCase kTestCase = {
+      AUTOMATIC,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/foo.dummy",
+      "",
+      FILE_PATH_LITERAL(""),
+
+      FILE_PATH_LITERAL("foo.dummy"),
+      DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+
+      EXPECT_CRDOWNLOAD,
+      TEMP_DIRECTORY};
+  EnableAutoOpenBasedOnExtension(
+      base::FilePath(FILE_PATH_LITERAL("dummy.dummy")));
+  RunTestCasesWithActiveItem(&kTestCase, 1);
+}
+
+// Even if we are set to auto-open this file, save the file to a permanent
+// location if triggered by a Save As.
+TEST_F(DownloadTargetDeterminerTest, AutoOpen_SaveAs) {
+  const DownloadTestCase kTestCase = {
+      SAVE_AS,
+      content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+      DownloadFileType::NOT_DANGEROUS,
+      "http://example.com/foo.dummy",
+      "",
+      FILE_PATH_LITERAL(""),
+
+      FILE_PATH_LITERAL("foo.dummy"),
+      DownloadItem::TARGET_DISPOSITION_PROMPT,
+
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
+  EnableAutoOpenBasedOnExtension(
+      base::FilePath(FILE_PATH_LITERAL("dummy.dummy")));
+  RunTestCasesWithActiveItem(&kTestCase, 1);
+}
+
 // If an embedder responds to a RequestConfirmation with a new path and a
 // CONTINUE_WITHOUT_CONFIRMATION, then we shouldn't consider the file as safe.
 TEST_F(DownloadTargetDeterminerTest, ContinueWithoutConfirmation_SaveAs) {
@@ -1350,7 +1446,8 @@
       FILE_PATH_LITERAL("foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_UNCONFIRMED};
+      EXPECT_UNCONFIRMED,
+      DEFAULT_DIRECTORY};
 
   EXPECT_CALL(
       *delegate(),
@@ -1378,7 +1475,8 @@
       FILE_PATH_LITERAL("foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   EXPECT_CALL(
       *delegate(),
@@ -1408,7 +1506,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 1: Automatic User Script - Shouldn't prompt for user script downloads
        //    even if "Prompt for download" preference is set.
@@ -1419,7 +1517,7 @@
        FILE_PATH_LITERAL("foo.user.js"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
   };
 
   SetPromptForDownload(true);
@@ -1439,7 +1537,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 1: Save_As Safe
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1448,7 +1546,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
   };
 
   SetManagedDownloadPath(test_download_dir());
@@ -1469,7 +1567,7 @@
        FILE_PATH_LITERAL("overridden/foo.txt"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 1: Save_As Safe
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1479,7 +1577,7 @@
        FILE_PATH_LITERAL("overridden/foo.txt"),
        DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 2: Automatic Dangerous
        AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
@@ -1489,7 +1587,7 @@
        FILE_PATH_LITERAL("overridden/foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 3: Forced Safe
        FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1499,7 +1597,7 @@
        FILE_PATH_LITERAL("forced-foo.txt"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY},
   };
 
   ON_CALL(*delegate(), NotifyExtensions(_, _, _))
@@ -1522,7 +1620,8 @@
       FILE_PATH_LITERAL("overridden/foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_UNCONFIRMED};
+      EXPECT_UNCONFIRMED,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase kHandledBySafeBrowsing = {
       AUTOMATIC,
@@ -1535,7 +1634,8 @@
       FILE_PATH_LITERAL("overridden/foo.kindabad"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_UNCONFIRMED};
+      EXPECT_UNCONFIRMED,
+      DEFAULT_DIRECTORY};
 
   ON_CALL(*delegate(), NotifyExtensions(_, _, _))
       .WillByDefault(Invoke(&NotifyExtensionsOverridePath));
@@ -1561,7 +1661,8 @@
       FILE_PATH_LITERAL("overridden/foo.txt"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase& test_case = kNotifyExtensionsTestCase;
   std::unique_ptr<content::MockDownloadItem> item =
@@ -1610,7 +1711,8 @@
       FILE_PATH_LITERAL("overridden/foo.txt"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase& test_case = kNotifyExtensionsTestCase;
   std::unique_ptr<content::MockDownloadItem> item =
@@ -1647,7 +1749,8 @@
       kInitialPath,
       DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase& test_case = kInitialPathTestCase;
   std::unique_ptr<content::MockDownloadItem> item =
@@ -1680,7 +1783,7 @@
 
        FILE_PATH_LITERAL("foo.txt"), DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 1: Save_As Safe: Initial path used.
        SAVE_AS, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1689,7 +1792,7 @@
 
        kInitialPath, DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
       {// 2: Automatic Dangerous: Initial path is ignored since the user hasn't
        // been prompted before.
@@ -1700,7 +1803,7 @@
        FILE_PATH_LITERAL("foo.kindabad"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_UNCONFIRMED},
+       EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
 
       {// 3: Forced Safe: Initial path is ignored due to the forced path.
        FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1710,7 +1813,7 @@
        FILE_PATH_LITERAL("forced-foo.txt"),
        DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-       EXPECT_LOCAL_PATH},
+       EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY},
   };
 
   // The test assumes that .kindabad files have a danger level of
@@ -1759,7 +1862,8 @@
       FILE_PATH_LITERAL("forced-foo.txt"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_LOCAL_PATH};
+      EXPECT_LOCAL_PATH,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase& test_case = kResumedForcedDownload;
   base::FilePath expected_path =
@@ -1804,27 +1908,24 @@
 
        kInitialPath, DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-       EXPECT_CRDOWNLOAD},
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
 
-      {
-          // 2: Automatic Dangerous
-          AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-          DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.kindabad",
-          "", FILE_PATH_LITERAL(""),
+      {// 2: Automatic Dangerous
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.kindabad", "",
+       FILE_PATH_LITERAL(""),
 
-          FILE_PATH_LITERAL("foo.kindabad"),
-          DownloadItem::TARGET_DISPOSITION_PROMPT, EXPECT_CRDOWNLOAD,
-      },
+       FILE_PATH_LITERAL("foo.kindabad"),
+       DownloadItem::TARGET_DISPOSITION_PROMPT, EXPECT_CRDOWNLOAD,
+       DEFAULT_DIRECTORY},
 
-      {
-          // 3: Automatic Dangerous
-          AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
-          DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.bad", "",
-          FILE_PATH_LITERAL(""),
+      {// 3: Automatic Dangerous
+       AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.bad", "",
+       FILE_PATH_LITERAL(""),
 
-          FILE_PATH_LITERAL("foo.bad"), DownloadItem::TARGET_DISPOSITION_PROMPT,
-          EXPECT_CRDOWNLOAD,
-      },
+       FILE_PATH_LITERAL("foo.bad"), DownloadItem::TARGET_DISPOSITION_PROMPT,
+       EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
   };
 
   ASSERT_EQ(DownloadFileType::ALLOW_ON_USER_GESTURE,
@@ -1878,7 +1979,7 @@
         FILE_PATH_LITERAL("foo.txt"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        FILE_PATH_LITERAL("bar.txt.crdownload"),
        FILE_PATH_LITERAL("foo.txt.crdownload")},
 
@@ -1889,7 +1990,7 @@
 
         kInitialPath, DownloadItem::TARGET_DISPOSITION_PROMPT,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        FILE_PATH_LITERAL("foo.txt.crdownload"),
        FILE_PATH_LITERAL("some_path/bar.txt.crdownload")},
 
@@ -1901,7 +2002,7 @@
         FILE_PATH_LITERAL("foo.kindabad"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_UNCONFIRMED},
+        EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
        FILE_PATH_LITERAL("Unconfirmed abcd.crdownload"),
        FILE_PATH_LITERAL("Unconfirmed abcd.crdownload")},
 
@@ -1913,7 +2014,7 @@
         FILE_PATH_LITERAL("foo.kindabad"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_UNCONFIRMED},
+        EXPECT_UNCONFIRMED, DEFAULT_DIRECTORY},
        FILE_PATH_LITERAL("other_path/Unconfirmed abcd.crdownload"),
        // Rely on the EXPECT_UNCONFIRMED check in the general test settings. A
        // new intermediate path of the form "Unconfirmed <number>.crdownload"
@@ -1929,7 +2030,7 @@
         FILE_PATH_LITERAL("forced-foo.txt"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_LOCAL_PATH},
+        EXPECT_LOCAL_PATH, DEFAULT_DIRECTORY},
        FILE_PATH_LITERAL("forced-foo.txt"),
        FILE_PATH_LITERAL("forced-foo.txt")},
   };
@@ -1988,7 +2089,7 @@
         FILE_PATH_LITERAL("foo.png"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        "image/png"},
       {{// 1: Empty MIME type in response.
         AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -1998,7 +2099,7 @@
         FILE_PATH_LITERAL("foo.png"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        "image/png"},
       {{// 2: Forced path.
         FORCED, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -2008,7 +2109,7 @@
         FILE_PATH_LITERAL("foo.png"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        "image/png"},
       {{// 3: Unknown file type.
         AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -2018,7 +2119,7 @@
         FILE_PATH_LITERAL("foo.notarealext"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        ""},
       {{// 4: Unknown file type.
         AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -2028,7 +2129,7 @@
         FILE_PATH_LITERAL("foo.notarealext"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        ""},
       {{// 5: x-x509-user-cert mime-type.
         AUTOMATIC, content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
@@ -2038,7 +2139,7 @@
         FILE_PATH_LITERAL("user.crt"),
         DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-        EXPECT_CRDOWNLOAD},
+        EXPECT_CRDOWNLOAD, DEFAULT_DIRECTORY},
        ""},
   };
 
@@ -2075,7 +2176,8 @@
       FILE_PATH_LITERAL(""),
       FILE_PATH_LITERAL("foo.crx"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   const DownloadTestCase& test_case = kUserValidatedTestCase;
   std::unique_ptr<content::MockDownloadItem> item(
@@ -2227,7 +2329,8 @@
       FILE_PATH_LITERAL("foo.fakeext"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   content::PluginService* plugin_service =
       content::PluginService::GetInstance();
@@ -2296,7 +2399,8 @@
       FILE_PATH_LITERAL("foo.fakeext"),
       DownloadItem::TARGET_DISPOSITION_OVERWRITE,
 
-      EXPECT_CRDOWNLOAD};
+      EXPECT_CRDOWNLOAD,
+      DEFAULT_DIRECTORY};
 
   content::PluginService* plugin_service =
       content::PluginService::GetInstance();
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index c8a2423..44d26a7 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -902,6 +902,7 @@
       PackExtensionJob::StandardSuccessMessage(crx_file, pem_file));
   response.status = developer::PACK_STATUS_SUCCESS;
   Respond(OneArgument(response.ToValue()));
+  pack_job_.reset();
   Release();  // Balanced in Run().
 }
 
@@ -919,6 +920,7 @@
     response.status = developer::PACK_STATUS_ERROR;
   }
   Respond(OneArgument(response.ToValue()));
+  pack_job_.reset();
   Release();  // Balanced in Run().
 }
 
@@ -958,8 +960,8 @@
 
   AddRef();  // Balanced in OnPackSuccess / OnPackFailure.
 
-  // TODO(devlin): Why is PackExtensionJob ref-counted?
-  pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags);
+  pack_job_ =
+      base::MakeUnique<PackExtensionJob>(this, root_directory, key_file, flags);
   pack_job_->Start();
   return RespondLater();
 }
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h
index 7580156..84e0e8c 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.h
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -466,7 +466,7 @@
   ResponseAction Run() override;
 
  private:
-  scoped_refptr<PackExtensionJob> pack_job_;
+  std::unique_ptr<PackExtensionJob> pack_job_;
   std::string item_path_str_;
   std::string key_path_str_;
 };
diff --git a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc
index 13c4e52..3e925eae 100644
--- a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc
+++ b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc
@@ -9,7 +9,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_context.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -86,4 +88,10 @@
   return dict;
 }
 
+system_logs::SystemLogsFetcher*
+ChromeFeedbackPrivateDelegate::CreateSystemLogsFetcher(
+    content::BrowserContext* context) const {
+  return system_logs::BuildChromeSystemLogsFetcher();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h
index 0e02e5b..27a881e 100644
--- a/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h
+++ b/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.h
@@ -22,6 +22,8 @@
   std::unique_ptr<base::DictionaryValue> GetStrings(
       content::BrowserContext* browser_context,
       bool from_crash) const override;
+  system_logs::SystemLogsFetcher* CreateSystemLogsFetcher(
+      content::BrowserContext* context) const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeFeedbackPrivateDelegate);
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc b/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc
index 534533b..993f9dc 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_private_api.cc
@@ -27,6 +27,7 @@
 #include "chrome/common/extensions/api/feedback_private.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/feedback/system_logs/system_logs_fetcher.h"
 #include "components/feedback/tracing_manager.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "extensions/browser/api/extensions_api_client.h"
@@ -92,9 +93,9 @@
 FeedbackPrivateAPI::FeedbackPrivateAPI(content::BrowserContext* context)
     : browser_context_(context),
 #if !defined(OS_CHROMEOS)
-      service_(new FeedbackService()) {
+      service_(new FeedbackService(context)) {
 #else
-      service_(new FeedbackService()),
+      service_(new FeedbackService(context)),
       log_source_access_manager_(new LogSourceAccessManager(context)){
 #endif  // defined(OS_CHROMEOS)
 }
@@ -211,13 +212,16 @@
 
 ExtensionFunction::ResponseAction
 FeedbackPrivateGetSystemInformationFunction::Run() {
-  FeedbackService* service = FeedbackPrivateAPI::GetFactoryInstance()
-                                 ->Get(browser_context())
-                                 ->GetService();
-  DCHECK(service);
-  service->GetSystemInformation(
-      base::Bind(
-          &FeedbackPrivateGetSystemInformationFunction::OnCompleted, this));
+  FeedbackPrivateDelegate* feedback_private_delegate =
+      ExtensionsAPIClient::Get()->GetFeedbackPrivateDelegate();
+  DCHECK(feedback_private_delegate);
+
+  // Self-deleting object.
+  system_logs::SystemLogsFetcher* fetcher =
+      feedback_private_delegate->CreateSystemLogsFetcher(browser_context());
+  fetcher->Fetch(base::Bind(
+      &FeedbackPrivateGetSystemInformationFunction::OnCompleted, this));
+
   return RespondLater();
 }
 
@@ -329,7 +333,7 @@
   }
 
   service->SendFeedback(
-      browser_context(), feedback_data,
+      feedback_data,
       base::Bind(&FeedbackPrivateSendFeedbackFunction::OnCompleted, this));
 
   return RespondLater();
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_service.cc b/chrome/browser/extensions/api/feedback_private/feedback_service.cc
index 8d1fcef..7baa7cfd 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_service.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_service.cc
@@ -9,7 +9,6 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
-#include "chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/blob_reader.h"
@@ -22,14 +21,13 @@
 
 namespace extensions {
 
-FeedbackService::FeedbackService() {
-}
+FeedbackService::FeedbackService(content::BrowserContext* browser_context)
+    : browser_context_(browser_context) {}
 
 FeedbackService::~FeedbackService() {
 }
 
-void FeedbackService::SendFeedback(content::BrowserContext* browser_context,
-                                   scoped_refptr<FeedbackData> feedback_data,
+void FeedbackService::SendFeedback(scoped_refptr<FeedbackData> feedback_data,
                                    const SendFeedbackCallback& callback) {
   feedback_data->set_locale(
       ExtensionsBrowserClient::Get()->GetApplicationLocale());
@@ -38,7 +36,7 @@
   if (!feedback_data->attached_file_uuid().empty()) {
     // Self-deleting object.
     BlobReader* attached_file_reader =
-        new BlobReader(browser_context, feedback_data->attached_file_uuid(),
+        new BlobReader(browser_context_, feedback_data->attached_file_uuid(),
                        base::Bind(&FeedbackService::AttachedFileCallback,
                                   AsWeakPtr(), feedback_data, callback));
     attached_file_reader->Start();
@@ -47,7 +45,7 @@
   if (!feedback_data->screenshot_uuid().empty()) {
     // Self-deleting object.
     BlobReader* screenshot_reader =
-        new BlobReader(browser_context, feedback_data->screenshot_uuid(),
+        new BlobReader(browser_context_, feedback_data->screenshot_uuid(),
                        base::Bind(&FeedbackService::ScreenshotCallback,
                                   AsWeakPtr(), feedback_data, callback));
     screenshot_reader->Start();
@@ -56,14 +54,6 @@
   CompleteSendFeedback(feedback_data, callback);
 }
 
-void FeedbackService::GetSystemInformation(
-    const system_logs::SysLogsFetcherCallback& callback) {
-  // Self-deleting object.
-  system_logs::SystemLogsFetcher* fetcher =
-      system_logs::BuildChromeSystemLogsFetcher();
-  fetcher->Fetch(callback);
-}
-
 void FeedbackService::AttachedFileCallback(
     scoped_refptr<feedback::FeedbackData> feedback_data,
     const SendFeedbackCallback& callback,
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_service.h b/chrome/browser/extensions/api/feedback_private/feedback_service.h
index d51ca86f..f6530de 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_service.h
+++ b/chrome/browser/extensions/api/feedback_private/feedback_service.h
@@ -13,11 +13,10 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/feedback/feedback_data.h"
-#include "components/feedback/system_logs/system_logs_fetcher.h"
 
 namespace content {
 class BrowserContext;
-}
+}  // namespace content
 
 namespace extensions {
 
@@ -32,19 +31,13 @@
   // offline).
   using SendFeedbackCallback = base::Callback<void(bool)>;
 
-  FeedbackService();
+  explicit FeedbackService(content::BrowserContext* browser_context);
   virtual ~FeedbackService();
 
   // Sends a feedback report.
-  void SendFeedback(content::BrowserContext* browser_context,
-                    scoped_refptr<feedback::FeedbackData> feedback_data,
+  void SendFeedback(scoped_refptr<feedback::FeedbackData> feedback_data,
                     const SendFeedbackCallback& callback);
 
-  // Start to gather system information.
-  // The |callback| will be invoked once the query is completed.
-  void GetSystemInformation(
-      const system_logs::SysLogsFetcherCallback& callback);
-
  private:
   // Callbacks to receive blob data.
   void AttachedFileCallback(scoped_refptr<feedback::FeedbackData> feedback_data,
@@ -61,6 +54,8 @@
   void CompleteSendFeedback(scoped_refptr<feedback::FeedbackData> feedback_data,
                             const SendFeedbackCallback& callback);
 
+  content::BrowserContext* browser_context_;
+
   DISALLOW_COPY_AND_ASSIGN(FeedbackService);
 };
 
diff --git a/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc b/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
index 103a946..cf7da48 100644
--- a/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
@@ -152,8 +152,11 @@
   drive::DriveIntegrationService* CreateDriveIntegrationService(
       Profile* profile) {
     // Ignore signin profile.
-    if (profile->GetPath() == chromeos::ProfileHelper::GetSigninProfileDir())
+    if (profile->GetPath() == chromeos::ProfileHelper::GetSigninProfileDir() ||
+        profile->GetPath() ==
+            chromeos::ProfileHelper::GetLockScreenAppProfilePath()) {
       return nullptr;
+    }
 
     // FileSystemApiTestForDrive doesn't expect that several user profiles could
     // exist simultaneously.
diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc
index ec76b112..30e3ef8 100644
--- a/chrome/browser/extensions/extension_context_menu_browsertest.cc
+++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc
@@ -76,13 +76,11 @@
     const extensions::ExtensionSet& extensions =
         extensions::ExtensionRegistry::Get(
             browser()->profile())->enabled_extensions();
-    for (extensions::ExtensionSet::const_iterator i = extensions.begin();
-         i != extensions.end(); ++i) {
-      if ((*i)->name() == name) {
-        return i->get();
-      }
+    for (const auto& ext : extensions) {
+      if (ext->name() == name)
+        return ext.get();
     }
-    return NULL;
+    return nullptr;
   }
 
   // This gets all the items that any extension has registered for possible
@@ -178,10 +176,8 @@
   bool MenuHasExtensionItemWithLabel(TestRenderViewContextMenu* menu,
                                      const std::string& label) {
     base::string16 label16 = base::UTF8ToUTF16(label);
-    std::map<int, MenuItem::Id>::iterator i;
-    for (i = menu->extension_items().extension_item_map_.begin();
-         i != menu->extension_items().extension_item_map_.end(); ++i) {
-      const MenuItem::Id& id = i->second;
+    for (const auto& it : menu->extension_items().extension_item_map_) {
+      const MenuItem::Id& id = it.second;
       base::string16 tmp_label;
       EXPECT_TRUE(GetItemLabel(menu, id, &tmp_label));
       if (tmp_label == label16)
@@ -195,12 +191,12 @@
   // false.
   bool GetItemLabel(TestRenderViewContextMenu* menu,
                     const MenuItem::Id& id,
-                    base::string16* result) {
+                    base::string16* result) const {
     int command_id = 0;
     if (!FindCommandId(menu, id, &command_id))
       return false;
 
-    MenuModel* model = NULL;
+    MenuModel* model = nullptr;
     int index = -1;
     if (!menu->GetMenuModelAndItemIndex(command_id, &model, &index)) {
       return false;
@@ -213,12 +209,10 @@
   // in the menu.
   bool FindCommandId(TestRenderViewContextMenu* menu,
                      const MenuItem::Id& id,
-                     int* command_id) {
-    std::map<int, MenuItem::Id>::const_iterator i;
-    for (i = menu->extension_items().extension_item_map_.begin();
-         i != menu->extension_items().extension_item_map_.end(); ++i) {
-      if (i->second == id) {
-        *command_id = i->first;
+                     int* command_id) const {
+    for (const auto& it : menu->extension_items().extension_item_map_) {
+      if (it.second == id) {
+        *command_id = it.first;
         return true;
       }
     }
@@ -312,8 +306,8 @@
   ASSERT_TRUE(listener_update2.WaitUntilSatisfied());
 
   // Rebuild the context menu and click on the second extension item.
-  menu.reset(TestRenderViewContextMenu::Create(GetWebContents(), page_url,
-                                               GURL(), GURL()));
+  menu = TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(),
+                                           GURL());
   id.string_uid = "id2";
   ASSERT_TRUE(FindCommandId(menu.get(), id, &command_id));
   menu->ExecuteCommand(command_id, 0);
@@ -542,7 +536,7 @@
       TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL()));
 
   int index = 0;
-  MenuModel* model = NULL;
+  MenuModel* model = nullptr;
 
   ASSERT_TRUE(menu->GetMenuModelAndItemIndex(
       ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0),
@@ -612,7 +606,7 @@
   // Load the extension.
   ASSERT_TRUE(LoadContextMenuExtension("separators"));
   const extensions::Extension* extension = GetExtensionNamed("Separators Test");
-  ASSERT_TRUE(extension != NULL);
+  ASSERT_TRUE(extension);
 
   // Navigate to test1.html inside the extension, which should create a bunch
   // of items at the top-level (but they'll get pushed into an auto-generated
@@ -628,7 +622,7 @@
 
   // The top-level item should be an "automagic parent" with the extension's
   // name.
-  MenuModel* model = NULL;
+  MenuModel* model = nullptr;
   int index = 0;
   base::string16 label;
   ASSERT_TRUE(menu->GetMenuModelAndItemIndex(
@@ -640,7 +634,7 @@
 
   // Get the submenu and verify the items there.
   MenuModel* submenu = model->GetSubmenuModelAt(index);
-  ASSERT_TRUE(submenu != NULL);
+  ASSERT_TRUE(submenu);
   VerifyMenuForSeparatorsTest(*submenu);
 
   // Now run our second test - navigate to test2.html which creates an explicit
@@ -649,15 +643,15 @@
   ui_test_utils::NavigateToURL(browser(),
                                GURL(extension->GetResourceURL("test2.html")));
   listener2.WaitUntilSatisfied();
-  menu.reset(
-      TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL()));
+  menu =
+      TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL());
   ASSERT_TRUE(menu->GetMenuModelAndItemIndex(
       ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0),
       &model,
       &index));
   EXPECT_EQ(base::UTF8ToUTF16("parent"), model->GetLabelAt(index));
   submenu = model->GetSubmenuModelAt(index);
-  ASSERT_TRUE(submenu != NULL);
+  ASSERT_TRUE(submenu);
   VerifyMenuForSeparatorsTest(*submenu);
 }
 
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 9be8d26..07c82e0 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -1912,18 +1912,20 @@
         temp_dir.GetPath().Append(expected_private_key_names[i]);
     PackExtensionTestClient pack_client(expected_crx_path,
                                         expected_private_key_path);
-    scoped_refptr<extensions::PackExtensionJob> packer(
-        new extensions::PackExtensionJob(&pack_client, output_dir,
-                                         base::FilePath(),
-                                         ExtensionCreator::kOverwriteCRX));
-    packer->Start();
+    {
+      extensions::PackExtensionJob packer(&pack_client, output_dir,
+                                          base::FilePath(),
+                                          ExtensionCreator::kOverwriteCRX);
+      packer.Start();
 
-    // The packer will post a notification task to the current thread's message
-    // loop when it is finished.  We manually run the loop here so that we
-    // block and catch the notification; otherwise, the process would exit.
-    // This call to |Run()| is matched by a call to |Quit()| in the
-    // |PackExtensionTestClient|'s notification handling code.
-    base::RunLoop().Run();
+      // The packer will post a notification task to the current thread's
+      // message loop when it is finished.  We manually run the loop here so
+      // that we block and catch the notification; otherwise, the process would
+      // exit.
+      // This call to |Run()| is matched by a call to |Quit()| in the
+      // |PackExtensionTestClient|'s notification handling code.
+      base::RunLoop().Run();
+    }
 
     if (HasFatalFailure())
       return;
diff --git a/chrome/browser/extensions/pack_extension_job.cc b/chrome/browser/extensions/pack_extension_job.cc
index 330b028..87d07d9 100644
--- a/chrome/browser/extensions/pack_extension_job.cc
+++ b/chrome/browser/extensions/pack_extension_job.cc
@@ -8,8 +8,11 @@
 #include "base/message_loop/message_loop.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/extensions/extension_creator.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/common/constants.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -21,70 +24,92 @@
                                    const base::FilePath& root_directory,
                                    const base::FilePath& key_file,
                                    int run_flags)
-    : client_(client), key_file_(key_file), asynchronous_(true),
+    : client_(client),
+      key_file_(key_file),
       run_flags_(run_flags | ExtensionCreator::kRequireModernManifestVersion) {
   root_directory_ = root_directory.StripTrailingSeparators();
-  CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
-}
-
-void PackExtensionJob::Start() {
-  if (asynchronous_) {
-    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-                            base::BindOnce(&PackExtensionJob::Run, this));
-  } else {
-    Run();
-  }
-}
-
-void PackExtensionJob::ClearClient() {
-  client_ = NULL;
 }
 
 PackExtensionJob::~PackExtensionJob() {}
 
-void PackExtensionJob::Run() {
-  crx_file_out_ = base::FilePath(root_directory_.value() +
-                                 kExtensionFileExtension);
+void PackExtensionJob::Start() {
+  if (run_mode_ == RunMode::ASYNCHRONOUS) {
+    scoped_refptr<base::SequencedTaskRunner> task_runner =
+        base::SequencedTaskRunnerHandle::Get();
+    GetExtensionFileTaskRunner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&PackExtensionJob::Run,
+                       // See class level comments for why Unretained is safe.
+                       base::Unretained(this), std::move(task_runner)));
+  } else {
+    DCHECK_EQ(RunMode::SYNCHRONOUS, run_mode_);
+    Run(nullptr);
+  }
+}
 
-  if (key_file_.empty())
-    key_file_out_ = base::FilePath(root_directory_.value() +
-                                   kExtensionKeyFileExtension);
+void PackExtensionJob::Run(
+    scoped_refptr<base::SequencedTaskRunner> async_reply_task_runner) {
+  DCHECK_EQ(!!async_reply_task_runner, run_mode_ == RunMode::ASYNCHRONOUS)
+      << "Provide task runner iff we are running in asynchronous mode.";
+  // TODO(lazyboy): Use root_directory_.AddExtension(kExtensionFileExtension).
+  auto crx_file_out = base::MakeUnique<base::FilePath>(root_directory_.value() +
+                                                       kExtensionFileExtension);
+
+  auto key_file_out = base::MakeUnique<base::FilePath>();
+  if (key_file_.empty()) {
+    // TODO(lazyboy): Use
+    // root_directory_.AddExtension(kExtensionKeyFileExtension).
+    *key_file_out =
+        base::FilePath(root_directory_.value() + kExtensionKeyFileExtension);
+  }
 
   // TODO(aa): Need to internationalize the errors that ExtensionCreator
   // returns. See bug 20734.
   ExtensionCreator creator;
-  if (creator.Run(root_directory_, crx_file_out_, key_file_, key_file_out_,
+  if (creator.Run(root_directory_, *crx_file_out, key_file_, *key_file_out,
                   run_flags_)) {
-    if (asynchronous_) {
-      BrowserThread::PostTask(
-          client_thread_id_, FROM_HERE,
-          base::BindOnce(&PackExtensionJob::ReportSuccessOnClientThread, this));
+    if (run_mode_ == RunMode::ASYNCHRONOUS) {
+      async_reply_task_runner->PostTask(
+          FROM_HERE,
+          base::BindOnce(&PackExtensionJob::ReportSuccessOnClientSequence,
+                         // See class level comments for why Unretained is safe.
+                         base::Unretained(this), std::move(crx_file_out),
+                         std::move(key_file_out)));
+
     } else {
-      ReportSuccessOnClientThread();
+      ReportSuccessOnClientSequence(std::move(crx_file_out),
+                                    std::move(key_file_out));
     }
   } else {
-    if (asynchronous_) {
-      BrowserThread::PostTask(
-          client_thread_id_, FROM_HERE,
-          base::BindOnce(&PackExtensionJob::ReportFailureOnClientThread, this,
-                         creator.error_message(), creator.error_type()));
+    if (run_mode_ == RunMode::ASYNCHRONOUS) {
+      async_reply_task_runner->PostTask(
+          FROM_HERE,
+          base::BindOnce(&PackExtensionJob::ReportFailureOnClientSequence,
+                         // See class level comments for why Unretained is safe.
+                         base::Unretained(this), creator.error_message(),
+                         creator.error_type()));
     } else {
-      ReportFailureOnClientThread(creator.error_message(),
-          creator.error_type());
+      DCHECK_EQ(RunMode::SYNCHRONOUS, run_mode_);
+      ReportFailureOnClientSequence(creator.error_message(),
+                                    creator.error_type());
     }
   }
 }
 
-void PackExtensionJob::ReportSuccessOnClientThread() {
-  if (client_)
-    client_->OnPackSuccess(crx_file_out_, key_file_out_);
+void PackExtensionJob::ReportSuccessOnClientSequence(
+    std::unique_ptr<base::FilePath> crx_file_out,
+    std::unique_ptr<base::FilePath> key_file_out) {
+  DCHECK(client_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  client_->OnPackSuccess(*crx_file_out, *key_file_out);
 }
 
-void PackExtensionJob::ReportFailureOnClientThread(
+void PackExtensionJob::ReportFailureOnClientSequence(
     const std::string& error,
     ExtensionCreator::ErrorType error_type) {
-  if (client_)
-    client_->OnPackFailure(error, error_type);
+  DCHECK(client_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  client_->OnPackFailure(error, error_type);
 }
 
 // static
diff --git a/chrome/browser/extensions/pack_extension_job.h b/chrome/browser/extensions/pack_extension_job.h
index 7d663b0f..62fdc254 100644
--- a/chrome/browser/extensions/pack_extension_job.h
+++ b/chrome/browser/extensions/pack_extension_job.h
@@ -9,16 +9,20 @@
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/ptr_util.h"
+#include "base/sequenced_task_runner.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/extensions/extension_creator.h"
-#include "content/public/browser/browser_thread.h"
 
 namespace extensions {
 
 // Manages packing an extension on the file thread and reporting the result
 // back to the UI.
-class PackExtensionJob : public base::RefCountedThreadSafe<PackExtensionJob> {
+// Ownership note: In "asynchronous" mode, |Client| has to make sure this
+// class's instances are kept alive until OnPackSuccess|OnPackFailure is called.
+// Therefore this class assumes that posting task with base::Unretained(this)
+// is safe.
+class PackExtensionJob {
  public:
   // Interface for people who want to use PackExtensionJob to implement.
   class Client {
@@ -36,41 +40,38 @@
                    const base::FilePath& root_directory,
                    const base::FilePath& key_file,
                    int run_flags);
+  ~PackExtensionJob();
 
   // Starts the packing job.
   void Start();
 
-  // The client should call this when it is destroyed to prevent
-  // PackExtensionJob from attempting to access it.
-  void ClearClient();
-
   // The standard packing success message.
   static base::string16 StandardSuccessMessage(const base::FilePath& crx_file,
                                          const base::FilePath& key_file);
 
-  void set_asynchronous(bool async) { asynchronous_ = async; }
+  void set_synchronous() { run_mode_ = RunMode::SYNCHRONOUS; }
 
  private:
-  friend class base::RefCountedThreadSafe<PackExtensionJob>;
+  enum class RunMode { SYNCHRONOUS, ASYNCHRONOUS };
 
-  virtual ~PackExtensionJob();
+  // If |run_mode_| is SYNCHRONOUS, this is run on whichever thread calls it.
+  void Run(scoped_refptr<base::SequencedTaskRunner> async_reply_task_runner);
+  void ReportSuccessOnClientSequence(
+      std::unique_ptr<base::FilePath> crx_file_out,
+      std::unique_ptr<base::FilePath> key_file_out);
+  void ReportFailureOnClientSequence(const std::string& error,
+                                     ExtensionCreator::ErrorType error_type);
 
-  // If |asynchronous_| is false, this is run on whichever thread calls it.
-  void Run();
-  void ReportSuccessOnClientThread();
-  void ReportFailureOnClientThread(const std::string& error,
-                                   ExtensionCreator::ErrorType error_type);
-
-  content::BrowserThread::ID client_thread_id_;
-  Client* client_;
+  Client* const client_;  // Owns us.
   base::FilePath root_directory_;
   base::FilePath key_file_;
-  base::FilePath crx_file_out_;
-  base::FilePath key_file_out_;
-  bool asynchronous_;
+  RunMode run_mode_ = RunMode::ASYNCHRONOUS;
   int run_flags_;  // Bitset of ExtensionCreator::RunFlags values - we always
                    // assume kRequireModernManifestVersion, though.
 
+  // Used to check methods that run on |client_|'s sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(PackExtensionJob);
 };
 
diff --git a/chrome/browser/extensions/startup_helper.cc b/chrome/browser/extensions/startup_helper.cc
index fef26b7..4bfaaa1 100644
--- a/chrome/browser/extensions/startup_helper.cc
+++ b/chrome/browser/extensions/startup_helper.cc
@@ -62,12 +62,12 @@
     private_key_path = cmd_line.GetSwitchValuePath(switches::kPackExtensionKey);
   }
 
-  // Launch a job to perform the packing on the file thread.  Ignore warnings
-  // from the packing process. (e.g. Overwrite any existing crx file.)
-  pack_job_ = new PackExtensionJob(this, src_dir, private_key_path,
-                                   ExtensionCreator::kOverwriteCRX);
-  pack_job_->set_asynchronous(false);
-  pack_job_->Start();
+  // Launch a job to perform the packing on the blocking thread.  Ignore
+  // warnings from the packing process. (e.g. Overwrite any existing crx file.)
+  PackExtensionJob pack_job(this, src_dir, private_key_path,
+                            ExtensionCreator::kOverwriteCRX);
+  pack_job.set_synchronous();
+  pack_job.Start();
 
   return pack_job_succeeded_;
 }
@@ -186,9 +186,6 @@
   return success;
 }
 
-StartupHelper::~StartupHelper() {
-  if (pack_job_.get())
-    pack_job_->ClearClient();
-}
+StartupHelper::~StartupHelper() {}
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/startup_helper.h b/chrome/browser/extensions/startup_helper.h
index 43a0d2e..f1f8e37 100644
--- a/chrome/browser/extensions/startup_helper.h
+++ b/chrome/browser/extensions/startup_helper.h
@@ -37,7 +37,6 @@
   bool ValidateCrx(const base::CommandLine& cmd_line, std::string* error);
 
  private:
-  scoped_refptr<PackExtensionJob> pack_job_;
   bool pack_job_succeeded_;
 
   DISALLOW_COPY_AND_ASSIGN(StartupHelper);
diff --git a/chrome/browser/feature_engagement/feature_tracker.cc b/chrome/browser/feature_engagement/feature_tracker.cc
new file mode 100644
index 0000000..f98a607
--- /dev/null
+++ b/chrome/browser/feature_engagement/feature_tracker.cc
@@ -0,0 +1,54 @@
+// 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/feature_engagement/feature_tracker.h"
+
+#include "base/time/time.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "components/feature_engagement/public/event_constants.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/feature_engagement/public/tracker.h"
+
+namespace feature_engagement {
+
+FeatureTracker::FeatureTracker(Profile* profile,
+                               SessionDurationUpdater* session_duration_updater)
+    : profile_(profile),
+      session_duration_updater_(session_duration_updater),
+      session_duration_observer_(this) {
+  AddSessionDurationObserver();
+}
+
+FeatureTracker::~FeatureTracker() = default;
+
+void FeatureTracker::AddSessionDurationObserver() {
+  session_duration_observer_.Add(session_duration_updater_);
+}
+
+void FeatureTracker::RemoveSessionDurationObserver() {
+  session_duration_observer_.Remove(session_duration_updater_);
+}
+
+bool FeatureTracker::IsObserving() {
+  return session_duration_observer_.IsObserving(session_duration_updater_);
+}
+
+Tracker* FeatureTracker::GetTracker() const {
+  return TrackerFactory::GetForBrowserContext(profile_);
+}
+
+void FeatureTracker::OnSessionEnded(base::TimeDelta total_session_time) {
+  if (HasEnoughSessionTimeElapsed(total_session_time)) {
+    OnSessionTimeMet();
+    RemoveSessionDurationObserver();
+  }
+}
+
+bool FeatureTracker::HasEnoughSessionTimeElapsed(
+    base::TimeDelta total_session_time) {
+  return total_session_time.InMinutes() >=
+         GetSessionTimeRequiredToShowInMinutes();
+}
+
+}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/feature_tracker.h b/chrome/browser/feature_engagement/feature_tracker.h
new file mode 100644
index 0000000..295f8c3
--- /dev/null
+++ b/chrome/browser/feature_engagement/feature_tracker.h
@@ -0,0 +1,81 @@
+// 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_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
+#define CHROME_BROWSER_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
+
+#include "base/scoped_observer.h"
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace feature_engagement {
+
+class Tracker;
+
+// The rFeatureTracker provides a backend for displaying in-product help for the
+// various features by collecting all common funcitonality. All subclasses of
+// FeatureTracker's factories depend on
+// SessionDurationUpdaterFactory::GetInstance() as SessionDurationUpdater is
+// responsible for letting all FeatureTrackers know how much active session time
+// has passed.
+//
+// SessionDurationUpdater keeps track of the observed session time and, upon
+// each session ending, updates all of the FeatureTrackers with the new total
+// observed session time. Once the observed session time exceeds the time
+// requirement provided by GetSessionTimeRequiredToShowInMinutes(), the
+// FeatureTracker unregisters itself as an obsever of SessionDurationUpdater.
+// SessionDurationUpdater stops updating the observed session time if no
+// features are observing it, and will start tracking the observed sesion time
+// again if another feature is added as an observer later.
+class FeatureTracker : public SessionDurationUpdater::Observer,
+                       public KeyedService {
+ public:
+  FeatureTracker(Profile* profile,
+                 SessionDurationUpdater* session_duration_updater);
+
+  // Adds the SessionDurationUpdater observer.
+  void AddSessionDurationObserver();
+  // Removes the SessionDurationUpdater observer.
+  void RemoveSessionDurationObserver();
+
+  // Returns the whether |session_duration_observer_| is observing sources for
+  // testing purposes.
+  bool IsObserving();
+
+  // SessionDurationUpdater::Observer:
+  void OnSessionEnded(base::TimeDelta total_session_time) override;
+
+ protected:
+  ~FeatureTracker() override;
+  // Returns the required session time in minutes for the FeatureTracker's
+  // subclass to show its promo.
+  virtual int GetSessionTimeRequiredToShowInMinutes() = 0;
+  // Alerts the feature tracker that the session time is up.
+  virtual void OnSessionTimeMet() = 0;
+  // Returns the Tracker associated with this FeatureTracker.
+  virtual Tracker* GetTracker() const;
+
+ private:
+  // Returns whether the active session time of a user has elapsed more than the
+  // required active session time for the feature.
+  bool HasEnoughSessionTimeElapsed(base::TimeDelta total_session_time);
+
+  // Owned by the ProfileManager.
+  Profile* const profile_;
+
+  // Singleton instance of KeyedService.
+  SessionDurationUpdater* const session_duration_updater_;
+
+  // Observes the SessionDurationUpdater and notifies when a desktop session
+  // starts and ends.
+  ScopedObserver<SessionDurationUpdater, SessionDurationUpdater::Observer>
+      session_duration_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeatureTracker);
+};
+
+}  // namespace feature_engagement
+
+#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
diff --git a/chrome/browser/feature_engagement/feature_tracker_unittest.cc b/chrome/browser/feature_engagement/feature_tracker_unittest.cc
new file mode 100644
index 0000000..85ef03d
--- /dev/null
+++ b/chrome/browser/feature_engagement/feature_tracker_unittest.cc
@@ -0,0 +1,131 @@
+// 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/feature_engagement/feature_tracker.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_param_associator.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
+#include "chrome/browser/feature_engagement/session_duration_updater_factory.h"
+#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/feature_engagement/public/event_constants.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/variations/variations_params_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feature_engagement {
+
+namespace {
+
+const int kTestTimeInMinutes = 100;
+const int kTestTimeSufficentInMinutes = 110;
+const int kTestTimeInsufficientInMinutes = 90;
+const char kTestProfileName[] = "test-profile";
+
+class TestFeatureTracker : public FeatureTracker {
+ public:
+  explicit TestFeatureTracker(Profile* profile)
+      : FeatureTracker(
+            profile,
+            feature_engagement::SessionDurationUpdaterFactory::GetInstance()
+                ->GetForProfile(profile)),
+        pref_service_(
+            base::MakeUnique<sync_preferences::TestingPrefServiceSyncable>()) {
+    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
+  }
+
+  int GetSessionTimeRequiredToShowInMinutes() override {
+    return kTestTimeInMinutes;
+  }
+
+  void OnSessionTimeMet() override {}
+
+ private:
+  const std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
+      pref_service_;
+};
+
+class MockTestFeatureTracker : public TestFeatureTracker {
+ public:
+  explicit MockTestFeatureTracker(Profile* profile)
+      : TestFeatureTracker(profile) {}
+  MOCK_METHOD0(OnSessionTimeMet, void());
+};
+
+class FeatureTrackerTest : public testing::Test {
+ public:
+  FeatureTrackerTest() = default;
+  ~FeatureTrackerTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    // Start the DesktopSessionDurationTracker to track active session time.
+    metrics::DesktopSessionDurationTracker::Initialize();
+    testing_profile_manager_ = base::MakeUnique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(testing_profile_manager_->SetUp());
+    mock_feature_tracker_ =
+        base::MakeUnique<testing::StrictMock<MockTestFeatureTracker>>(
+            testing_profile_manager_->CreateTestingProfile(kTestProfileName));
+  }
+
+  void TearDown() override {
+    metrics::DesktopSessionDurationTracker::CleanupForTesting();
+    testing_profile_manager_.reset();
+  }
+
+ protected:
+  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
+  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker_;
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeatureTrackerTest);
+};
+
+// If OnSessionEnded parameter is greater than HasEnoughSessionTimeElapsed
+// then OnSessionTimeMet should be called.
+//
+// Note: in this case, RemoveSessionDurationObserver is called inside of
+// OnSessionTimeMet, so it doesn't need to be called after the fact.
+TEST_F(FeatureTrackerTest, TestExpectOnSessionTimeMet) {
+  EXPECT_CALL(*mock_feature_tracker_, OnSessionTimeMet());
+  mock_feature_tracker_.get()->OnSessionEnded(
+      base::TimeDelta::FromMinutes(kTestTimeSufficentInMinutes));
+}
+
+// If OnSessionEnded parameter is less than than HasEnoughSessionTimeElapsed
+// then OnSessionTimeMet should not be called.
+TEST_F(FeatureTrackerTest, TestDontExpectOnSessionTimeMet) {
+  mock_feature_tracker_.get()->OnSessionEnded(
+      base::TimeDelta::FromMinutes(kTestTimeInsufficientInMinutes));
+  mock_feature_tracker_.get()->RemoveSessionDurationObserver();
+}
+
+// The FeatureTracker should be observing sources until the
+// RemoveSessionDurationObserver is called.
+TEST_F(FeatureTrackerTest, TestAddAndRemoveObservers) {
+  // AddSessionDurationObserver is called on initialization.
+  ASSERT_TRUE(mock_feature_tracker_->IsObserving());
+
+  mock_feature_tracker_.get()->RemoveSessionDurationObserver();
+
+  EXPECT_FALSE(mock_feature_tracker_->IsObserving());
+}
+
+}  // namespace
+
+}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
index e735dde..fc57d4f0 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
@@ -14,8 +14,6 @@
 #include "components/feature_engagement/public/event_constants.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/tracker.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
 
 namespace {
 
@@ -25,31 +23,21 @@
 
 namespace feature_engagement {
 
-NewTabTracker::NewTabTracker(Profile* profile)
-    : profile_(profile), duration_tracker_observer_(this) {
-  AddDurationTrackerObserver();
-}
+NewTabTracker::NewTabTracker(Profile* profile,
+                             SessionDurationUpdater* session_duration_updater)
+    : FeatureTracker(profile, session_duration_updater) {}
 
-NewTabTracker::NewTabTracker() : NewTabTracker(nullptr) {}
+NewTabTracker::NewTabTracker(SessionDurationUpdater* session_duration_updater)
+    : NewTabTracker(nullptr, session_duration_updater) {}
 
 NewTabTracker::~NewTabTracker() = default;
 
-// static
-void NewTabTracker::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterIntegerPref(prefs::kSessionTimeTotal, 0);
-}
-
 void NewTabTracker::OnNewTabOpened() {
-  GetFeatureTracker()->NotifyEvent(events::kNewTabOpened);
+  GetTracker()->NotifyEvent(events::kNewTabOpened);
 }
 
 void NewTabTracker::OnOmniboxNavigation() {
-  GetFeatureTracker()->NotifyEvent(events::kOmniboxInteraction);
-}
-
-void NewTabTracker::OnSessionTimeMet() {
-  GetFeatureTracker()->NotifyEvent(events::kSessionTime);
+  GetTracker()->NotifyEvent(events::kOmniboxInteraction);
 }
 
 void NewTabTracker::OnOmniboxFocused() {
@@ -58,61 +46,23 @@
 }
 
 void NewTabTracker::OnPromoClosed() {
-  GetFeatureTracker()->Dismissed(kIPHNewTabFeature);
+  GetTracker()->Dismissed(kIPHNewTabFeature);
 }
 
 bool NewTabTracker::ShouldShowPromo() {
-  return GetFeatureTracker()->ShouldTriggerHelpUI(kIPHNewTabFeature);
+  return GetTracker()->ShouldTriggerHelpUI(kIPHNewTabFeature);
 }
 
-void NewTabTracker::AddDurationTrackerObserver() {
-  duration_tracker_observer_.Add(metrics::DesktopSessionDurationTracker::Get());
+void NewTabTracker::OnSessionTimeMet() {
+  GetTracker()->NotifyEvent(events::kNewTabSessionTimeMet);
 }
 
-void NewTabTracker::RemoveDurationTrackerObserver() {
-  duration_tracker_observer_.Remove(
-      metrics::DesktopSessionDurationTracker::Get());
-}
-
-bool NewTabTracker::HasEnoughSessionTimeElapsed() {
-  return GetPrefs()->GetInteger(prefs::kSessionTimeTotal) >= kTwoHoursInMinutes;
+int NewTabTracker::GetSessionTimeRequiredToShowInMinutes() {
+  return kTwoHoursInMinutes;
 }
 
 void NewTabTracker::ShowPromo() {
   NewTabButton::ShowPromoForLastActiveBrowser();
 }
 
-Tracker* NewTabTracker::GetFeatureTracker() {
-  return TrackerFactory::GetForBrowserContext(profile_);
-}
-
-PrefService* NewTabTracker::GetPrefs() {
-  return profile_->GetPrefs();
-}
-
-void NewTabTracker::UpdateSessionTime(base::TimeDelta elapsed) {
-  // Session time does not need to be tracked anymore if the
-  // in-product help has been shown already.
-  // This prevents unnecessary interaction with prefs.
-  if (GetFeatureTracker()->GetTriggerState(kIPHNewTabFeature) ==
-      Tracker::TriggerState::HAS_BEEN_DISPLAYED) {
-    return;
-  }
-
-  base::TimeDelta elapsed_session_time;
-  elapsed_session_time += base::TimeDelta::FromMinutes(GetPrefs()->GetInteger(
-                              prefs::kSessionTimeTotal)) +
-                          elapsed;
-  GetPrefs()->SetInteger(prefs::kSessionTimeTotal,
-                         elapsed_session_time.InMinutes());
-}
-
-void NewTabTracker::OnSessionEnded(base::TimeDelta delta) {
-  UpdateSessionTime(delta);
-  if (HasEnoughSessionTimeElapsed()) {
-    OnSessionTimeMet();
-    RemoveDurationTrackerObserver();
-  }
-}
-
 }  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
index 5cb02f8..19d8f7cc 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
@@ -5,33 +5,34 @@
 #ifndef CHROME_BROWSER_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_H_
 #define CHROME_BROWSER_FEATURE_ENGAGEMENT_NEW_TAB_NEW_TAB_TRACKER_H_
 
-#include "base/scoped_observer.h"
-#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/feature_engagement/public/tracker.h"
-#include "components/keyed_service/core/keyed_service.h"
+#include "chrome/browser/feature_engagement/feature_tracker.h"
 
-namespace user_prefs {
-class PrefRegistrySyncable;
-}  // namespace user_prefs
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
+#include "chrome/browser/feature_engagement/session_duration_updater_factory.h"
 
 namespace feature_engagement {
 
-// The NewTabTracker provides a backend for displaying
-// in-product help for the new tab button.
-class NewTabTracker : public metrics::DesktopSessionDurationTracker::Observer,
-                      public KeyedService {
+// The NewTabTracker provides a backend for displaying in-product help for the
+// new tab button. NewTabTracker is the interface through which the event
+// constants for the NewTab feature can be be altered. Once all of the event
+// constants are met, NewTabTracker calls for the NewTabPromo to be shown, along
+// with recording when the NewTabPromo is dimissed. The requirements to show
+// the NewTabPromo are as follows:
+//
+// - At least two hours of observed session time have elapsed.
+// - The user has never opened another tab through any means.
+// - The user has navigated away from the start home screen.
+// - The omnibox is in focus, which implies the user is intending on navigating
+//   to a new page.
+class NewTabTracker : public FeatureTracker {
  public:
-  explicit NewTabTracker(Profile* profile);
-
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+  NewTabTracker(Profile* profile,
+                SessionDurationUpdater* session_duration_updater);
 
   // Alerts the new tab tracker that a new tab was opened.
   void OnNewTabOpened();
   // Alerts the new tab tracker that the omnibox has been used.
   void OnOmniboxNavigation();
-  // Alerts the new tab tracker that the session time is up.
-  void OnSessionTimeMet();
   // Checks if the promo should be displayed since the omnibox is on focus.
   void OnOmniboxFocused();
   // Clears the flag for whether there is any in-product help being displayed.
@@ -39,44 +40,24 @@
   // Returns whether or not the promo should be displayed.
   bool ShouldShowPromo();
 
-  // Adding the DesktopSessionDurationTracker observer.
-  void AddDurationTrackerObserver();
-  // Removing the DesktopSessionDurationTracker observer.
-  void RemoveDurationTrackerObserver();
-
  protected:
-  NewTabTracker();
+  // Alternate constructor to support unit testing.
+  explicit NewTabTracker(SessionDurationUpdater* session_duration_updater);
   ~NewTabTracker() override;
 
  private:
-  // Returns whether the active session time of a user has elapsed
-  // more than two hours.
-  bool HasEnoughSessionTimeElapsed();
+  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerEventTest, TestOnSessionTimeMet);
+  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerTest, TestShouldNotShowPromo);
+  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerTest, TestShouldShowPromo);
+  FRIEND_TEST_ALL_PREFIXES(NewTabTrackerBrowserTest, TestShowPromo);
+
+  // FeatureTracker:
+  int GetSessionTimeRequiredToShowInMinutes() override;
+  void OnSessionTimeMet() override;
 
   // Sets the NewTabInProductHelp pref to true and calls the New Tab Promo.
   void ShowPromo();
 
-  // Virtual to support mocking by unit tests.
-  virtual Tracker* GetFeatureTracker();
-
-  virtual PrefService* GetPrefs();
-
-  // Updates the pref that stores active session time per user unless the
-  // new tab in-product help has been displayed already.
-  void UpdateSessionTime(base::TimeDelta elapsed);
-
-  // metrics::DesktopSessionDurationTracker::Observer::
-  void OnSessionEnded(base::TimeDelta delta) override;
-
-  // Owned by ProfileManager.
-  Profile* const profile_;
-
-  // Observes the DesktopSessionDurationTracker and notifies when a desktop
-  // session starts and ends.
-  ScopedObserver<metrics::DesktopSessionDurationTracker,
-                 metrics::DesktopSessionDurationTracker::Observer>
-      duration_tracker_observer_;
-
   DISALLOW_COPY_AND_ASSIGN(NewTabTracker);
 };
 
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc
index 9c638fb..ca4c502 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_browsertest.cc
@@ -89,7 +89,8 @@
 
 IN_PROC_BROWSER_TEST_F(NewTabTrackerBrowserTest, TestShowPromo) {
   // Bypassing the 2 hour active session time requirement.
-  EXPECT_CALL(*feature_engagement_tracker_, NotifyEvent(events::kSessionTime));
+  EXPECT_CALL(*feature_engagement_tracker_,
+              NotifyEvent(events::kNewTabSessionTimeMet));
   NewTabTrackerFactory::GetInstance()
       ->GetForProfile(browser()->profile())
       ->OnSessionTimeMet();
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.cc
index 91f85ea..1f48ca5 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.cc
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.cc
@@ -6,6 +6,8 @@
 
 #include "base/memory/singleton.h"
 #include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
+#include "chrome/browser/feature_engagement/session_duration_updater_factory.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -28,6 +30,7 @@
     : BrowserContextKeyedServiceFactory(
           "NewTabTracker",
           BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(SessionDurationUpdaterFactory::GetInstance());
   DependsOn(TrackerFactory::GetInstance());
 }
 
@@ -35,7 +38,10 @@
 
 KeyedService* NewTabTrackerFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return new NewTabTracker(Profile::FromBrowserContext(context));
+  return new NewTabTracker(
+      Profile::FromBrowserContext(context),
+      feature_engagement::SessionDurationUpdaterFactory::GetInstance()
+          ->GetForProfile(Profile::FromBrowserContext(context)));
 }
 
 content::BrowserContext* NewTabTrackerFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
index d5e12872..c838edf 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
@@ -11,8 +11,14 @@
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/feature_engagement/feature_tracker.h"
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
+#include "chrome/browser/feature_engagement/session_duration_updater_factory.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
 #include "components/feature_engagement/public/event_constants.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/tracker.h"
@@ -29,6 +35,7 @@
 
 const char kGroupName[] = "Enabled";
 const char kNewTabTrialName[] = "NewTabTrial";
+const char kTestProfileName[] = "test-profile";
 
 class MockTracker : public Tracker {
  public:
@@ -44,17 +51,20 @@
 
 class FakeNewTabTracker : public NewTabTracker {
  public:
-  explicit FakeNewTabTracker(Tracker* feature_tracker)
-      : feature_tracker_(feature_tracker),
+  FakeNewTabTracker(Tracker* feature_tracker, Profile* profile)
+      : NewTabTracker(
+            feature_engagement::SessionDurationUpdaterFactory::GetInstance()
+                ->GetForProfile(profile)),
+        feature_tracker_(feature_tracker),
         pref_service_(
             base::MakeUnique<sync_preferences::TestingPrefServiceSyncable>()) {
-    NewTabTracker::RegisterProfilePrefs(pref_service_->registry());
+    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
   }
 
-  // feature_engagement::NewTabTracker::
-  Tracker* GetFeatureTracker() override { return feature_tracker_; }
+  PrefService* GetPrefs() { return pref_service_.get(); }
 
-  PrefService* GetPrefs() override { return pref_service_.get(); }
+  // feature_engagement::NewTabTracker:
+  Tracker* GetTracker() const override { return feature_tracker_; }
 
  private:
   Tracker* const feature_tracker_;
@@ -71,18 +81,25 @@
   void SetUp() override {
     // Start the DesktopSessionDurationTracker to track active session time.
     metrics::DesktopSessionDurationTracker::Initialize();
+    testing_profile_manager_ = base::MakeUnique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(testing_profile_manager_->SetUp());
     mock_tracker_ = base::MakeUnique<testing::StrictMock<MockTracker>>();
-    new_tab_tracker_ = base::MakeUnique<FakeNewTabTracker>(mock_tracker_.get());
+    new_tab_tracker_ = base::MakeUnique<FakeNewTabTracker>(
+        mock_tracker_.get(),
+        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
   }
 
   void TearDown() override {
-    new_tab_tracker_->RemoveDurationTrackerObserver();
+    new_tab_tracker_->RemoveSessionDurationObserver();
     metrics::DesktopSessionDurationTracker::CleanupForTesting();
+    testing_profile_manager_.reset();
   }
 
  protected:
-  std::unique_ptr<FakeNewTabTracker> new_tab_tracker_;
+  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
   std::unique_ptr<MockTracker> mock_tracker_;
+  std::unique_ptr<FakeNewTabTracker> new_tab_tracker_;
 
  private:
   content::TestBrowserThreadBundle thread_bundle_;
@@ -111,7 +128,7 @@
 // If OnSessionTimeMet() is called, the feature_engagement::Tracker
 // receives the kSessionTime event.
 TEST_F(NewTabTrackerEventTest, TestOnSessionTimeMet) {
-  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kSessionTime));
+  EXPECT_CALL(*mock_tracker_, NotifyEvent(events::kNewTabSessionTimeMet));
   new_tab_tracker_->OnSessionTimeMet();
 }
 
@@ -144,8 +161,8 @@
         "name:new_tab_opened;comparator:==0;window:3650;storage:3650";
     new_tab_params["event_omnibox_used"] =
         "name:omnibox_used;comparator:>=1;window:3650;storage:3650";
-    new_tab_params["event_session_time"] =
-        "name:session_time;comparator:>=1;window:3650;storage:3650";
+    new_tab_params["event_new_tab_session_time_met"] =
+        "name:new_tab_session_time_met;comparator:>=1;window:3650;storage:3650";
     new_tab_params["event_trigger"] =
         "name:new_tab_trigger;comparator:any;window:3650;storage:3650";
     new_tab_params["event_used"] =
@@ -158,10 +175,15 @@
     // Start the DesktopSessionDurationTracker to track active session time.
     metrics::DesktopSessionDurationTracker::Initialize();
 
+    testing_profile_manager_ = base::MakeUnique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(testing_profile_manager_->SetUp());
+
     feature_engagement_tracker_ = CreateTestTracker();
 
-    new_tab_tracker_ =
-        base::MakeUnique<FakeNewTabTracker>(feature_engagement_tracker_.get());
+    new_tab_tracker_ = base::MakeUnique<FakeNewTabTracker>(
+        feature_engagement_tracker_.get(),
+        testing_profile_manager_->CreateTestingProfile(kTestProfileName));
 
     // The feature engagement tracker does async initialization.
     base::RunLoop().RunUntilIdle();
@@ -169,7 +191,8 @@
   }
 
   void TearDown() override {
-    new_tab_tracker_->RemoveDurationTrackerObserver();
+    new_tab_tracker_->RemoveSessionDurationObserver();
+    testing_profile_manager_->DeleteTestingProfile(kTestProfileName);
     metrics::DesktopSessionDurationTracker::CleanupForTesting();
 
     // This is required to ensure each test can define its own params.
@@ -194,6 +217,7 @@
   variations::testing::VariationParamsManager params_manager_;
 
  private:
+  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
   base::test::ScopedFeatureList scoped_feature_list_;
   content::TestBrowserThreadBundle thread_bundle_;
   std::map<std::string, base::FieldTrial*> trials_;
@@ -232,22 +256,4 @@
   EXPECT_TRUE(new_tab_tracker_->ShouldShowPromo());
 }
 
-// Test that the correct duration of session is being recorded.
-TEST_F(NewTabTrackerTest, TestOnSessionEnded) {
-  metrics::DesktopSessionDurationTracker::Observer* observer =
-      dynamic_cast<FakeNewTabTracker*>(new_tab_tracker_.get());
-
-  // Simulate passing 30 active minutes.
-  observer->OnSessionEnded(base::TimeDelta::FromMinutes(30));
-
-  EXPECT_EQ(30,
-            new_tab_tracker_->GetPrefs()->GetInteger(prefs::kSessionTimeTotal));
-
-  // Simulate passing 50 minutes.
-  observer->OnSessionEnded(base::TimeDelta::FromMinutes(50));
-
-  EXPECT_EQ(80,
-            new_tab_tracker_->GetPrefs()->GetInteger(prefs::kSessionTimeTotal));
-}
-
 }  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/session_duration_updater.cc b/chrome/browser/feature_engagement/session_duration_updater.cc
new file mode 100644
index 0000000..46468ac
--- /dev/null
+++ b/chrome/browser/feature_engagement/session_duration_updater.cc
@@ -0,0 +1,85 @@
+// 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/feature_engagement/session_duration_updater.h"
+
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/common/pref_names.h"
+#include "components/feature_engagement/public/event_constants.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+
+namespace feature_engagement {
+
+SessionDurationUpdater::SessionDurationUpdater(PrefService* pref_service)
+    : duration_tracker_observer_(this), pref_service_(pref_service) {
+  AddDurationTrackerObserver();
+}
+
+SessionDurationUpdater::~SessionDurationUpdater() = default;
+
+// static
+void SessionDurationUpdater::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterIntegerPref(prefs::kObservedSessionTime, 0);
+}
+
+void SessionDurationUpdater::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+
+  // Re-adds SessionDurationUpdater as an observer of
+  // DesktopSessionDurationTracker if another feature is added after
+  // SessionDurationUpdater was removed.
+  if (!duration_tracker_observer_.IsObserving(
+          metrics::DesktopSessionDurationTracker::Get()))
+    AddDurationTrackerObserver();
+}
+
+void SessionDurationUpdater::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+  // If all the observer Features have removed themselves due to their active
+  // time limits have been reached, the SessionDurationUpdater removes itself
+  // as an observer of DesktopSessionDurationTracker.
+  if (!observer_list_.might_have_observers())
+    RemoveDurationTrackerObserver();
+}
+
+void SessionDurationUpdater::OnSessionEnded(base::TimeDelta elapsed) {
+  // This case is only used during testing as that is the only case that
+  // DesktopSessionDurationTracker isn't calling this on its observer.
+  if (!duration_tracker_observer_.IsObserving(
+          metrics::DesktopSessionDurationTracker::Get())) {
+    return;
+  }
+
+  base::TimeDelta elapsed_session_time;
+
+  elapsed_session_time +=
+      base::TimeDelta::FromMinutes(
+          pref_service_->GetInteger(prefs::kObservedSessionTime)) +
+      elapsed;
+  pref_service_->SetInteger(prefs::kObservedSessionTime,
+                            elapsed_session_time.InMinutes());
+
+  for (Observer& observer : observer_list_)
+    observer.OnSessionEnded(elapsed_session_time);
+}
+
+void SessionDurationUpdater::AddDurationTrackerObserver() {
+  duration_tracker_observer_.Add(metrics::DesktopSessionDurationTracker::Get());
+}
+
+void SessionDurationUpdater::RemoveDurationTrackerObserver() {
+  duration_tracker_observer_.Remove(
+      metrics::DesktopSessionDurationTracker::Get());
+}
+
+PrefService* SessionDurationUpdater::GetPrefs() {
+  return pref_service_;
+}
+
+}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/session_duration_updater.h b/chrome/browser/feature_engagement/session_duration_updater.h
new file mode 100644
index 0000000..df41ac31
--- /dev/null
+++ b/chrome/browser/feature_engagement/session_duration_updater.h
@@ -0,0 +1,87 @@
+// 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_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_H_
+#define CHROME_BROWSER_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_H_
+
+#include "base/scoped_observer.h"
+#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_service.h"
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}  // namespace user_prefs
+
+namespace feature_engagement {
+
+// The SessionDurationUpdater tracks the total amount of observed time across
+// Chrome restarts. Observed time in this context is active session time that
+// occurs while there is a FeatureTracker whose active session time requirement
+// has not been satisfied. This allows subclasses of FeatureTracker to check how
+// much active time has passed, and decide whether to show their respective
+// promos accordingly.
+//
+// When an active session is closed, DesktopSessionDurationTracker calls
+// OnSessionEnded and the observed time is incremented and persisted. Then,
+// OnSessionEnded is called on all of Features observing SessionDurationUpdater.
+// If the feature has its time limit exceeded, it should remove itself as an
+// observer of SessionDurationUpdater. If SessionDurationUpdater has no
+// observers, it means that the time limits of all the observing features has
+// been exceeded, so the SessionDurationUpdater removes itself as an observer of
+// DesktopSessionDurationTracker and stops updating observed session time.
+//
+// If all observers are removed, SessionDurationUpdater doesn't continue
+// updating the observed session time. However, if another feature is added as
+// an observer later, SessionDurationUpdater starts incrementing the observed
+// session time again.
+
+class SessionDurationUpdater
+    : public metrics::DesktopSessionDurationTracker::Observer,
+      public KeyedService {
+ public:
+  // The methods for the observer will be called on the UI thread.
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+    virtual void OnSessionEnded(base::TimeDelta total_session_time) = 0;
+  };
+
+  explicit SessionDurationUpdater(PrefService* pref_service);
+  ~SessionDurationUpdater() override;
+
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+  // For observing the status of the session tracker.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // metrics::DesktopSessionDurationtracker::Observer:
+  void OnSessionEnded(base::TimeDelta delta) override;
+
+ private:
+  // Adds the DesktopSessionDurationTracker observer.
+  void AddDurationTrackerObserver();
+  // Removes the DesktopSessionDurationTracker observer.
+  void RemoveDurationTrackerObserver();
+  // Returns the pref service associated with this SessionDurationUpdater.
+  virtual PrefService* GetPrefs();
+
+  // Observes the DesktopSessionDurationTracker and notifies when a desktop
+  // session starts and ends.
+  ScopedObserver<metrics::DesktopSessionDurationTracker,
+                 metrics::DesktopSessionDurationTracker::Observer>
+      duration_tracker_observer_;
+
+  // Owned by Profile manager.
+  PrefService* const pref_service_;
+
+  base::ObserverList<Observer> observer_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionDurationUpdater);
+};
+
+}  // namespace feature_engagement
+
+#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_H_
diff --git a/chrome/browser/feature_engagement/session_duration_updater_factory.cc b/chrome/browser/feature_engagement/session_duration_updater_factory.cc
new file mode 100644
index 0000000..8e78ac8c
--- /dev/null
+++ b/chrome/browser/feature_engagement/session_duration_updater_factory.cc
@@ -0,0 +1,46 @@
+// 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/feature_engagement/session_duration_updater_factory.h"
+
+#include "base/memory/singleton.h"
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_context.h"
+
+namespace feature_engagement {
+
+// static
+SessionDurationUpdaterFactory* SessionDurationUpdaterFactory::GetInstance() {
+  return base::Singleton<SessionDurationUpdaterFactory>::get();
+}
+
+SessionDurationUpdater* SessionDurationUpdaterFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<SessionDurationUpdater*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+SessionDurationUpdaterFactory::SessionDurationUpdaterFactory()
+    : BrowserContextKeyedServiceFactory(
+          "SessionDurationUpdater",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+SessionDurationUpdaterFactory::~SessionDurationUpdaterFactory() = default;
+
+KeyedService* SessionDurationUpdaterFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new SessionDurationUpdater(
+      Profile::FromBrowserContext(context)->GetPrefs());
+}
+
+content::BrowserContext* SessionDurationUpdaterFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
+
+}  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/session_duration_updater_factory.h b/chrome/browser/feature_engagement/session_duration_updater_factory.h
new file mode 100644
index 0000000..55f1f2be
--- /dev/null
+++ b/chrome/browser/feature_engagement/session_duration_updater_factory.h
@@ -0,0 +1,53 @@
+// 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_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_FACTORY_H_
+#define CHROME_BROWSER_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_FACTORY_H_
+
+#include "base/macros.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace feature_engagement {
+
+class SessionDurationUpdater;
+
+// SessionDurationUpdaterFactory is the main client class for interaction with
+// the SessionDurationUpdater component.
+class SessionDurationUpdaterFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  // Returns singleton instance of SessionDurationUpdaterFactory.
+  static SessionDurationUpdaterFactory* GetInstance();
+
+  // Returns the SessionDurationUpdater associated with the profile.
+  SessionDurationUpdater* GetForProfile(Profile* profile);
+
+ private:
+  friend struct base::DefaultSingletonTraits<SessionDurationUpdaterFactory>;
+
+  SessionDurationUpdaterFactory();
+  ~SessionDurationUpdaterFactory() override;
+
+  // BrowserContextKeyedServiceFactory overrides:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionDurationUpdaterFactory);
+};
+
+}  // namespace feature_engagement
+
+#endif  // CHROME_BROWSER_FEATURE_ENGAGEMENT_SESSION_DURATION_UPDATER_FACTORY_H_
diff --git a/chrome/browser/feature_engagement/session_duration_updater_unittest.cc b/chrome/browser/feature_engagement/session_duration_updater_unittest.cc
new file mode 100644
index 0000000..b0740cb5
--- /dev/null
+++ b/chrome/browser/feature_engagement/session_duration_updater_unittest.cc
@@ -0,0 +1,146 @@
+// 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/feature_engagement/session_duration_updater.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_param_associator.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/feature_engagement/feature_tracker.h"
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
+#include "chrome/browser/feature_engagement/session_duration_updater_factory.h"
+#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "components/feature_engagement/public/event_constants.h"
+#include "components/feature_engagement/public/feature_constants.h"
+#include "components/feature_engagement/test/test_tracker.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace feature_engagement {
+
+namespace {
+
+class TestObserver : public SessionDurationUpdater::Observer {
+ public:
+  TestObserver()
+      : pref_service_(
+            base::MakeUnique<sync_preferences::TestingPrefServiceSyncable>()),
+        session_duration_updater_(
+            new SessionDurationUpdater(pref_service_.get())),
+        session_duration_observer_(this) {
+    SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
+  }
+
+  void AddSessionDurationObserver() {
+    session_duration_observer_.Add(session_duration_updater_.get());
+  }
+
+  void RemoveSessionDurationObserver() {
+    session_duration_observer_.Remove(session_duration_updater_.get());
+  }
+
+  // SessionDurationUpdater::Observer:
+  void OnSessionEnded(base::TimeDelta total_session_time) override {}
+
+  PrefService* GetPrefs() { return pref_service_.get(); }
+
+  SessionDurationUpdater* GetSessionDurationUpdater() {
+    return session_duration_updater_.get();
+  }
+
+ private:
+  const std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
+      pref_service_;
+  std::unique_ptr<SessionDurationUpdater> session_duration_updater_;
+  ScopedObserver<SessionDurationUpdater, SessionDurationUpdater::Observer>
+      session_duration_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
+class SessionDurationUpdaterTest : public testing::Test {
+ public:
+  SessionDurationUpdaterTest() = default;
+  ~SessionDurationUpdaterTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    // Start the DesktopSessionDurationTracker to track active session time.
+    metrics::DesktopSessionDurationTracker::Initialize();
+
+    test_observer_ = base::MakeUnique<TestObserver>();
+    test_observer_->AddSessionDurationObserver();
+  }
+
+  void TearDown() override {
+    test_observer_->RemoveSessionDurationObserver();
+    metrics::DesktopSessionDurationTracker::CleanupForTesting();
+  }
+
+ protected:
+  std::unique_ptr<TestObserver> test_observer_;
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionDurationUpdaterTest);
+};
+
+}  // namespace
+
+// kObservedSessionTime should be 0 on initalization and 50 after simulation.
+TEST_F(SessionDurationUpdaterTest, TestTimeAdded) {
+  // Tests the pref is registered to 0 before any session time passes.
+  EXPECT_EQ(
+      0, test_observer_->GetPrefs()->GetInteger(prefs::kObservedSessionTime));
+
+  // Tests 50 minutes passing with an observer added.
+  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
+      base::TimeDelta::FromMinutes(50));
+
+  EXPECT_EQ(
+      50, test_observer_->GetPrefs()->GetInteger(prefs::kObservedSessionTime));
+}
+
+// kObservedSessionTime should not be updated when SessionDurationUpdater has
+// no observers, but should start updating again if another observer is added.
+TEST_F(SessionDurationUpdaterTest, TestAddingAndRemovingObservers) {
+  // Tests 50 minutes passing with an observer added.
+  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
+      base::TimeDelta::FromMinutes(50));
+
+  EXPECT_EQ(
+      50, test_observer_->GetPrefs()->GetInteger(prefs::kObservedSessionTime));
+
+  // Tests 50 minutes passing without any observers. No time should be added to
+  // the pref in this case.
+  test_observer_->RemoveSessionDurationObserver();
+
+  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
+      base::TimeDelta::FromMinutes(50));
+
+  EXPECT_EQ(
+      50, test_observer_->GetPrefs()->GetInteger(prefs::kObservedSessionTime));
+
+  // Tests 50 minutes passing with an observer re-added. Time should be added
+  // again now.
+  test_observer_->AddSessionDurationObserver();
+
+  test_observer_->GetSessionDurationUpdater()->OnSessionEnded(
+      base::TimeDelta::FromMinutes(50));
+
+  EXPECT_EQ(
+      100, test_observer_->GetPrefs()->GetInteger(prefs::kObservedSessionTime));
+}
+
+}  // namespace feature_engagement
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
index 26cf335..20d322b 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
@@ -171,8 +171,7 @@
 
 void CastMediaSinkServiceImpl::OnDialSinkAdded(const MediaSinkInternal& sink) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto ip_address = sink.dial_data().ip_address;
-  net::IPEndPoint ip_endpoint(ip_address, kCastControlPort);
+  net::IPEndPoint ip_endpoint(sink.dial_data().ip_address, kCastControlPort);
 
   if (base::ContainsKey(current_service_ip_endpoints_, ip_endpoint)) {
     DVLOG(2) << "Sink discovered by mDNS, skip adding [name]: "
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
index bb28759..64f974c 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
@@ -284,10 +284,6 @@
   media_sink_service_impl_.current_sinks_by_mdns_[ip_endpoint1] = cast_sink1;
   media_sink_service_impl_.current_sinks_by_mdns_[ip_endpoint2] = cast_sink2;
   // Cast sink 2, 3 from dial discovery
-  auto extra_data = cast_sink2.cast_data();
-  extra_data.discovered_by_dial = true;
-  extra_data.model_name += " dial";
-
   media_sink_service_impl_.current_sinks_by_dial_[ip_endpoint2] = cast_sink2;
   media_sink_service_impl_.current_sinks_by_dial_[ip_endpoint3] = cast_sink3;
 
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 997fd1b8..748615af 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -70,7 +70,6 @@
     content::mojom::NetworkContextParamsPtr* network_context_params,
     bool* is_quic_allowed) {
   SystemNetworkContextManager* manager = GetInstance();
-  DCHECK(!manager->io_thread_network_context_);
   *network_context_request =
       mojo::MakeRequest(&manager->io_thread_network_context_);
   *network_context_params = CreateNetworkContextParams();
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 2ee854e..5ae70ff 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ui/login/login_handler_test_utils.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -57,6 +58,7 @@
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
+#include "ui/base/ui_base_switches.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/geometry/point.h"
 
@@ -3270,4 +3272,50 @@
   histograms.ExpectTotalCount(kHistogram, 0);
 }
 
+// Harness for showing dialogs as part of the DialogBrowserTest suite. Allows
+// the dialogs to be shown interactively when invoked with, e.g.,
+//   browser_tests --gtest_filter=BrowserDialogTest.Invoke --interactive
+//       --dialog=PasswordManagerDialogBrowserTest.InvokeDialog_normal.
+class PasswordManagerDialogBrowserTest
+    : public SupportsTestDialog<PasswordManagerBrowserTestBase> {
+ public:
+  PasswordManagerDialogBrowserTest() = default;
+
+  // content::BrowserTestBase:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // BrowserDialogTest only works on Mac with --secondary-ui-md.
+    command_line->AppendSwitch(switches::kExtendMdToSecondaryUi);
+  }
+
+  void ShowDialog(const std::string& name) override {
+    // Note regarding flakiness: LocationBarBubbleDelegateView::ShowForReason()
+    // uses ShowInactive() unless the bubble is invoked with reason ==
+    // USER_GESTURE. This means that, so long as these dialogs are not triggered
+    // by gesture, the dialog does not attempt to take focus, and so should
+    // never _lose_ focus in the test, which could cause flakes when tests are
+    // run in parallel. LocationBarBubbles also dismiss on other events, but
+    // only events in the WebContents. E.g. Rogue mouse clicks should not cause
+    // the dialog to dismiss since they won't be sent via WebContents.
+    // A user gesture is determined in browser_commands.cc by checking
+    // ManagePasswordsUIController::IsAutomaticallyOpeningBubble(), but that's
+    // set and cleared immediately while showing the bubble, so it can't be
+    // checked here.
+    NavigateToFile("/password/password_form.html");
+    NavigationObserver observer(WebContents());
+    std::string fill_and_submit =
+        "document.getElementById('username_field').value = 'temp';"
+        "document.getElementById('password_field').value = 'random';"
+        "document.getElementById('input_submit_button').click()";
+    ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
+    observer.Wait();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PasswordManagerDialogBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PasswordManagerDialogBrowserTest, InvokeDialog_normal) {
+  RunDialog();
+}
+
 }  // namespace password_manager
diff --git a/chrome/browser/payments/android/journey_logger_android.cc b/chrome/browser/payments/android/journey_logger_android.cc
index 5d50b90..644cf50 100644
--- a/chrome/browser/payments/android/journey_logger_android.cc
+++ b/chrome/browser/payments/android/journey_logger_android.cc
@@ -111,6 +111,16 @@
                                           requested_phone, requested_name);
 }
 
+void JourneyLoggerAndroid::SetRequestedPaymentMethodTypes(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    jboolean requested_basic_card,
+    jboolean requested_method_google,
+    jboolean requested_method_other) {
+  journey_logger_.SetRequestedPaymentMethodTypes(
+      requested_basic_card, requested_method_google, requested_method_other);
+}
+
 void JourneyLoggerAndroid::SetCompleted(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller) {
diff --git a/chrome/browser/payments/android/journey_logger_android.h b/chrome/browser/payments/android/journey_logger_android.h
index 4fbb5496..3d89ee1 100644
--- a/chrome/browser/payments/android/journey_logger_android.h
+++ b/chrome/browser/payments/android/journey_logger_android.h
@@ -57,6 +57,12 @@
       jboolean requested_email,
       jboolean requested_phone,
       jboolean requested_name);
+  void SetRequestedPaymentMethodTypes(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller,
+      jboolean requested_basic_card,
+      jboolean requested_method_google,
+      jboolean requested_method_other);
   void SetCompleted(JNIEnv* env,
                     const base::android::JavaParamRef<jobject>& jcaller);
   void SetAborted(JNIEnv* env,
diff --git a/chrome/browser/predictors/OWNERS b/chrome/browser/predictors/OWNERS
index 538cb20..791f6ad 100644
--- a/chrome/browser/predictors/OWNERS
+++ b/chrome/browser/predictors/OWNERS
@@ -1,4 +1,3 @@
 alexilin@chromium.org
 lizeb@chromium.org
 pasko@chromium.org
-zhenw@chromium.org
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index aed9e942..082ad59 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -263,9 +263,8 @@
 #endif
 
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) && !defined(OS_MACOSX)
-#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
+#include "chrome/browser/feature_engagement/session_duration_updater.h"
 #endif
-
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 #include "chrome/browser/ui/startup/default_browser_prompt.h"
 #endif
@@ -587,7 +586,7 @@
 #endif
 
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) && !defined(OS_MACOSX)
-  feature_engagement::NewTabTracker::RegisterProfilePrefs(registry);
+  feature_engagement::SessionDurationUpdater::RegisterProfilePrefs(registry);
 #endif
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 157df68..0ff4e1c 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -993,6 +993,9 @@
       g_browser_process->platform_part()
               ->profile_helper()
               ->GetSigninProfileDir() != profile->GetPath() &&
+      g_browser_process->platform_part()
+              ->profile_helper()
+              ->GetLockScreenAppProfilePath() != profile->GetPath() &&
 #endif
       command_line->HasSwitch(switches::kSupervisedUserId);
   if (force_supervised_user_id) {
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 2e8532b..a1a4cb9 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
@@ -115,9 +115,9 @@
     params.writing_direction_default = 0;
     params.writing_direction_left_to_right = 0;
     params.writing_direction_right_to_left = 0;
-#endif  // OS_MACOSX
-    std::unique_ptr<TestRenderViewContextMenu> menu(
-        new TestRenderViewContextMenu(web_contents->GetMainFrame(), params));
+#endif
+    auto menu = base::MakeUnique<TestRenderViewContextMenu>(
+        web_contents->GetMainFrame(), params);
     menu->Init();
     return menu;
   }
@@ -135,7 +135,8 @@
 
 class PdfPluginContextMenuBrowserTest : public InProcessBrowserTest {
  public:
-  PdfPluginContextMenuBrowserTest() {}
+  PdfPluginContextMenuBrowserTest() = default;
+  ~PdfPluginContextMenuBrowserTest() override = default;
 
   void SetUpOnMainThread() override {
     guest_view::GuestViewManager::set_factory_for_testing(&factory_);
@@ -648,8 +649,8 @@
 
   void AttemptImageSearch() {
     // |menu_observer_| will cause the search-by-image menu item to be clicked.
-    menu_observer_.reset(new ContextMenuNotificationObserver(
-        IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE));
+    menu_observer_ = base::MakeUnique<ContextMenuNotificationObserver>(
+        IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE);
     RightClickImage();
   }
 
@@ -818,7 +819,7 @@
       return;
 
     requests_to_wait_for_ = requests_to_wait_for;
-    run_loop_.reset(new base::RunLoop());
+    run_loop_ = base::MakeUnique<base::RunLoop>();
     run_loop_->Run();
     run_loop_.reset();
     requests_to_wait_for_ = -1;
@@ -882,8 +883,8 @@
   void AttemptLoadImage() {
     // Right-click where the image should be.
     // |menu_observer_| will cause the "Load image" menu item to be clicked.
-    menu_observer_.reset(new ContextMenuNotificationObserver(
-        IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE));
+    menu_observer_ = base::MakeUnique<ContextMenuNotificationObserver>(
+        IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE);
     content::WebContents* tab =
         browser()->tab_strip_model()->GetActiveWebContents();
     content::SimulateMouseClickAt(tab, 0, blink::WebMouseEvent::Button::kRight,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.cc
index 7b3cfc60..dea0eac 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.cc
@@ -20,7 +20,7 @@
 TestRenderViewContextMenu::~TestRenderViewContextMenu() {}
 
 // static
-TestRenderViewContextMenu* TestRenderViewContextMenu::Create(
+std::unique_ptr<TestRenderViewContextMenu> TestRenderViewContextMenu::Create(
     content::WebContents* web_contents,
     const GURL& page_url,
     const GURL& link_url,
@@ -29,18 +29,19 @@
   params.page_url = page_url;
   params.link_url = link_url;
   params.frame_url = frame_url;
-  TestRenderViewContextMenu* menu =
-      new TestRenderViewContextMenu(web_contents->GetMainFrame(), params);
+  auto menu = base::MakeUnique<TestRenderViewContextMenu>(
+      web_contents->GetMainFrame(), params);
   menu->Init();
   return menu;
 }
 
-bool TestRenderViewContextMenu::IsItemPresent(int command_id) {
+bool TestRenderViewContextMenu::IsItemPresent(int command_id) const {
   return menu_model_.GetIndexOfCommandId(command_id) != -1;
 }
 
-bool TestRenderViewContextMenu::IsItemInRangePresent(int command_id_first,
-                                                     int command_id_last) {
+bool TestRenderViewContextMenu::IsItemInRangePresent(
+    int command_id_first,
+    int command_id_last) const {
   DCHECK_LE(command_id_first, command_id_last);
   for (int command_id = command_id_first; command_id <= command_id_last;
        ++command_id) {
@@ -65,7 +66,8 @@
         *found_model = model;
         *found_index = i;
         return true;
-      } else if (model->GetTypeAt(i) == MenuModel::TYPE_SUBMENU) {
+      }
+      if (model->GetTypeAt(i) == MenuModel::TYPE_SUBMENU) {
         models_to_search.push_back(model->GetSubmenuModelAt(i));
       }
     }
@@ -75,11 +77,12 @@
 }
 
 int TestRenderViewContextMenu::GetCommandIDByProfilePath(
-    const base::FilePath& path) {
+    const base::FilePath& path) const {
   size_t count = profile_link_paths_.size();
-  for (size_t i = 0; i < count; ++i)
+  for (size_t i = 0; i < count; ++i) {
     if (profile_link_paths_[i] == path)
       return IDC_OPEN_LINK_IN_PROFILE_FIRST + static_cast<int>(i);
+  }
   return -1;
 }
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h b/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h
index 7dd050c7..ea9f5b8 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h
@@ -7,6 +7,8 @@
 
 #include <stddef.h>
 
+#include <memory>
+
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
@@ -34,20 +36,21 @@
   // This is a lightweight method to create a test RenderViewContextMenu
   // instance.
   // Use the constructor if you want to create menu with fine-grained params.
-  static TestRenderViewContextMenu* Create(content::WebContents* web_contents,
-                                           const GURL& page_url,
-                                           const GURL& link_url,
-                                           const GURL& frame_url);
+  static std::unique_ptr<TestRenderViewContextMenu> Create(
+      content::WebContents* web_contents,
+      const GURL& page_url,
+      const GURL& link_url,
+      const GURL& frame_url);
 
   // Returns true if the command specified by |command_id| is present
   // in the menu.
   // A list of command ids can be found in chrome/app/chrome_command_ids.h.
-  bool IsItemPresent(int command_id);
+  bool IsItemPresent(int command_id) const;
 
   // Returns true if a command specified by any command id between
   // |command_id_first| and |command_id_last| (inclusive) is present in the
   // menu.
-  bool IsItemInRangePresent(int command_id_first, int command_id_last);
+  bool IsItemInRangePresent(int command_id_first, int command_id_last) const;
 
   // Searches for an menu item with |command_id|. If it's found, the return
   // value is true and the model and index where it appears in that model are
@@ -57,7 +60,7 @@
                                 int* found_index);
 
   // Returns the command id of the menu item with the specified |path|.
-  int GetCommandIDByProfilePath(const base::FilePath& path);
+  int GetCommandIDByProfilePath(const base::FilePath& path) const;
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ContextMenuMatcher& extension_items() { return extension_items_; }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index 5f9fdf29..f6b4d1bc 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -89,8 +89,8 @@
     ProtocolHandlerRegistry* registry) {
   content::ContextMenuParams params = CreateParams(MenuItem::LINK);
   params.unfiltered_link_url = params.link_url;
-  std::unique_ptr<TestRenderViewContextMenu> menu(
-      new TestRenderViewContextMenu(web_contents->GetMainFrame(), params));
+  auto menu = base::MakeUnique<TestRenderViewContextMenu>(
+      web_contents->GetMainFrame(), params);
   menu->set_protocol_handler_registry(registry);
   menu->Init();
   return menu;
@@ -317,7 +317,7 @@
   void SetUp() override {
     RenderViewContextMenuTest::SetUp();
     // TestingProfile does not provide a protocol registry.
-    registry_.reset(new ProtocolHandlerRegistry(profile(), nullptr));
+    registry_ = base::MakeUnique<ProtocolHandlerRegistry>(profile(), nullptr);
   }
 
   void TearDown() override {
@@ -376,7 +376,7 @@
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-    registry_.reset(new ProtocolHandlerRegistry(profile(), nullptr));
+    registry_ = base::MakeUnique<ProtocolHandlerRegistry>(profile(), nullptr);
   }
 
   void TearDown() override {
@@ -393,8 +393,8 @@
   std::unique_ptr<TestRenderViewContextMenu> CreateContextMenuOnChromeLink() {
     content::ContextMenuParams params = CreateParams(MenuItem::LINK);
     params.unfiltered_link_url = params.link_url = GURL("chrome://settings");
-    std::unique_ptr<TestRenderViewContextMenu> menu(
-        new TestRenderViewContextMenu(web_contents()->GetMainFrame(), params));
+    auto menu = base::MakeUnique<TestRenderViewContextMenu>(
+        web_contents()->GetMainFrame(), params);
     menu->set_protocol_handler_registry(registry_.get());
     menu->Init();
     return menu;
@@ -501,9 +501,8 @@
 
   content::ContextMenuParams params = CreateParams(MenuItem::IMAGE);
   params.unfiltered_link_url = params.link_url;
-  content::WebContents* wc = web_contents();
-  std::unique_ptr<TestRenderViewContextMenu> menu(
-      new TestRenderViewContextMenu(wc->GetMainFrame(), params));
+  auto menu = base::MakeUnique<TestRenderViewContextMenu>(
+      web_contents()->GetMainFrame(), params);
 
   menu->ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 0);
 
@@ -523,9 +522,8 @@
 
   content::ContextMenuParams params = CreateParams(MenuItem::IMAGE);
   params.unfiltered_link_url = params.link_url;
-  content::WebContents* wc = web_contents();
-  std::unique_ptr<TestRenderViewContextMenu> menu(
-      new TestRenderViewContextMenu(wc->GetMainFrame(), params));
+  auto menu = base::MakeUnique<TestRenderViewContextMenu>(
+      web_contents()->GetMainFrame(), params);
 
   menu->ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 0);
 
@@ -547,9 +545,8 @@
       data_reduction_proxy::chrome_proxy_content_transform_header()] =
           data_reduction_proxy::empty_image_directive();
   params.unfiltered_link_url = params.link_url;
-  content::WebContents* wc = web_contents();
-  std::unique_ptr<TestRenderViewContextMenu> menu(
-      new TestRenderViewContextMenu(wc->GetMainFrame(), params));
+  auto menu = base::MakeUnique<TestRenderViewContextMenu>(
+      web_contents()->GetMainFrame(), params);
   AppendImageItems(menu.get());
 
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE));
diff --git a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc
index 736216a..ff5c0806 100644
--- a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc
+++ b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc
@@ -26,7 +26,7 @@
 #include "content/public/test/text_input_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "ui/base/ime/composition_underline.h"
+#include "ui/base/ime/ime_text_span.h"
 #include "ui/base/ime/text_edit_commands.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/base/ime/text_input_mode.h"
@@ -820,8 +820,8 @@
     // Commit some text for this frame.
     content::SendImeCommitTextToWidget(
         frames[index]->GetView()->GetRenderWidgetHost(),
-        base::UTF8ToUTF16(sample_text[index]),
-        std::vector<ui::CompositionUnderline>(), gfx::Range(), 0);
+        base::UTF8ToUTF16(sample_text[index]), std::vector<ui::ImeTextSpan>(),
+        gfx::Range(), 0);
 
     // Verify that the text we committed is now selected by listening to a
     // selection update from a RenderWidgetHostView which has the expected
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index cac302a..bec1740 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -602,6 +602,25 @@
   return GetWebContentsData(web_contents)->is_restored_in_foreground();
 }
 
+bool TabManager::IsLoadingBackgroundTabs() const {
+  if (IsSessionRestoreLoadingTabs())
+    return false;
+
+  if (!pending_navigations_.empty())
+    return true;
+
+  // Excluding session restore above leaves only background opening tabs in
+  // |loading_contents_|. As long as they still remain in background, we are
+  // still loading background tabs, even after we emptied our pending navigation
+  // list.
+  for (const content::WebContents* tab : loading_contents_) {
+    if (!tab->IsVisible())
+      return true;
+  }
+
+  return false;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // TabManager, private:
 
@@ -1132,7 +1151,9 @@
 content::NavigationThrottle::ThrottleCheckResult
 TabManager::MaybeThrottleNavigation(BackgroundTabNavigationThrottle* throttle) {
   content::NavigationHandle* navigation_handle = throttle->navigation_handle();
-  if (CanLoadNextTab()) {
+  if (!base::FeatureList::IsEnabled(
+          features::kStaggeredBackgroundTabOpenExperiment) ||
+      CanLoadNextTab()) {
     loading_contents_.insert(navigation_handle->GetWebContents());
     return content::NavigationThrottle::PROCEED;
   }
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index 5a74ffc1..f2531898 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -226,6 +226,11 @@
   // foreground.
   bool IsTabRestoredInForeground(content::WebContents* web_contents) const;
 
+  // Returns whether the tab manager is currently loading background tabs. This
+  // always returns false during an ongoing session restore, even if background
+  // tabs are being loaded, in order to separate the two activities.
+  bool IsLoadingBackgroundTabs() const;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, PurgeBackgroundRenderer);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ActivateTabResetPurgeState);
@@ -257,6 +262,9 @@
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, BackgroundTabLoadingMode);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, BackgroundTabLoadingSlots);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, BackgroundTabsLoadingOrdering);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, IsLoadingBackgroundTabs);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerWithExperimentDisabledTest,
+                           IsLoadingBackgroundTabs);
   FRIEND_TEST_ALL_PREFIXES(TabManagerStatsCollectorTest,
                            HistogramsSessionRestoreSwitchToTab);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest,
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index 2a0d49f8..cce45cc2 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/strings/string16.h"
 #include "base/test/mock_entropy_provider.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -152,6 +153,10 @@
     contents2_ = nav_handle2_->GetWebContents();
     contents3_ = nav_handle3_->GetWebContents();
 
+    contents1_->WasShown();
+    contents2_->WasHidden();
+    contents3_->WasHidden();
+
     throttle1_ = base::MakeUnique<NonResumingBackgroundTabNavigationThrottle>(
         nav_handle1_.get());
     throttle2_ = base::MakeUnique<NonResumingBackgroundTabNavigationThrottle>(
@@ -166,6 +171,14 @@
     NavigationThrottle::ThrottleCheckResult result3 =
         tab_manager->MaybeThrottleNavigation(throttle3_.get());
 
+    CheckThrottleResults(result1, result2, result3, loading_slots);
+  }
+
+  virtual void CheckThrottleResults(
+      NavigationThrottle::ThrottleCheckResult result1,
+      NavigationThrottle::ThrottleCheckResult result2,
+      NavigationThrottle::ThrottleCheckResult result3,
+      size_t loading_slots) {
     // First tab starts navigation right away because there is no tab loading.
     EXPECT_EQ(content::NavigationThrottle::PROCEED, result1);
     switch (loading_slots) {
@@ -198,6 +211,27 @@
   WebContents* contents3_;
 };
 
+class TabManagerWithExperimentDisabledTest : public TabManagerTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kStaggeredBackgroundTabOpenExperiment);
+    ChromeRenderViewHostTestHarness::SetUp();
+  }
+
+  void CheckThrottleResults(NavigationThrottle::ThrottleCheckResult result1,
+                            NavigationThrottle::ThrottleCheckResult result2,
+                            NavigationThrottle::ThrottleCheckResult result3,
+                            size_t loading_slots) override {
+    EXPECT_EQ(content::NavigationThrottle::PROCEED, result1);
+    EXPECT_EQ(content::NavigationThrottle::PROCEED, result2);
+    EXPECT_EQ(content::NavigationThrottle::PROCEED, result3);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 // TODO(georgesak): Add tests for protection to tabs with form input and
 // playing audio;
 
@@ -976,6 +1010,81 @@
   EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle3_.get()));
 }
 
+TEST_F(TabManagerTest, IsLoadingBackgroundTabs) {
+  TabManager* tab_manager = g_browser_process->GetTabManager();
+  tab_manager->ResetMemoryPressureListenerForTest();
+  EXPECT_FALSE(tab_manager->IsLoadingBackgroundTabs());
+
+  MaybeThrottleNavigations(tab_manager);
+  tab_manager->GetWebContentsData(contents1_)
+      ->DidStartNavigation(nav_handle1_.get());
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  tab_manager->GetWebContentsData(contents1_)->DidStopLoading();
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  tab_manager->GetWebContentsData(contents2_)->DidStopLoading();
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  contents3_->WasShown();
+  EXPECT_FALSE(tab_manager->IsLoadingBackgroundTabs());
+
+  contents3_->WasHidden();
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  tab_manager->GetWebContentsData(contents3_)->DidStopLoading();
+  EXPECT_FALSE(tab_manager->IsLoadingBackgroundTabs());
+}
+
+TEST_F(TabManagerWithExperimentDisabledTest, IsLoadingBackgroundTabs) {
+  EXPECT_FALSE(base::FeatureList::IsEnabled(
+      features::kStaggeredBackgroundTabOpenExperiment));
+
+  TabManager* tab_manager = g_browser_process->GetTabManager();
+  tab_manager->ResetMemoryPressureListenerForTest();
+  EXPECT_FALSE(tab_manager->IsLoadingBackgroundTabs());
+
+  MaybeThrottleNavigations(tab_manager);
+  tab_manager->GetWebContentsData(contents1_)
+      ->DidStartNavigation(nav_handle1_.get());
+  tab_manager->GetWebContentsData(contents2_)
+      ->DidStartNavigation(nav_handle1_.get());
+  tab_manager->GetWebContentsData(contents3_)
+      ->DidStartNavigation(nav_handle1_.get());
+
+  EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents1_));
+  EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents2_));
+  EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents3_));
+  EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle1_.get()));
+  EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle2_.get()));
+  EXPECT_FALSE(tab_manager->IsNavigationDelayedForTest(nav_handle3_.get()));
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  tab_manager->GetWebContentsData(contents1_)->DidStopLoading();
+  EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents1_));
+  EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents2_));
+  EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents3_));
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  tab_manager->GetWebContentsData(contents2_)->DidStopLoading();
+  EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents1_));
+  EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents2_));
+  EXPECT_TRUE(tab_manager->IsTabLoadingForTest(contents3_));
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  contents3_->WasShown();
+  EXPECT_FALSE(tab_manager->IsLoadingBackgroundTabs());
+
+  contents3_->WasHidden();
+  EXPECT_TRUE(tab_manager->IsLoadingBackgroundTabs());
+
+  tab_manager->GetWebContentsData(contents3_)->DidStopLoading();
+  EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents1_));
+  EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents2_));
+  EXPECT_FALSE(tab_manager->IsTabLoadingForTest(contents3_));
+  EXPECT_FALSE(tab_manager->IsLoadingBackgroundTabs());
+}
+
 TEST_F(TabManagerTest, IsTabRestoredInForeground) {
   TabManager* tab_manager = g_browser_process->GetTabManager();
 
diff --git a/chrome/browser/resources/chromeos/arc_support/playstore.js b/chrome/browser/resources/chromeos/arc_support/playstore.js
index a0d052dd..0bde6ca 100644
--- a/chrome/browser/resources/chromeos/arc_support/playstore.js
+++ b/chrome/browser/resources/chromeos/arc_support/playstore.js
@@ -3,54 +3,65 @@
 // found in the LICENSE file.
 
 /**
+ * Analyzes current document and tries to find the link to the Play Store ToS
+ * that matches requested |language| and |countryCode|. Once found, navigate
+ * to this link and returns True. If no match was found then returns False.
+ */
+function navigateToLanguageAndCountryCode(language, countryCode) {
+  var doc = document;
+  var selectLangZoneTerms =
+      doc.getElementById('play-footer').getElementsByTagName('select')[0];
+
+  var applyTermsForLangAndZone = function(termsLang) {
+    var matchByLangZone =
+        '/intl/' + termsLang + '_' + countryCode + '/about/play-terms.html';
+    for (var i = selectLangZoneTerms.options.length - 1; i >= 0; --i) {
+      var option = selectLangZoneTerms.options[i];
+      if (option.value == matchByLangZone) {
+        window.location.href = option.value;
+        return true;
+      }
+    }
+    return false;
+  };
+
+  // Try two versions of the language, full and short (if it exists, for
+  // example en-GB -> en). Note, terms may contain entries for both types, for
+  // example: en_ie, es-419_ar, es_as, pt-PT_pt.
+  if (applyTermsForLangAndZone(language)) {
+    return true;
+  }
+  var langSegments = language.split('-');
+  if (langSegments.length == 2 && applyTermsForLangAndZone(langSegments[0])) {
+    return true;
+  }
+
+  return false;
+}
+
+/**
  * Processes select tag that contains list of available terms for different
  * languages and zones. In case of initial load, tries to find terms that match
  * exactly current language and country code and automatically redirects the
  * view in case such terms are found. Leaves terms in select tag that only match
  * current language or country code or default English variant or currently
- * selected. Note that document.countryCode must be set before calling this
- * function.
+ * selected.
+ *
+ * @return {boolean} True.
  */
-function processLangZoneTerms() {
-  var doc = document;
-  var selectLangZoneTerms =
-      doc.getElementById('play-footer').getElementsByTagName('select')[0];
-
-  var initialLoad =
-      window.location.href == 'https://play.google.com/about/play-terms.html';
-  var langSegments = navigator.language.split('-');
-  if (initialLoad) {
-    var applyTermsForLangAndZone = function(termsLang) {
-      var matchByLangZone = '/intl/' + termsLang + '_' + document.countryCode +
-          '/about/play-terms.html';
-      for (var i = selectLangZoneTerms.options.length - 1; i >= 0; --i) {
-        var option = selectLangZoneTerms.options[i];
-        if (option.value == matchByLangZone) {
-          window.location.href = option.value;
-          return true;
-        }
-      }
-      return false;
-    };
-
-    // Try two versions of the language, full and short (if it exists, for
-    // example en-GB -> en). Note, terms may contain entries for both types, for
-    // example: en_ie, es-419_ar, es_as, pt-PT_pt.
-    if (applyTermsForLangAndZone(navigator.language)) {
-      return;
-    }
-    if (langSegments.length == 2 && applyTermsForLangAndZone(langSegments[0])) {
-      return;
-    }
+function processLangZoneTerms(initialLoad, language, countryCode) {
+  var langSegments = language.split('-');
+  if (initialLoad && navigateToLanguageAndCountryCode(language, countryCode)) {
+    return true;
   }
 
-  var matchByLang = '/intl/' + navigator.language + '_';
+  var matchByLang = '/intl/' + language + '_';
   var matchByLangShort = null;
   if (langSegments.length == 2) {
     matchByLangShort = '/intl/' + langSegments[0] + '_';
   }
 
-  var matchByZone = '_' + document.countryCode + '/about/play-terms.html';
+  var matchByZone = '_' + countryCode + '/about/play-terms.html';
   var matchByDefault = '/intl/en/about/play-terms.html';
 
   // We are allowed to display terms by default only in language that matches
@@ -58,6 +69,9 @@
   var langMatch = false;
   var defaultExist = false;
 
+  var doc = document;
+  var selectLangZoneTerms =
+      doc.getElementById('play-footer').getElementsByTagName('select')[0];
   for (var i = selectLangZoneTerms.options.length - 1; i >= 0; --i) {
     var option = selectLangZoneTerms.options[i];
     if (selectLangZoneTerms.selectedIndex == i) {
@@ -69,19 +83,20 @@
       defaultExist = true;
       continue;
     }
-    if (!option.value.startsWith(matchByLang) &&
+
+    option.hidden = !option.value.startsWith(matchByLang) &&
         !option.value.endsWith(matchByZone) &&
         !(matchByLangShort && option.value.startsWith(matchByLangShort)) &&
-        option.text != 'English') {
-      selectLangZoneTerms.removeChild(option);
-    }
+        option.text != 'English';
   }
+
   if (initialLoad && !langMatch && defaultExist) {
     window.location.href = matchByDefault;
   } else {
     // Show content once we reached target url.
     document.body.hidden = false;
   }
+  return true;
 }
 
 /**
@@ -131,5 +146,26 @@
   return 'https://www.google.com/policies/privacy/';
 }
 
-formatDocument();
-processLangZoneTerms();
+/**
+ * Processes the current document by applying required formatting and selected
+ * right PlayStore ToS.
+ * Note that document.countryCode must be set before calling this function.
+ */
+function processDocument() {
+  if (document.wasProcessed) {
+    return;
+  }
+  formatDocument();
+
+  var initialLoad =
+      window.location.href == 'https://play.google.com/about/play-terms.html';
+  var language = document.language;
+  if (!language) {
+    language = navigator.language;
+  }
+
+  processLangZoneTerms(initialLoad, language, document.countryCode);
+  document.wasProcessed = true;
+}
+
+processDocument();
diff --git a/chrome/browser/resources/chromeos/login/images/1x/updating_1x.png b/chrome/browser/resources/chromeos/login/images/1x/updating_1x.png
new file mode 100644
index 0000000..758e0e3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/images/1x/updating_1x.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/images/2x/updating_2x.png b/chrome/browser/resources/chromeos/login/images/2x/updating_2x.png
new file mode 100644
index 0000000..da1f440
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/images/2x/updating_2x.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/md_header_bar.js b/chrome/browser/resources/chromeos/login/md_header_bar.js
index 927d568..67ba4cca 100644
--- a/chrome/browser/resources/chromeos/login/md_header_bar.js
+++ b/chrome/browser/resources/chromeos/login/md_header_bar.js
@@ -8,6 +8,16 @@
 
 cr.define('login', function() {
   /**
+   * Enum for user actions taken from lock screen header while a lock screen
+   * app is in background.
+   * @enum {string}
+   */
+  var LOCK_SCREEN_APPS_UNLOCK_ACTION = {
+    SIGN_OUT: 'LOCK_SCREEN_APPS_UNLOCK_ACTION.SIGN_OUT',
+    SHUTDOWN: 'LOCK_SCREEN_APPS_UNLOCK_ACTION.SHUTDOWN'
+  };
+
+  /**
    * Creates a header bar element.
    *
    * @constructor
@@ -50,11 +60,13 @@
     decorate: function() {
       document.addEventListener('click', this.handleClick_.bind(this));
       $('shutdown-header-bar-item')
-          .addEventListener('click', this.handleShutdownClick_);
-      $('shutdown-button').addEventListener('click', this.handleShutdownClick_);
+          .addEventListener('click', this.handleShutdownClick_.bind(this));
+      $('shutdown-button')
+          .addEventListener('click', this.handleShutdownClick_.bind(this));
       $('restart-header-bar-item')
-          .addEventListener('click', this.handleShutdownClick_);
-      $('restart-button').addEventListener('click', this.handleShutdownClick_);
+          .addEventListener('click', this.handleShutdownClick_.bind(this));
+      $('restart-button')
+          .addEventListener('click', this.handleShutdownClick_.bind(this));
       $('add-user-button').addEventListener('click', this.handleAddUserClick_);
       $('more-settings-button')
           .addEventListener('click', this.handleMoreSettingsClick_.bind(this));
@@ -62,7 +74,7 @@
           .addEventListener('click', this.handleGuestClick_);
       $('guest-user-button').addEventListener('click', this.handleGuestClick_);
       $('sign-out-user-button')
-          .addEventListener('click', this.handleSignoutClick_);
+          .addEventListener('click', this.handleSignoutClick_.bind(this));
       $('cancel-multiple-sign-in-button')
           .addEventListener('click', this.handleCancelMultipleSignInClick_);
       $('unlock-user-button')
@@ -184,6 +196,12 @@
      */
     handleSignoutClick_: function(e) {
       this.disabled = true;
+      if (this.lockScreenAppsState_ == LOCK_SCREEN_APPS_STATE.BACKGROUND) {
+        chrome.send(
+            'recordLockScreenAppUnlockAction',
+            [LOCK_SCREEN_APPS_UNLOCK_ACTION.SIGN_OUT]);
+      }
+
       chrome.send('signOutUser');
       e.stopPropagation();
     },
@@ -194,6 +212,11 @@
      * @private
      */
     handleShutdownClick_: function(e) {
+      if (this.lockScreenAppsState_ == LOCK_SCREEN_APPS_STATE.BACKGROUND) {
+        chrome.send(
+            'recordLockScreenAppUnlockAction',
+            [LOCK_SCREEN_APPS_UNLOCK_ACTION.SHUTDOWN]);
+      }
       chrome.send('shutdownSystem');
       e.stopPropagation();
     },
diff --git a/chrome/browser/resources/chromeos/login/md_top_header_bar.html b/chrome/browser/resources/chromeos/login/md_top_header_bar.html
index 0fc2608..056bc034 100644
--- a/chrome/browser/resources/chromeos/login/md_top_header_bar.html
+++ b/chrome/browser/resources/chromeos/login/md_top_header_bar.html
@@ -3,7 +3,7 @@
     <defs>
       <g id="action-icon" fill="none" fill-rule="evenodd">
         <path d="M0 0h16v16H0"></path>
-        <path fill="#FFF" fill-rule="nonzero" d="M9.333 1.333H4c-.737 0-1.327.597-1.327 1.334l-.006 10.666c0 .737.59 1.334 1.326 1.334H12c.737 0 1.333-.597 1.333-1.334v-8l-4-4zm1.334 9.334h-2v2H7.333v-2h-2V9.333h2v-2h1.334v2h2v1.334zM8.667 6V2.333L12.333 6H8.667z"></path>
+        <path fill="#FFF" fill-rule="nonzero" d="M8.993 1.007h-4.67c-.734 0-1.327.6-1.327 1.333L2.99 13.69c0 .734.592 1.31 1.326 1.31h7.362c.733 0 1.333-.576 1.333-1.31V4.984L8.994 1.007zM9 6V2.535L12.542 6H9z"></path>
       </g>
     </defs>
   </svg>
diff --git a/chrome/browser/resources/chromeos/login/md_top_header_bar.js b/chrome/browser/resources/chromeos/login/md_top_header_bar.js
index 7108c2e..3a11efa 100644
--- a/chrome/browser/resources/chromeos/login/md_top_header_bar.js
+++ b/chrome/browser/resources/chromeos/login/md_top_header_bar.js
@@ -11,6 +11,15 @@
  */
 
 cr.define('login', function() {
+  /**
+   * The new note request type.
+   * @enum {string}
+   */
+  var NEW_NOTE_REQUEST = {
+    TAP: 'NEW_NOTE_REQUEST.TAP',
+    SWIPE: 'NEW_NOTE_REQUEST.SWIPE',
+    KEYBOARD: 'NEW_NOTE_REQUEST.KEYBOARD'
+  };
 
   /**
    * Calculates diagonal length of a rectangle with the provided sides.
@@ -349,7 +358,7 @@
     /** override */
     decorate: function() {
       $('new-note-action')
-          .addEventListener('click', this.activateNoteAction_.bind(this));
+          .addEventListener('click', this.handleNewNoteActionClick_.bind(this));
       $('new-note-action')
           .addEventListener(
               'keydown', this.handleNewNoteActionKeyDown_.bind(this));
@@ -416,6 +425,14 @@
     },
 
     /**
+     * Handler for clicks on note action element.
+     * @private
+     */
+    handleNewNoteActionClick_: function() {
+      this.activateNoteAction_(NEW_NOTE_REQUEST.TAP);
+    },
+
+    /**
      * Handler for key down event.
      * @param {!KeyboardEvent} evt The key down event.
      * @private
@@ -423,7 +440,7 @@
     handleNewNoteActionKeyDown_: function(evt) {
       if (evt.code != 'Enter')
         return;
-      this.activateNoteAction_();
+      this.activateNoteAction_(NEW_NOTE_REQUEST.KEYBOARD);
     },
 
     /**
@@ -442,13 +459,15 @@
       if (diag(velocity.x, velocity.y) < MIN_SWIPE_VELOCITY)
         return;
 
-      this.activateNoteAction_();
+      this.activateNoteAction_(NEW_NOTE_REQUEST.SWIPE);
     },
 
     /**
+     * @param {!NEW_NOTE_REQUEST} requestType The type of request that triggered
+     *      new note action.
      * @private
      */
-    activateNoteAction_: function() {
+    activateNoteAction_: function(requestType) {
       $('new-note-action').classList.toggle('disabled', true);
       $('new-note-action-icon').hidden = true;
       $('top-header-bar').classList.toggle('version-labels-unset', true);
@@ -457,9 +476,7 @@
           (function() {
             if (this.lockScreenAppsState_ != LOCK_SCREEN_APPS_STATE.AVAILABLE)
               return;
-            chrome.send(
-                'setLockScreenAppsState',
-                [LOCK_SCREEN_APPS_STATE.LAUNCH_REQUESTED]);
+            chrome.send('requestNewLockScreenNote', [requestType]);
           }).bind(this));
 
       var container = $('new-note-action-container');
diff --git a/chrome/browser/resources/chromeos/login/oobe_change_picture.html b/chrome/browser/resources/chromeos/login/oobe_change_picture.html
index 89ff91b0..c2fc0ad0 100644
--- a/chrome/browser/resources/chromeos/login/oobe_change_picture.html
+++ b/chrome/browser/resources/chromeos/login/oobe_change_picture.html
@@ -26,7 +26,8 @@
         --cr-picture-image-size: 192px;
         -webkit-margin-end: 28px;
         flex-shrink: 0;
-        margin-top: 48px;
+        height: 288px;
+        position: relative;
         width: 288px;
       }
 
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.css b/chrome/browser/resources/chromeos/login/oobe_update.css
index d85623c8..f90d743 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.css
+++ b/chrome/browser/resources/chromeos/login/oobe_update.css
@@ -20,6 +20,10 @@
   padding: 0;
 }
 
+.update-illustration {
+  padding-bottom: 10px;
+}
+
 paper-progress {
   --paper-progress-active-color: var(--google-blue-500);
   --paper-progress-secondary-color: var(--google-blue-100);
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.html b/chrome/browser/resources/chromeos/login/oobe_update.html
index c0909ab..ee2328e 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.html
+++ b/chrome/browser/resources/chromeos/login/oobe_update.html
@@ -38,6 +38,10 @@
       </div>
       <div class="footer flex layout vertical">
         <paper-progress id="checking-progress" indeterminate></paper-progress>
+        <div class="flex"></div>
+        <img class="update-illustration"
+            srcset="images/1x/updating_1x.png 1x,
+                    images/2x/updating_2x.png 2x">
       </div>
     </oobe-dialog>
     <oobe-dialog hidden="[[checkingForUpdate]]" tabindex="0"
@@ -58,6 +62,10 @@
             hidden="[[!estimatedTimeLeftShown]]">[[estimatedTimeLeft]]</div>
         <div id="progress-message" class="progress-message"
             hidden="[[!progressMessageShown]]">[[progressMessage]]</div>
+        <div class="flex"></div>
+        <img class="update-illustration"
+            srcset="images/1x/updating_1x.png 1x,
+                    images/2x/updating_2x.png 2x">
       </div>
     </oobe-dialog>
   </template>
diff --git a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
index edd6873e..2d2f103 100644
--- a/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
+++ b/chrome/browser/resources/chromeos/login/screen_arc_terms_of_service.js
@@ -10,12 +10,40 @@
   return {
     EXTERNAL_API: [
       'setMetricsMode', 'setBackupAndRestoreMode', 'setLocationServicesMode',
-      'setCountryCode'
+      'loadPlayStoreToS'
     ],
 
     /** @override */
     decorate: function(element) {
       // Valid newOobeUI is not available at this time.
+      this.countryCode_ = null;
+      this.language_ = null;
+    },
+
+    /**
+     * Returns current language that can be updated in OOBE flow. If OOBE flow
+     * does not exist then use navigator.language.
+     *
+     * @private
+     */
+    getCurrentLanguage_: function() {
+      var languageList = loadTimeData.getValue('languageList');
+      if (languageList) {
+        var language = Oobe.getSelectedValue(languageList);
+        if (language) {
+          return language;
+        }
+      }
+      return navigator.language;
+    },
+
+    /**
+     * Returns true if page was intialized.
+     *
+     * @private
+     */
+    isPageReady_: function() {
+      return typeof this.useMDOobe !== 'undefined';
     },
 
     /**
@@ -25,8 +53,7 @@
      */
     setMDMode_: function() {
       var useMDOobe = (loadTimeData.getString('newOobeUI') == 'on');
-      if (typeof this.useMDOobe !== 'undefined' &&
-          this.useMDOobe == useMDOobe) {
+      if (this.isPageReady_() && this.useMDOobe == useMDOobe) {
         return;
       }
 
@@ -75,17 +102,8 @@
                   typeof results[0] == 'string') {
                 self.showUrlOverlay(results[0]);
               } else {
-                // currentLanguage can be updated in OOBE but not in Login page.
-                // languageList exists in OOBE otherwise use navigator.language.
-                var currentLanguage;
-                var languageList = loadTimeData.getValue('languageList');
-                if (languageList) {
-                  currentLanguage = Oobe.getSelectedValue(languageList);
-                } else {
-                  currentLanguage = navigator.language;
-                }
                 var defaultLink = 'https://www.google.com/intl/' +
-                    currentLanguage + '/policies/privacy/';
+                    self.getCurrentLanguage_() + '/policies/privacy/';
                 self.showUrlOverlay(defaultLink);
               }
             });
@@ -148,15 +166,35 @@
     },
 
     /**
-     * Sets current country code for ToS.
+     * Loads Play Store ToS in case country code has been changed or previous
+     * attempt failed.
      * @param {string} countryCode Country code based on current timezone.
      */
-    setCountryCode: function(countryCode) {
+    loadPlayStoreToS: function(countryCode) {
+      // Make sure page is initialized for login mode. For OOBE mode, page is
+      // initialized as result of handling updateLocalizedContent.
+      this.setMDMode_();
+
+      var language = this.getCurrentLanguage_();
+      countryCode = countryCode.toLowerCase();
+
+      if (this.language_ && this.language_ == language && this.countryCode_ &&
+          this.countryCode_ == countryCode &&
+          !this.classList.contains('error')) {
+        return;
+      }
+
+      // Store current ToS parameters.
+      this.language_ = language;
+      this.countryCode_ = countryCode;
+
       var scriptSetParameters =
-          'document.countryCode = \'' + countryCode.toLowerCase() + '\';';
+          'document.countryCode = \'' + countryCode + '\';';
+      scriptSetParameters += 'document.language = \'' + language + '\';';
       if (this.useMDOobe) {
         scriptSetParameters += 'document.viewMode = \'large-view\';';
       }
+
       var termsView = this.getElement_('arc-tos-view');
       termsView.removeContentScripts(['preProcess']);
       termsView.addContentScripts([{
@@ -166,9 +204,21 @@
         run_at: 'document_start'
       }]);
 
-      if (!$('arc-tos').hidden) {
-        this.reloadPlayStore();
+      // Try to use currently loaded document first.
+      var self = this;
+      if (termsView.src != '' && this.classList.contains('arc-tos-loaded')) {
+        var navigateScript = 'processLangZoneTerms(true, \'' + language +
+            '\', \'' + countryCode + '\');';
+        termsView.executeScript({code: navigateScript}, function(results) {
+          if (!results || results.length != 1 ||
+              typeof results[0] !== 'boolean' || !results[0]) {
+            self.reloadPlayStoreToS();
+          }
+        });
+      } else {
+        this.reloadPlayStoreToS();
       }
+
     },
 
     /**
@@ -191,7 +241,7 @@
       retryButton.id = 'arc-tos-retry-button';
       retryButton.textContent =
           loadTimeData.getString('arcTermsOfServiceRetryButton');
-      retryButton.addEventListener('click', this.reloadPlayStore.bind(this));
+      retryButton.addEventListener('click', this.reloadPlayStoreToS.bind(this));
       buttons.push(retryButton);
 
       var acceptButton = this.ownerDocument.createElement('button');
@@ -288,9 +338,9 @@
     },
 
     /**
-     * Reloads Play Store.
+     * Reloads Play Store ToS.
      */
-    reloadPlayStore: function() {
+    reloadPlayStoreToS: function() {
       this.termsError = false;
       var termsView = this.getElement_('arc-tos-view');
       termsView.src = 'https://play.google.com/about/play-terms.html';
@@ -372,7 +422,6 @@
      * @param {object} data Screen init payload.
      */
     onBeforeShow: function(data) {
-      this.setMDMode_();
       this.setLearnMoreHandlers_();
 
       Oobe.getInstance().headerHidden = true;
@@ -383,7 +432,6 @@
           'https://play.google.com/about/images/play_logo.png';
 
       this.hideOverlay();
-      this.reloadPlayStore();
     },
 
     /**
@@ -406,6 +454,11 @@
     updateLocalizedContent: function() {
       this.setMDMode_();
       this.setLearnMoreHandlers_();
+
+      // We might need to reload Play Store ToS in case language was changed.
+      if (this.countryCode_) {
+        this.loadPlayStoreToS(this.countryCode_);
+      }
     },
 
     /**
diff --git a/chrome/browser/resources/chromeos/login/screen_error_message.css b/chrome/browser/resources/chromeos/login/screen_error_message.css
index b85da668..8b6db95 100644
--- a/chrome/browser/resources/chromeos/login/screen_error_message.css
+++ b/chrome/browser/resources/chromeos/login/screen_error_message.css
@@ -42,10 +42,6 @@
   display: block;
 }
 
-#oobe.error-message #step-logo {
-  z-index: 1;
-}
-
 .error-header {
   background-clip: padding-box;
   background-color: white;
diff --git a/chrome/browser/resources/chromeos/login/screen_error_message.html b/chrome/browser/resources/chromeos/login/screen_error_message.html
index ec7fd9c..43047ec 100644
--- a/chrome/browser/resources/chromeos/login/screen_error_message.html
+++ b/chrome/browser/resources/chromeos/login/screen_error_message.html
@@ -1,6 +1,6 @@
 <link rel="import" href="chrome://oobe/custom_elements.html">
 
-<div id="error-message" class="step hidden show-offline-error" hidden>
+<div id="error-message" class="step hidden show-offline-error no-logo" hidden>
   <div class="step-contents">
     <div class="error-header" aria-live="assertive">
       <img alt class="error-icon" src="chrome://theme/IDR_ERROR_NETWORK_OFFLINE">
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index 4040aca7..fd94782 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -664,7 +664,8 @@
           (this.screenMode_ == ScreenMode.SAML_INTERSTITIAL);
       params.menuGuestMode = data.guestSignin;
       params.menuKeyboardOptions = false;
-      params.menuEnterpriseEnrollment = true;
+      params.menuEnterpriseEnrollment =
+          !(data.enterpriseManagedDevice || data.hasDeviceOwner);
 
       this.gaiaAuthParams_ = params;
 
diff --git a/chrome/browser/resources/md_bookmarks/BUILD.gn b/chrome/browser/resources/md_bookmarks/BUILD.gn
index e0b81a21e..16e7498 100644
--- a/chrome/browser/resources/md_bookmarks/BUILD.gn
+++ b/chrome/browser/resources/md_bookmarks/BUILD.gn
@@ -1,22 +1,12 @@
 import("../vulcanize.gni")
 
-vulcanized_unbuilt = "vulcanized.unbuilt.html"
-
-vulcanize("vulcanize") {
+vulcanize("build") {
   host = "bookmarks"
   html_in_files = [ "bookmarks.html" ]
-  html_out_files = [ vulcanized_unbuilt ]
+  html_out_files = [ "vulcanized.html" ]
 
   input = rebase_path(".", root_build_dir)
   js_out_files = [ "crisper.js" ]
 
   deps = []
 }
-
-polymer_css_build("build") {
-  input_files = [ vulcanized_unbuilt ]
-  output_files = [ "vulcanized.html" ]
-  deps = [
-    ":vulcanize",
-  ]
-}
diff --git a/chrome/browser/resources/md_downloads/BUILD.gn b/chrome/browser/resources/md_downloads/BUILD.gn
index 923b8e8..1b340e3 100644
--- a/chrome/browser/resources/md_downloads/BUILD.gn
+++ b/chrome/browser/resources/md_downloads/BUILD.gn
@@ -1,21 +1,11 @@
 import("../vulcanize.gni")
 
-vulcanized_unbuilt = "vulcanized.unbuilt.html"
-
-vulcanize("vulcanize") {
+vulcanize("build") {
   deps = []
   host = "downloads"
   html_in_files = [ "downloads.html" ]
-  html_out_files = [ vulcanized_unbuilt ]
+  html_out_files = [ "vulcanized.html" ]
   input = rebase_path(".", root_build_dir)
   insert_in_head = "<base href=chrome://downloads>"
   js_out_files = [ "crisper.js" ]
 }
-
-polymer_css_build("build") {
-  input_files = [ vulcanized_unbuilt ]
-  output_files = [ "vulcanized.html" ]
-  deps = [
-    ":vulcanize",
-  ]
-}
diff --git a/chrome/browser/resources/md_history/BUILD.gn b/chrome/browser/resources/md_history/BUILD.gn
index f6ab0e0..2625f11 100644
--- a/chrome/browser/resources/md_history/BUILD.gn
+++ b/chrome/browser/resources/md_history/BUILD.gn
@@ -1,17 +1,14 @@
 import("../vulcanize.gni")
 
-app_unbuilt = "app.vulcanized.unbuilt.html"
-lazy_load_unbuilt = "lazy_load.vulcanized.unbuilt.html"
-
-vulcanize("vulcanize_app") {
+vulcanize("build") {
   host = "history"
   html_in_files = [
     "app.html",
     "lazy_load.html",
   ]
   html_out_files = [
-    app_unbuilt,
-    lazy_load_unbuilt,
+    "app.vulcanized.html",
+    "lazy_load.vulcanized.html",
   ]
 
   input = rebase_path(".", root_build_dir)
@@ -27,17 +24,3 @@
 
   deps = []
 }
-
-polymer_css_build("build") {
-  input_files = [
-    app_unbuilt,
-    lazy_load_unbuilt,
-  ]
-  output_files = [
-    "app.vulcanized.html",
-    "lazy_load.vulcanized.html",
-  ]
-  deps = [
-    ":vulcanize_app",
-  ]
-}
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 3f0ae9c4..0885042 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
@@ -17,6 +17,7 @@
       <div>
         <div id="route-time-controls" hidden="[[!routeStatus.canSeek]]">
           <paper-slider
+              aria-valuetext$="[[getTimeSliderValueText_(displayedCurrentTime_)]]"
               dir="ltr"
               id="route-time-slider"
               on-change="onSeekComplete_"
@@ -25,10 +26,12 @@
               title="[[i18n('seekTitle')]]"
               value="[[displayedCurrentTime_]]"></paper-slider>
           <div id="timeline">
-            <span id="current-time">
+            <span id="current-time"
+                  aria-label$="[[getCurrentTimeLabel_(displayedCurrentTime_)]]">
               [[getFormattedTime_(displayedCurrentTime_)]]
             </span>
-            <span id="duration">
+            <span id="duration"
+                  aria-label$="[[getDurationLabel_(routeStatus.duration)]]">
               [[getFormattedTime_(routeStatus.duration)]]
             </span>
           </div>
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 7dde729..510ac91d 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
@@ -134,6 +134,30 @@
   },
 
   /**
+   * Creates an accessibility label for the element showing the media's current
+   * time.
+   * @param {number} displayedCurrentTime
+   * @return {string}
+   * @private
+   */
+  getCurrentTimeLabel_: function(displayedCurrentTime) {
+    return `${
+              this.i18n('currentTimeLabel')
+            } ${this.getFormattedTime_(displayedCurrentTime)}`;
+  },
+
+  /**
+   * Creates an accessibility label for the element showing the media's
+   * duration.
+   * @param {number} duration
+   * @return {string}
+   * @private
+   */
+  getDurationLabel_: function(duration) {
+    return `${this.i18n('durationLabel')} ${this.getFormattedTime_(duration)}`;
+  },
+
+  /**
    * Converts a number representing an interval of seconds to a string with
    * HH:MM:SS format.
    * @param {number} timeInSec Must be non-negative. Intervals longer than 100
@@ -148,8 +172,9 @@
     var hours = Math.floor(timeInSec / 3600);
     var minutes = Math.floor(timeInSec / 60) % 60;
     var seconds = Math.floor(timeInSec) % 60;
-    return ('0' + hours).substr(-2) + ':' + ('0' + minutes).substr(-2) + ':' +
-        ('0' + seconds).substr(-2);
+    // Show the hours only if it is nonzero.
+    return (hours ? ('0' + hours).substr(-2) + ':' : '') +
+        ('0' + minutes).substr(-2) + ':' + ('0' + seconds).substr(-2);
   },
 
   /**
@@ -194,6 +219,15 @@
         this.i18n('pauseTitle');
   },
 
+  getTimeSliderValueText_: function(displayedCurrentTime) {
+    if (!this.routeStatus) {
+      return '';
+    }
+    return `${
+              this.getFormattedTime_(displayedCurrentTime)
+            } / ${this.getFormattedTime_(this.routeStatus.duration)}`;
+  },
+
   /**
    * Checks whether the media is still playing, and if so, sends a media status
    * update incrementing the current time and schedules another call for a
diff --git a/chrome/browser/resources/polymer_css_build_gn.py b/chrome/browser/resources/polymer_css_build_gn.py
deleted file mode 100755
index 14fe7c5..0000000
--- a/chrome/browser/resources/polymer_css_build_gn.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-import argparse
-import os
-import sys
-
-_HERE_PATH = os.path.dirname(__file__)
-_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..'))
-_CWD = os.getcwd()
-
-sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node'))
-import node
-import node_modules
-
-def _css_build(out_folder, input_files, output_files):
-  out_path = os.path.join(_CWD, out_folder)
-  in_paths = map(lambda f: os.path.join(out_path, f), input_files)
-  out_paths = map(lambda f: os.path.join(out_path, f), output_files)
-
-  args = ['--no-inline-includes', '-f'] + in_paths + ['-o'] + out_paths
-  node.RunNode([node_modules.PathToPolymerCssBuild()] + args)
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--out_folder')
-  parser.add_argument('--input_files', nargs='+')
-  parser.add_argument('--output_files', nargs='+')
-  args = parser.parse_args()
-
-  _css_build(
-      args.out_folder,
-      input_files=args.input_files,
-      output_files=args.output_files)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/chrome/browser/resources/print_preview/settings/scaling_settings.js b/chrome/browser/resources/print_preview/settings/scaling_settings.js
index 99d752c..2db68ad 100644
--- a/chrome/browser/resources/print_preview/settings/scaling_settings.js
+++ b/chrome/browser/resources/print_preview/settings/scaling_settings.js
@@ -121,11 +121,13 @@
 
     /**
      * Display the fit to page scaling in the scaling field if there is a valid
-     * fit to page scaling value. If not, make the field blank.
+     * fit to page scaling value. If not, make the field blank. The fit to page
+     * value is always considered valid, so remove the hint if it is displayed.
      * @private
      */
     displayFitToPageScaling: function() {
       this.inputField_.value = this.fitToPageScaling_ || '';
+      this.removeHint_();
     },
 
     /**
@@ -151,17 +153,24 @@
     },
 
     /**
+     * Removes the error message and red background from the input.
+     * @private
+     */
+    removeHint_: function() {
+      this.inputField_.classList.remove('invalid');
+      fadeOutElement(this.getChildElement('.hint'));
+    },
+
+    /**
      * Updates the state of the scaling settings UI controls.
      * @private
      */
     updateState_: function() {
       if (this.isAvailable()) {
-        var displayedValue = this.getInputAsNumber();
         // If fit to page is selected and the display matches, mark valid
         // and return.
         if (this.isFitToPageSelected() && this.displayMatchesFitToPage()) {
-          this.inputField_.classList.remove('invalid');
-          fadeOutElement(this.getChildElement('.hint'));
+          this.removeHint_();
           this.updateUiStateInternal();
           return;
         }
@@ -174,8 +183,7 @@
         }
 
         this.inputField_.value = this.scalingTicketItem_.getValue();
-        this.inputField_.classList.remove('invalid');
-        fadeOutElement(this.getChildElement('.hint'));
+        this.removeHint_();
       }
       this.updateUiStateInternal();
     },
@@ -191,9 +199,11 @@
         this.displayFitToPageScaling();
       } else if (
           this.fitToPageTicketItem_.isCapabilityAvailable() &&
-          this.displayMatchesFitToPage()) {
+          (this.displayMatchesFitToPage() ||
+           !this.inputField_.validity.valid)) {
         // Fit to page unchecked. Return to last scaling.
         this.inputField_.value = this.scalingTicketItem_.getValue();
+        this.removeHint_();
       }
     },
 
@@ -207,7 +217,8 @@
         this.updateState_();
         return;
       }
-      if (this.isFitToPageSelected() && !this.displayMatchesFitToPage()) {
+      if (this.isFitToPageSelected() && !this.displayMatchesFitToPage() &&
+          this.inputField_.value != '') {
         // User modified value away from fit to page.
         this.fitToPageTicketItem_.updateValue(false);
       }
@@ -262,12 +273,12 @@
       if (this.inputField_.value == '' && this.inputField_.validity.valid) {
         if (this.isFitToPageSelected()) {
           this.displayFitToPageScaling();
+        } else if (this.scalingTicketItem_.getValue() == '100') {
+          // No need to update the ticket, but change the display to match.
+          this.inputField_.value = '100';
+          this.removeHint_();
         } else {
-          if (this.scalingTicketItem_.getValue() == '100')
-            // No need to update the ticket, but change the display to match.
-            this.inputField_.value = '100';
-          else
-            this.scalingTicketItem_.updateValue('100');
+          this.scalingTicketItem_.updateValue('100');
         }
       }
     },
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index 4b7895a..af4bf1d 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -2,20 +2,18 @@
 import("//tools/grit/grit_rule.gni")
 import("//chrome/common/features.gni")
 
-app_unbuilt = "vulcanized.unbuilt.html"
-lazy_load_unbuilt = "lazy_load.vulcanized.unbuilt.html"
 settings_pak_file = "settings_resources.pak"
 unpak_folder = "settings_resources.unpak"
 
-vulcanize("vulcanize_app") {
+vulcanize("build") {
   host = "settings"
   html_in_files = [
     "settings.html",
     "lazy_load.html",
   ]
   html_out_files = [
-    app_unbuilt,
-    lazy_load_unbuilt,
+    "vulcanized.html",
+    "lazy_load.vulcanized.html",
   ]
   insert_in_head = "<base href=\"chrome://\$i18n{hostname}\">"
   input = rebase_path("$target_gen_dir/$unpak_folder", root_build_dir)
@@ -38,20 +36,6 @@
   ]
 }
 
-polymer_css_build("build") {
-  input_files = [
-    app_unbuilt,
-    lazy_load_unbuilt,
-  ]
-  output_files = [
-    "vulcanized.html",
-    "lazy_load.vulcanized.html",
-  ]
-  deps = [
-    ":vulcanize_app",
-  ]
-}
-
 grit("flattened_resources") {
   source = "settings_resources.grd"
 
diff --git a/chrome/browser/resources/settings/people_page/change_picture.html b/chrome/browser/resources/settings/people_page/change_picture.html
index 8d0c573..c8e093f38 100644
--- a/chrome/browser/resources/settings/people_page/change_picture.html
+++ b/chrome/browser/resources/settings/people_page/change_picture.html
@@ -30,7 +30,8 @@
         --cr-picture-image-size: 192px;
         -webkit-margin-end: 24px;
         flex-shrink: 0;
-        margin-top: 48px;
+        height: 288px;
+        position: relative;
         width: 288px;
       }
 
diff --git a/chrome/browser/resources/vulcanize_gn.py b/chrome/browser/resources/vulcanize_gn.py
index 5b1d517b..78e37ad 100755
--- a/chrome/browser/resources/vulcanize_gn.py
+++ b/chrome/browser/resources/vulcanize_gn.py
@@ -180,26 +180,33 @@
       f.close()
 
   try:
+    crisper_html_out_paths = []
     for index, html_in_file in enumerate(args.html_in_files):
-      html_out_file = args.html_out_files[index]
+      crisper_html_out_paths.append(
+          os.path.join(tmp_out_dir, args.html_out_files[index]))
       js_out_file = args.js_out_files[index]
 
       # Run crisper to separate the JS from the HTML file.
       node.RunNode([node_modules.PathToCrisper(),
                    '--source', os.path.join(tmp_out_dir, html_in_file),
                    '--script-in-head', 'false',
-                   '--html', os.path.join(tmp_out_dir, html_out_file),
+                   '--html', crisper_html_out_paths[index],
                    '--js', os.path.join(tmp_out_dir, js_out_file)])
 
-      # Move the HTML file to its final destination.
-      shutil.copy(os.path.join(tmp_out_dir, html_out_file), out_path)
-
       # Pass the JS file through Uglify and write the output to its final
       # destination.
       node.RunNode([node_modules.PathToUglify(),
                     os.path.join(tmp_out_dir, js_out_file),
                     '--comments', '"/Copyright|license|LICENSE|\<\/?if/"',
                     '--output', os.path.join(out_path, js_out_file)])
+
+    # Run polymer-css-build and write the output HTML files to their final
+    # destination.
+    pcb_html_out_paths = [
+        os.path.join(out_path, f) for f in args.html_out_files]
+    node.RunNode([node_modules.PathToPolymerCssBuild()] +
+                 ['--no-inline-includes', '-f'] +
+                 crisper_html_out_paths + ['-o'] + pcb_html_out_paths)
   finally:
     shutil.rmtree(tmp_out_dir)
   return manifest_out_path
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor.cc b/chrome/browser/safe_browsing/browser_feature_extractor.cc
index bbabc07..d627b039 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
index 83958d9..76253cc 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
@@ -24,7 +24,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index b5ba16f..15d9191 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -23,7 +23,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 7559c39..4ee5678 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -23,7 +23,7 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
index 30743b7..4d97ebf9 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -25,7 +25,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index 62a43d3..ecea7d38 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -22,7 +22,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "crypto/sha2.h"
diff --git a/chrome/browser/safe_browsing/client_side_model_loader.cc b/chrome/browser/safe_browsing/client_side_model_loader.cc
index b598044..c1da018 100644
--- a/chrome/browser/safe_browsing/client_side_model_loader.cc
+++ b/chrome/browser/safe_browsing/client_side_model_loader.cc
@@ -19,7 +19,7 @@
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
 #include "components/safe_browsing/common/safebrowsing_switches.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_response_headers.h"
diff --git a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
index f30d5590..237b83ad 100644
--- a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/common/safe_browsing/client_model.pb.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/http/http_status_code.h"
diff --git a/chrome/browser/safe_browsing/download_feedback.cc b/chrome/browser/safe_browsing/download_feedback.cc
index b0be578..08e1563a 100644
--- a/chrome/browser/safe_browsing/download_feedback.cc
+++ b/chrome/browser/safe_browsing/download_feedback.cc
@@ -10,7 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task_runner.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_errors.h"
 
diff --git a/chrome/browser/safe_browsing/download_feedback_unittest.cc b/chrome/browser/safe_browsing/download_feedback_unittest.cc
index 8bd291f..301f49b 100644
--- a/chrome/browser/safe_browsing/download_feedback_unittest.cc
+++ b/chrome/browser/safe_browsing/download_feedback_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/task_scheduler/post_task.h"
 #include "chrome/browser/safe_browsing/two_phase_uploader.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/safe_browsing/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection_service.cc
index 82fd1dd..18ecd68 100644
--- a/chrome/browser/safe_browsing/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection_service.cc
@@ -58,7 +58,7 @@
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/common/safebrowsing_switches.h"
 #include "components/safe_browsing/common/utils.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/page_navigator.h"
diff --git a/chrome/browser/safe_browsing/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection_service_unittest.cc
index 39a7ccd..9f88de2 100644
--- a/chrome/browser/safe_browsing/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection_service_unittest.cc
@@ -41,7 +41,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/common/safebrowsing_switches.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
index bf35ec2..c78d52f9 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
index 0d619b9..7f4331e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/signature_evaluator_mac.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
index a6f371b..01b9e0d5 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_mac_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
index 3b6e7ca..e05eb2b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/common/chrome_version.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
index 2c17aa4..5c9f26116 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer_win_unittest.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_version.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
index a914bfba..a20a8e4 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
index bb86600..061d7e7 100644
--- a/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_unittest.cc
@@ -11,7 +11,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc
index 4349fc84..52be87a0 100644
--- a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_analyzer_win.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome_elf/blacklist/blacklist.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc
index c62e2b6f..b757e746 100644
--- a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc
index cd88c8c..9f92cf4 100644
--- a/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
index 53b518d..6c5cd38e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.cc
@@ -22,7 +22,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/task_scheduler/task_traits.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
index 23b85b6..ede70704 100644
--- a/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/download_metadata_manager_unittest.cc
@@ -16,7 +16,7 @@
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/test/mock_download_item.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
index 06e02ec4..2001009 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection.cc
@@ -11,7 +11,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/common/channel_info.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/version_info/version_info.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
index ccbd5df..9a7dd8e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.cc
@@ -26,7 +26,7 @@
 #include "chrome/browser/safe_browsing/path_sanitizer.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome_elf/chrome_elf_constants.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/variations/variations_associated_data.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
index 2013471..69deb08 100644
--- a/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win_unittest.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h"
 #include "chrome/browser/safe_browsing/path_sanitizer.h"
 #include "chrome_elf/chrome_elf_constants.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "net/base/winsock_init.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
index d60f976..6471ded 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/extensions/install_signer.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
index 8378b6c..cfd373e 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
@@ -21,7 +21,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident.cc b/chrome/browser/safe_browsing/incident_reporting/incident.cc
index 21d0b1b8..6c9bfc1 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident.cc
@@ -8,7 +8,7 @@
 
 #include "base/logging.h"
 #include "base/time/time.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
index 358e0f32..5b344d2 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc
@@ -8,7 +8,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
index f50fdc44..6639f0d 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/test/test_simple_task_runner.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/test_url_fetcher_factory.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
index e95b976..b794818 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
@@ -37,7 +37,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
index 9d4d540..4f0c73fb 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
@@ -35,7 +35,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "extensions/browser/quota_service.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index 96d6612a..5d36b8b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -28,7 +28,7 @@
 #include "components/history/core/browser/download_constants.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
index d3cf2fd1e..8b031fbe 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
@@ -44,7 +44,7 @@
 #include "components/history/core/browser/history_database_params.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
index c1d99e9..7e24fcc 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.cc
@@ -17,7 +17,7 @@
 #include "base/scoped_native_library.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/pe_image.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
index 9d91af9a..452c520 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win_unittest.cc
@@ -21,7 +21,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/pe_image.h"
 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_unittest_util_win.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc
index eea5a41..de02bf8 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.h"
 #include "chrome/browser/safe_browsing/path_sanitizer.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 
 #if defined(SAFE_BROWSING_DB_LOCAL)
diff --git a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc
index 344194f2..56998c2 100644
--- a/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/module_load_analyzer_win_unittest.cc
@@ -15,7 +15,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
index d871351..6bd74eb 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.cc
@@ -12,7 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
index 0aa2d823..0ea185a6 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/values.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
index 30fed1f..9885c5f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/resource_request_incident.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
index e52c8132..83431bf 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/previews_state.h"
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
index 109b499..a08df8f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc
index 2f5589e..78fcd0a7 100644
--- a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc
index 27450a5..04b050b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/suspicious_module_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
index bc999fe..63ad14c 100644
--- a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
index c5a9e33..1ad07e0b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc
index c35c1af..842d0d5 100644
--- a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_analyzer.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
 #include "chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/variations/service/variations_service.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc
index abd66c1..f358d0f 100644
--- a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_handler_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc
index 07b0261..46a3f6a 100644
--- a/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/variations_seed_signature_incident_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/browser/safe_browsing/notification_image_reporter.cc b/chrome/browser/safe_browsing/notification_image_reporter.cc
index ae292b09..dcca406 100644
--- a/chrome/browser/safe_browsing/notification_image_reporter.cc
+++ b/chrome/browser/safe_browsing/notification_image_reporter.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc b/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc
index 5a3774af..5cdb126 100644
--- a/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc
+++ b/chrome/browser/safe_browsing/notification_image_reporter_unittest.cc
@@ -14,7 +14,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
index 473cb52..9767f57 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
@@ -8,7 +8,7 @@
 #include <deque>
 #include "base/feature_list.h"
 #include "base/supports_user_data.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/safe_browsing/signature_evaluator_mac.mm b/chrome/browser/safe_browsing/signature_evaluator_mac.mm
index 9ffcb66..a3d3968 100644
--- a/chrome/browser/safe_browsing/signature_evaluator_mac.mm
+++ b/chrome/browser/safe_browsing/signature_evaluator_mac.mm
@@ -19,7 +19,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc b/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
index 3041104..aa3f72d 100644
--- a/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
+++ b/chrome/browser/safe_browsing/signature_evaluator_mac_unittest.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
 #include "chrome/browser/safe_browsing/incident_reporting/mock_incident_receiver.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/safe_browsing/threat_details_unittest.cc b/chrome/browser/safe_browsing/threat_details_unittest.cc
index d7da5a4..8788a47 100644
--- a/chrome/browser/safe_browsing/threat_details_unittest.cc
+++ b/chrome/browser/safe_browsing/threat_details_unittest.cc
@@ -23,7 +23,7 @@
 #include "components/safe_browsing/browser/threat_details.h"
 #include "components/safe_browsing/browser/threat_details_history.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_renderer_host.h"
diff --git a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
index c3588664..29f1274 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
@@ -160,7 +160,7 @@
                                    ->GetSSL()
                                    .certificate.get();
   EXPECT_TRUE(cert->Equals(expected_cert));
-  EXPECT_TRUE(expired_explanation.insecure_explanations[0].has_certificate);
+  EXPECT_TRUE(!!expired_explanation.insecure_explanations[0].certificate);
 }
 
 // Checks that the given |explanation| contains an appropriate
@@ -184,7 +184,7 @@
                                    ->GetSSL()
                                    .certificate.get();
   EXPECT_TRUE(cert->Equals(expected_cert));
-  EXPECT_TRUE(explanation.has_certificate);
+  EXPECT_TRUE(!!explanation.certificate);
 }
 
 // Checks that the given |explanation| contains an appropriate
diff --git a/chrome/browser/sync/DEPS b/chrome/browser/sync/DEPS
index a2eaea2..88800dc 100644
--- a/chrome/browser/sync/DEPS
+++ b/chrome/browser/sync/DEPS
@@ -5,14 +5,3 @@
   "+components/sync_wifi",
   "+google/cacheinvalidation",
 ]
-specific_include_rules = {
-  "sync_error_notifier_ash\.cc": [
-    "+ash/system/system_notifier.h",
-  ],
-  "sync_error_notifier_factory_ash\.cc": [
-    "+ash/shell.h",
-  ],
-  ".*_unittest\.cc": [
-    "+ash/test/ash_test_base.h",
-  ]
-}
diff --git a/chrome/browser/sync/sync_error_notifier_ash.cc b/chrome/browser/sync/sync_error_notifier_ash.cc
index 5ca2dce0b..e6c2929 100644
--- a/chrome/browser/sync/sync_error_notifier_ash.cc
+++ b/chrome/browser/sync/sync_error_notifier_ash.cc
@@ -4,10 +4,11 @@
 
 #include "chrome/browser/sync/sync_error_notifier_ash.h"
 
-#include "ash/system/system_notifier.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/user_flow.h"
+#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/notifications/notification_ui_manager.h"
 #include "chrome/browser/profiles/profile.h"
@@ -20,17 +21,12 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/message_center/notification.h"
 #include "ui/message_center/notification_delegate.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/user_flow.h"
-#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
-#include "components/user_manager/user_manager.h"
-#endif
-
 namespace {
 
 const char kProfileSyncNotificationId[] = "chrome://settings/sync/";
@@ -137,7 +133,6 @@
     return;
   }
 
-#if defined(OS_CHROMEOS)
   if (user_manager::UserManager::IsInitialized()) {
     chromeos::UserFlow* user_flow =
         chromeos::ChromeUserManager::Get()->GetCurrentUserFlow();
@@ -148,7 +143,6 @@
     if (!user_flow->ShouldLaunchBrowser())
       return;
   }
-#endif
 
   // Error state just got triggered. There shouldn't be previous notification.
   // Let's display one.
diff --git a/chrome/browser/sync/sync_error_notifier_ash_unittest.cc b/chrome/browser/sync/sync_error_notifier_ash_unittest.cc
index 41d7a43..4b34e58 100644
--- a/chrome/browser/sync/sync_error_notifier_ash_unittest.cc
+++ b/chrome/browser/sync/sync_error_notifier_ash_unittest.cc
@@ -8,7 +8,6 @@
 
 #include <memory>
 
-#include "ash/test/ash_test_base.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
@@ -19,6 +18,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -32,8 +32,6 @@
 using ::testing::ReturnRef;
 using ::testing::_;
 
-namespace ash {
-
 namespace {
 
 const char kTestAccountId[] = "testuser@test.com";
@@ -67,7 +65,7 @@
   return base::MakeUnique<FakeLoginUIService>();
 }
 
-class SyncErrorNotifierTest : public AshTestBase {
+class SyncErrorNotifierTest : public BrowserWithTestWindowTest {
  public:
   SyncErrorNotifierTest() {}
   ~SyncErrorNotifierTest() override {}
@@ -75,7 +73,7 @@
   void SetUp() override {
     DCHECK(TestingBrowserProcess::GetGlobal());
 
-    AshTestBase::SetUp();
+    BrowserWithTestWindowTest::SetUp();
 
     profile_manager_ = base::MakeUnique<TestingProfileManager>(
         TestingBrowserProcess::GetGlobal());
@@ -104,7 +102,7 @@
     service_.reset();
     profile_manager_.reset();
 
-    AshTestBase::TearDown();
+    BrowserWithTestWindowTest::TearDown();
   }
 
  protected:
@@ -148,8 +146,6 @@
   DISALLOW_COPY_AND_ASSIGN(SyncErrorNotifierTest);
 };
 
-}  // namespace
-
 // Test that SyncErrorNotifier shows an notification if a passphrase is
 // required.
 TEST_F(SyncErrorNotifierTest, PassphraseNotification) {
@@ -209,4 +205,4 @@
   }
 }
 
-}  // namespace ash
+}  // namespace
diff --git a/chrome/browser/sync/sync_error_notifier_factory_ash.cc b/chrome/browser/sync/sync_error_notifier_factory_ash.cc
index 2af253c..2ab7418 100644
--- a/chrome/browser/sync/sync_error_notifier_factory_ash.cc
+++ b/chrome/browser/sync/sync_error_notifier_factory_ash.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/sync/sync_error_notifier_factory_ash.h"
 
-#include "ash/shell.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
@@ -35,9 +34,6 @@
 
 KeyedService* SyncErrorNotifierFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  if (!ash::Shell::HasInstance())
-    return nullptr;
-
   Profile* profile = static_cast<Profile*>(context);
   browser_sync::ProfileSyncService* profile_sync_service =
       ProfileSyncServiceFactory::GetForProfile(profile);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 996ebba..2c7ae1f4 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3056,6 +3056,8 @@
         "cocoa/restart_browser.mm",
         "cocoa/screen_capture_notification_ui_cocoa.h",
         "cocoa/screen_capture_notification_ui_cocoa.mm",
+        "cocoa/separate_fullscreen_window.h",
+        "cocoa/separate_fullscreen_window.mm",
         "cocoa/session_crashed_bubble.mm",
         "cocoa/simple_message_box_bridge_views.mm",
         "cocoa/simple_message_box_cocoa.h",
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 8bd8e2c..e1792c0 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -78,17 +78,18 @@
           model->search_box(), model->results(),
           HistoryFactory::GetForBrowserContext(profile));
 
-  // Add mixer groups. There are three main groups: apps, webstore and
-  // omnibox. Each group has a "soft" maximum number of results. However, if
+  // Add mixer groups. There are four main groups: answer card, apps, webstore
+  // and omnibox. Each group has a "soft" maximum number of results. However, if
   // a query turns up very few results, the mixer may take more than this
   // maximum from a particular group.
 
-  // Multiplier 100 is used because the answer card is designed to be the most
-  // relevant result and must be on the top of the result list.
-  size_t answer_card_group_id = controller->AddGroup(1, 100.0);
-  size_t apps_group_id = controller->AddGroup(kMaxAppsGroupResults, 1.0);
-  size_t omnibox_group_id = controller->AddGroup(kMaxOmniboxResults, 1.0);
-  size_t webstore_group_id = controller->AddGroup(kMaxWebstoreResults, 0.4);
+  size_t answer_card_group_id = controller->AddGroup(1, 1.0, 10.0);
+  size_t apps_group_id =
+      controller->AddGroup(kMaxAppsGroupResults, 1.0,
+                           features::IsFullscreenAppListEnabled() ? 5.0 : 0.0);
+  size_t omnibox_group_id = controller->AddGroup(kMaxOmniboxResults, 1.0, 0.0);
+  size_t webstore_group_id =
+      controller->AddGroup(kMaxWebstoreResults, 0.4, 0.0);
 
   // Add search providers.
   controller->AddProvider(
@@ -112,7 +113,7 @@
   }
   if (IsSuggestionsSearchProviderEnabled()) {
     size_t suggestions_group_id =
-        controller->AddGroup(kMaxSuggestionsResults, 1.0);
+        controller->AddGroup(kMaxSuggestionsResults, 1.0, 0.0);
     controller->AddProvider(
         suggestions_group_id,
         base::MakeUnique<SuggestionsSearchProvider>(profile, list_controller));
@@ -123,7 +124,7 @@
   if (app_list::switches::IsDriveSearchInChromeLauncherEnabled() &&
       !profile->IsGuestSession()) {
     size_t search_api_group_id =
-        controller->AddGroup(kMaxLauncherSearchResults, 1.0);
+        controller->AddGroup(kMaxLauncherSearchResults, 1.0, 0.0);
     controller->AddProvider(search_api_group_id,
                             base::MakeUnique<LauncherSearchProvider>(profile));
   }
@@ -131,7 +132,7 @@
 #if defined(OS_CHROMEOS)
   if (features::IsPlayStoreAppSearchEnabled()) {
     size_t playstore_api_group_id =
-        controller->AddGroup(kMaxPlayStoreResults, 1.0);
+        controller->AddGroup(kMaxPlayStoreResults, 1.0, 0.0);
     controller->AddProvider(
         playstore_api_group_id,
         base::MakeUnique<ArcPlayStoreSearchProvider>(kMaxPlayStoreResults,
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc b/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
index a5ba98b..c08458dc 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
@@ -9,13 +9,20 @@
 #include "ash/public/interfaces/constants.mojom.h"
 #include "chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h"
 #include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/app_list/presenter/app_list_presenter_impl.h"
 #include "ui/gfx/geometry/rect.h"
 
-AppListPresenterService::AppListPresenterService(Profile* profile)
-    : profile_(profile), binding_(this) {
+AppListPresenterService::AppListPresenterService() : binding_(this) {}
+
+AppListPresenterService::~AppListPresenterService() {}
+
+void AppListPresenterService::Init() {
+  if (binding_.is_bound())
+    return;
+
   content::ServiceManagerConnection* connection =
       content::ServiceManagerConnection::GetForProcess();
   if (connection && connection->GetConnector()) {
@@ -32,8 +39,6 @@
   }
 }
 
-AppListPresenterService::~AppListPresenterService() {}
-
 void AppListPresenterService::Show(int64_t display_id) {
   GetPresenter()->Show(display_id);
 }
@@ -48,7 +53,8 @@
 
 void AppListPresenterService::StartVoiceInteractionSession() {
   auto* service =
-      arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(profile_);
+      arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(
+          ChromeLauncherController::instance()->profile());
   if (service)
     service->StartSessionFromUserInteraction(gfx::Rect());
 }
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_service.h b/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
index 759db7d..2151a32 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
@@ -9,8 +9,6 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "ui/app_list/presenter/app_list_presenter.mojom.h"
 
-class Profile;
-
 namespace app_list {
 class AppListPresenterImpl;
 }
@@ -18,9 +16,12 @@
 // A service providing the Mojo interface to manipulate the App List.
 class AppListPresenterService : public app_list::mojom::AppListPresenter {
  public:
-  explicit AppListPresenterService(Profile* profile);
+  AppListPresenterService();
   ~AppListPresenterService() override;
 
+  // Initialize service and mojo bindings.
+  void Init();
+
   // app_list::mojom::AppListPresenter:
   void Show(int64_t display_id) override;
   void Dismiss() override;
@@ -33,7 +34,6 @@
  private:
   app_list::AppListPresenterImpl* GetPresenter();
 
-  Profile* const profile_;  // Owned by ProfileManager.
   mojo::Binding<app_list::mojom::AppListPresenter> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListPresenterService);
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
index 2eaaee6..365f09f 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
@@ -100,6 +100,7 @@
       base::MakeUnique<app_list::AppListPresenterImpl>(std::move(factory));
   controller_delegate_ =
       base::MakeUnique<AppListControllerDelegateAsh>(app_list_presenter_.get());
+  app_list_presenter_service_ = base::MakeUnique<AppListPresenterService>();
 }
 
 AppListServiceAsh::~AppListServiceAsh() {}
@@ -109,11 +110,7 @@
 }
 
 void AppListServiceAsh::Init(Profile* initial_profile) {
-  // The AppListPresenterService ctor calls AppListServiceAsh::GetInstance(),
-  // which isn't available in the AppListServiceAsh constructor, so init here.
-  // This establishes the mojo connections between the app list and presenter.
-  app_list_presenter_service_ =
-      base::MakeUnique<AppListPresenterService>(initial_profile);
+  app_list_presenter_service_->Init();
 
   // Ensure the StartPageService is created here. This early initialization is
   // necessary to allow the WebContents to load before the app list is shown.
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index f84a6a1..ba9d08f 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1337,15 +1337,6 @@
                                           security_style_explanations);
 }
 
-void Browser::ShowCertificateViewerInDevTools(
-    content::WebContents* web_contents,
-    scoped_refptr<net::X509Certificate> certificate) {
-  DevToolsWindow* devtools_window =
-      DevToolsWindow::GetInstanceForInspectedWebContents(web_contents);
-  if (devtools_window)
-    devtools_window->ShowCertificateViewer(certificate);
-}
-
 std::unique_ptr<content::BluetoothChooser> Browser::RunBluetoothChooser(
     content::RenderFrameHost* frame,
     const content::BluetoothChooser::EventHandler& event_handler) {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 9825121..df8a86b 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -491,9 +491,6 @@
   blink::WebSecurityStyle GetSecurityStyle(
       content::WebContents* web_contents,
       content::SecurityStyleExplanations* security_style_explanations) override;
-  void ShowCertificateViewerInDevTools(
-      content::WebContents* web_contents,
-      scoped_refptr<net::X509Certificate> certificate) override;
   std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser(
       content::RenderFrameHost* frame,
       const content::BluetoothChooser::EventHandler& event_handler) override;
diff --git a/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc b/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc
index 9bec796..86fef1f 100644
--- a/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc
+++ b/chrome/browser/ui/browser_command_controller_interactive_browsertest.cc
@@ -14,10 +14,10 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
@@ -31,6 +31,7 @@
 #endif
 
 namespace {
+
 // The html file to receive key events, prevent defaults and export all the
 // events with "getKeyEventReport()" function. It has two magic keys: pressing
 // "S" to enter fullscreen mode; pressing "X" to indicate the end of all the
@@ -53,8 +54,8 @@
   ~BrowserCommandControllerInteractiveTest() override = default;
 
  protected:
-  // Starts the test page and waits for it to be loaded.
-  void StartTestPage();
+  // Starts |kFullscreenKeyboardLockHTML| in a new tab and waits for load.
+  void StartFullscreenLockPage();
 
   // Sends a control or command + |key| shortcut to the focused window. Shift
   // modifier will be added if |shift| is true.
@@ -78,13 +79,91 @@
   // for the operation to take effect.
   void SendEscapeAndWaitForExitingFullscreen();
 
-  // Sends a set of preventable shortcuts to the web page.
-  void SendShortcutsInFullscreen();
+  // Sends a set of preventable shortcuts to the web page and expects them to be
+  // prevented.
+  void SendShortcutsAndExpectPrevented();
+
+  // Sends a set of preventable shortcuts to the web page and expects them to
+  // not be prevented. If |js_fullscreen| is true, the test will use
+  // SendJsFullscreenShortcutAndWait() to trigger the fullscreen mode. Otherwise
+  // SendFullscreenShortcutAndWait() will be used.
+  void SendShortcutsAndExpectNotPrevented(bool js_fullscreen);
 
   // Sends a magic KeyX to the focused window to stop the test case, receives
   // the result and verifies if it is equal to |expected_result_|.
   void FinishTestAndVerifyResult();
 
+  // Returns whether the active tab is in html fullscreen mode.
+  bool IsActiveTabFullscreen() const {
+    auto* contents = GetActiveWebContents();
+    return contents->GetDelegate()->IsFullscreenForTabOrPending(contents);
+  }
+
+  // Returns whether the GetActiveBrowser() is in browser fullscreen mode.
+  bool IsInBrowserFullscreen() const {
+    return GetActiveBrowser()
+               ->exclusive_access_manager()
+               ->fullscreen_controller()
+               ->IsFullscreenForBrowser();
+  }
+
+  content::WebContents* GetActiveWebContents() const {
+    return GetActiveBrowser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  // Gets the current active tab index.
+  int GetActiveTabIndex() const {
+    return GetActiveBrowser()->tab_strip_model()->active_index();
+  }
+
+  // Gets the count of tabs in current browser.
+  int GetTabCount() const {
+    return GetActiveBrowser()->tab_strip_model()->count();
+  }
+
+  // Gets the count of browser instances.
+  size_t GetBrowserCount() const {
+    return BrowserList::GetInstance()->size();
+  }
+
+  // Gets the last active Browser instance.
+  Browser* GetActiveBrowser() const {
+    return BrowserList::GetInstance()->GetLastActive();
+  }
+
+  // Ensures GetActiveBrowser() is focused.
+  void FocusOnLastActiveBrowser() {
+    ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(
+        GetActiveBrowser()));
+  }
+
+  // Waits until the count of Browser instances becomes |expected|.
+  void WaitForBrowserCount(size_t expected) {
+    while (GetBrowserCount() != expected)
+      content::RunAllPendingInMessageLoop();
+  }
+
+  // Waits until the count of the tabs in active Browser instance becomes
+  // |expected|.
+  void WaitForTabCount(int expected) {
+    while (GetTabCount() != expected)
+      content::RunAllPendingInMessageLoop();
+  }
+
+  // Waits until the index of active tab in active Browser instance becomes
+  // |expected|.
+  void WaitForActiveTabIndex(int expected) {
+    while (GetActiveTabIndex() != expected)
+      content::RunAllPendingInMessageLoop();
+  }
+
+  // Waits until the index of active tab in active Browser instance is not
+  // |expected|.
+  void WaitForInactiveTabIndex(int expected) {
+    while (GetActiveTabIndex() == expected)
+      content::RunAllPendingInMessageLoop();
+  }
+
  private:
   void SetUpOnMainThread() override;
 
@@ -95,19 +174,21 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserCommandControllerInteractiveTest);
 };
 
-void BrowserCommandControllerInteractiveTest::StartTestPage() {
-  ASSERT_TRUE(embedded_test_server()->Start());
+void BrowserCommandControllerInteractiveTest::StartFullscreenLockPage() {
   // Ensures the initial states.
-  ASSERT_EQ(1, browser()->tab_strip_model()->count());
-  ASSERT_EQ(0, browser()->tab_strip_model()->active_index());
-  ASSERT_EQ(1U, BrowserList::GetInstance()->size());
+  ASSERT_EQ(1, GetTabCount());
+  ASSERT_EQ(0, GetActiveTabIndex());
+  ASSERT_EQ(1U, GetBrowserCount());
   // Add a second tab for counting and focus purposes.
   AddTabAtIndex(1, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_LINK);
-  ASSERT_EQ(2, browser()->tab_strip_model()->count());
-  ASSERT_EQ(1U, BrowserList::GetInstance()->size());
+  ASSERT_EQ(2, GetTabCount());
+  ASSERT_EQ(1U, GetBrowserCount());
 
+  if (!embedded_test_server()->Started())
+    ASSERT_TRUE(embedded_test_server()->Start());
   ui_test_utils::NavigateToURLWithDisposition(
-      browser(), embedded_test_server()->GetURL(kFullscreenKeyboardLockHTML),
+      GetActiveBrowser(),
+      embedded_test_server()->GetURL(kFullscreenKeyboardLockHTML),
       WindowOpenDisposition::CURRENT_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
 }
@@ -122,8 +203,9 @@
   const bool control_modifier = true;
   const bool command_modifier = false;
 #endif
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, control_modifier,
-                                              shift, false, command_modifier));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), key, control_modifier, shift, false,
+      command_modifier));
 
   expected_result_ += ui::KeycodeConverter::DomCodeToCodeString(
       ui::UsLayoutKeyboardCodeToDomCode(key));
@@ -151,15 +233,15 @@
   // Enter fullscreen.
 #if defined(OS_MACOSX)
   // On MACOSX, Command + Control + F is used.
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_F, true,
-                                              false, false, true));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_F, true, false, false, true));
 #elif defined(OS_CHROMEOS)
   // A dedicated fullscreen key is used on Chrome OS, so send a fullscreen
   // command directly instead, to avoid constructing the key press.
-  ASSERT_TRUE(chrome::ExecuteCommand(browser(), IDC_FULLSCREEN));
+  ASSERT_TRUE(chrome::ExecuteCommand(GetActiveBrowser(), IDC_FULLSCREEN));
 #else
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_F11, false,
-                                              false, false, false));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_F11, false, false, false, false));
 #endif
 
   observer.Wait();
@@ -170,15 +252,16 @@
   content::WindowedNotificationObserver observer(
       chrome::NOTIFICATION_FULLSCREEN_CHANGED,
       content::NotificationService::AllSources());
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_S, false,
-                                              false, false, false));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_S, false, false, false, false));
   expected_result_ += "KeyS ctrl:false shift:false alt:false meta:false\n";
   observer.Wait();
+  ASSERT_TRUE(IsActiveTabFullscreen());
 }
 
 void BrowserCommandControllerInteractiveTest::SendEscape() {
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, false,
-                                              false, false, false));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_ESCAPE, false, false, false, false));
   expected_result_ += "Escape ctrl:false shift:false alt:false meta:false\n";
 }
 
@@ -187,43 +270,143 @@
   content::WindowedNotificationObserver observer(
       chrome::NOTIFICATION_FULLSCREEN_CHANGED,
       content::NotificationService::AllSources());
-  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE, false,
-                                              false, false, false));
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_ESCAPE, false, false, false, false));
   observer.Wait();
+  ASSERT_FALSE(IsActiveTabFullscreen());
 }
 
-void BrowserCommandControllerInteractiveTest::SendShortcutsInFullscreen() {
-  const int initial_active_index = browser()->tab_strip_model()->active_index();
-  const int initial_tab_count = browser()->tab_strip_model()->count();
-  const size_t initial_browser_count = BrowserList::GetInstance()->size();
+void BrowserCommandControllerInteractiveTest::
+    SendShortcutsAndExpectPrevented() {
+  const int initial_active_index = GetActiveTabIndex();
+  const int initial_tab_count = GetTabCount();
+  const size_t initial_browser_count = GetBrowserCount();
   // The tab should not be closed.
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
-  ASSERT_EQ(initial_tab_count, browser()->tab_strip_model()->count());
+  ASSERT_EQ(initial_tab_count, GetTabCount());
   // The window should not be closed.
   ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_W));
-  ASSERT_EQ(initial_browser_count, BrowserList::GetInstance()->size());
+  ASSERT_EQ(initial_browser_count, GetBrowserCount());
   // TODO(zijiehe): ChromeOS incorrectly handles these;
   // see http://crbug.com/737307.
 #if !defined(OS_CHROMEOS)
   // A new tab should not be created.
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
-  ASSERT_EQ(initial_tab_count, browser()->tab_strip_model()->count());
+  ASSERT_EQ(initial_tab_count, GetTabCount());
   // A new window should not be created.
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_N));
-  ASSERT_EQ(initial_browser_count, BrowserList::GetInstance()->size());
+  ASSERT_EQ(initial_browser_count, GetBrowserCount());
   // A new incognito window should not be created.
   ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_N));
-  ASSERT_EQ(initial_browser_count, BrowserList::GetInstance()->size());
+  ASSERT_EQ(initial_browser_count, GetBrowserCount());
   // Last closed tab should not be restored.
   ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_T));
-  ASSERT_EQ(initial_tab_count, browser()->tab_strip_model()->count());
+  ASSERT_EQ(initial_tab_count, GetTabCount());
 #endif
   // Browser should not switch to the next tab.
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_TAB));
-  ASSERT_EQ(initial_active_index, browser()->tab_strip_model()->active_index());
+  ASSERT_EQ(initial_active_index, GetActiveTabIndex());
   // Browser should not switch to the previous tab.
   ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_TAB));
-  ASSERT_EQ(initial_active_index, browser()->tab_strip_model()->active_index());
+  ASSERT_EQ(initial_active_index, GetActiveTabIndex());
+}
+
+void BrowserCommandControllerInteractiveTest::
+    SendShortcutsAndExpectNotPrevented(bool js_fullscreen) {
+  const int initial_active_index = GetActiveTabIndex();
+  const int initial_tab_count = GetTabCount();
+  const size_t initial_browser_count = GetBrowserCount();
+  const auto enter_fullscreen = [this, js_fullscreen]() {
+    ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(
+        this->GetActiveBrowser()));
+    if (js_fullscreen) {
+      if (!this->IsActiveTabFullscreen()) {
+        const std::string page =
+            "<html><head><script>"
+            "document.addEventListener('keydown', "
+            "    () => { document.body.webkitRequestFullscreen(); });"
+            "</script></head><body></body></html>";
+        ui_test_utils::NavigateToURLWithDisposition(
+            this->GetActiveBrowser(),
+            GURL("data:text/html," + page),
+            WindowOpenDisposition::CURRENT_TAB,
+            ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+        content::WindowedNotificationObserver observer(
+            chrome::NOTIFICATION_FULLSCREEN_CHANGED,
+            content::NotificationService::AllSources());
+        ASSERT_NO_FATAL_FAILURE(this->SendJsFullscreenShortcutAndWait());
+        observer.Wait();
+        ASSERT_TRUE(this->IsActiveTabFullscreen());
+      }
+    } else {
+      if (!this->IsInBrowserFullscreen()) {
+        ASSERT_NO_FATAL_FAILURE(this->SendFullscreenShortcutAndWait());
+      }
+      ASSERT_TRUE(this->IsInBrowserFullscreen());
+    }
+  };
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // A new tab should be created and focused.
+  ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
+  WaitForTabCount(initial_tab_count + 1);
+  ASSERT_NE(initial_active_index, GetActiveTabIndex());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // The newly created tab should be closed.
+  ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
+  WaitForTabCount(initial_tab_count);
+  ASSERT_EQ(initial_active_index, GetActiveTabIndex());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // A new tab should be created and focused.
+  ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
+  WaitForTabCount(initial_tab_count + 1);
+  ASSERT_NE(initial_active_index, GetActiveTabIndex());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // The previous tab should be focused.
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_TAB, true, true, false, false));
+  WaitForActiveTabIndex(initial_active_index);
+  ASSERT_EQ(initial_active_index, GetActiveTabIndex());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // The newly created tab should be focused.
+  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_TAB, true, false, false, false));
+  WaitForInactiveTabIndex(initial_active_index);
+  ASSERT_NE(initial_active_index, GetActiveTabIndex());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // The newly created tab should be closed.
+  ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
+  WaitForTabCount(initial_tab_count);
+  ASSERT_EQ(initial_active_index, GetActiveTabIndex());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // A new window should be created and focused.
+  ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_N));
+  WaitForBrowserCount(initial_browser_count + 1);
+  ASSERT_EQ(initial_browser_count + 1, GetBrowserCount());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
+
+  // The newly created window should be closed.
+  ASSERT_NO_FATAL_FAILURE(SendShiftShortcut(ui::VKEY_W));
+  WaitForBrowserCount(initial_browser_count);
+
+  ASSERT_EQ(initial_browser_count, GetBrowserCount());
+  ASSERT_EQ(initial_active_index, GetActiveTabIndex());
+
+  ASSERT_NO_FATAL_FAILURE(enter_fullscreen());
 }
 
 void BrowserCommandControllerInteractiveTest::FinishTestAndVerifyResult() {
@@ -232,12 +415,12 @@
   // sent. So we sent a KeyX to the webpage to indicate the end of the test
   // case. After processing this key event, web page is safe to send the record
   // back through window.domAutomationController.
-  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_X, false,
-                                              false, false, false));
+  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
+      GetActiveBrowser(), ui::VKEY_X, false, false, false, false));
   expected_result_ += "KeyX ctrl:false shift:false alt:false meta:false";
   std::string result;
   EXPECT_TRUE(content::ExecuteScriptAndExtractString(
-      browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
+      GetActiveWebContents()->GetRenderViewHost(),
       "getKeyEventReport();", &result));
   NormalizeMetaKeyForMacOS(&result);
   NormalizeMetaKeyForMacOS(&expected_result_);
@@ -246,30 +429,28 @@
 }
 
 void BrowserCommandControllerInteractiveTest::SetUpOnMainThread() {
-  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
+  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(GetActiveBrowser()));
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserCommandControllerInteractiveTest,
                        ShortcutsShouldTakeEffectInWindowMode) {
-  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+  ASSERT_EQ(1, GetTabCount());
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
-  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+  ASSERT_EQ(2, GetTabCount());
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_T));
-  ASSERT_EQ(3, browser()->tab_strip_model()->count());
+  ASSERT_EQ(3, GetTabCount());
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
-  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+  ASSERT_EQ(2, GetTabCount());
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_W));
-  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+  ASSERT_EQ(1, GetTabCount());
   ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
-  ASSERT_TRUE(browser()
-                  ->exclusive_access_manager()
-                  ->fullscreen_controller()
-                  ->IsFullscreenForBrowser());
+  ASSERT_TRUE(IsInBrowserFullscreen());
+  ASSERT_FALSE(IsActiveTabFullscreen());
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserCommandControllerInteractiveTest,
                        UnpreservedShortcutsShouldBePreventable) {
-  ASSERT_NO_FATAL_FAILURE(StartTestPage());
+  ASSERT_NO_FATAL_FAILURE(StartFullscreenLockPage());
 
   // The browser print function should be blocked by the web page.
   ASSERT_NO_FATAL_FAILURE(SendShortcut(ui::VKEY_P));
@@ -290,23 +471,29 @@
 IN_PROC_BROWSER_TEST_F(
     BrowserCommandControllerInteractiveTest,
     MAYBE_KeyEventsShouldBeConsumedByWebPageInBrowserFullscreen) {
-  ASSERT_NO_FATAL_FAILURE(StartTestPage());
+  ASSERT_NO_FATAL_FAILURE(StartFullscreenLockPage());
 
   ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
-  ASSERT_NO_FATAL_FAILURE(SendShortcutsInFullscreen());
+  ASSERT_FALSE(IsActiveTabFullscreen());
+  ASSERT_TRUE(IsInBrowserFullscreen());
+  ASSERT_NO_FATAL_FAILURE(SendShortcutsAndExpectPrevented());
   // Current page should not exit browser fullscreen mode.
   ASSERT_NO_FATAL_FAILURE(SendEscape());
 
   ASSERT_NO_FATAL_FAILURE(FinishTestAndVerifyResult());
+
+  ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
+  ASSERT_FALSE(IsActiveTabFullscreen());
+  ASSERT_FALSE(IsInBrowserFullscreen());
 }
 
 IN_PROC_BROWSER_TEST_F(
     BrowserCommandControllerInteractiveTest,
     KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForEsc) {
-  ASSERT_NO_FATAL_FAILURE(StartTestPage());
+  ASSERT_NO_FATAL_FAILURE(StartFullscreenLockPage());
 
   ASSERT_NO_FATAL_FAILURE(SendJsFullscreenShortcutAndWait());
-  ASSERT_NO_FATAL_FAILURE(SendShortcutsInFullscreen());
+  ASSERT_NO_FATAL_FAILURE(SendShortcutsAndExpectPrevented());
   // Current page should exit HTML fullscreen mode.
   ASSERT_NO_FATAL_FAILURE(SendEscapeAndWaitForExitingFullscreen());
 
@@ -316,21 +503,70 @@
 IN_PROC_BROWSER_TEST_F(
     BrowserCommandControllerInteractiveTest,
     KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11) {
-  ASSERT_NO_FATAL_FAILURE(StartTestPage());
+  ASSERT_NO_FATAL_FAILURE(StartFullscreenLockPage());
 
   ASSERT_NO_FATAL_FAILURE(SendJsFullscreenShortcutAndWait());
-  ASSERT_NO_FATAL_FAILURE(SendShortcutsInFullscreen());
+  ASSERT_NO_FATAL_FAILURE(SendShortcutsAndExpectPrevented());
 #if defined(OS_MACOSX)
   // On 10.9 or earlier, sending the exit fullscreen shortcut will crash the
   // binary. See http://crbug.com/740250.
   if (base::mac::IsAtLeastOS10_10()) {
     // Current page should exit browser fullscreen mode.
     ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
+    ASSERT_FALSE(IsActiveTabFullscreen());
+    ASSERT_FALSE(IsInBrowserFullscreen());
   }
 #else
   // Current page should exit browser fullscreen mode.
   ASSERT_NO_FATAL_FAILURE(SendFullscreenShortcutAndWait());
+  ASSERT_FALSE(IsActiveTabFullscreen());
+  ASSERT_FALSE(IsInBrowserFullscreen());
 #endif
 
   ASSERT_NO_FATAL_FAILURE(FinishTestAndVerifyResult());
 }
+
+#if defined(OS_MACOSX)
+// TODO(zijiehe): Figure out why this test crashes on Mac OSX. The suspicious
+// command is "SendFullscreenShortcutAndWait()". See, http://crbug.com/738949.
+#define MAYBE_ShortcutsShouldTakeEffectInBrowserFullscreen \
+        DISABLED_ShortcutsShouldTakeEffectInBrowserFullscreen
+#else
+#define MAYBE_ShortcutsShouldTakeEffectInBrowserFullscreen \
+        ShortcutsShouldTakeEffectInBrowserFullscreen
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserCommandControllerInteractiveTest,
+                       MAYBE_ShortcutsShouldTakeEffectInBrowserFullscreen) {
+#if defined(OS_MACOSX)
+  // On 10.9 or earlier, sending the exit fullscreen shortcut will crash the
+  // binary. See http://crbug.com/740250.
+  if (base::mac::IsAtMostOS10_9())
+    return;
+#endif
+  ASSERT_NO_FATAL_FAILURE(SendShortcutsAndExpectNotPrevented(false));
+}
+
+#if !defined(OS_MACOSX)
+// HTML fullscreen is automatically exited after some commands are executed,
+// such as Ctrl + T (new tab). But some commands won't have this effect, such as
+// Ctrl + N (new window).
+// On Mac OSX, AppKit implementation is used for HTML fullscreen mode. Entering
+// and exiting AppKit fullscreen mode triggers an animation. A
+// FullscreenChangeObserver is needed to ensure the animation is finished. But
+// the FullscreenChangeObserver won't finish if the command actually won't cause
+// the page to exit fullscreen mode. So we need to maintain a list of exiting /
+// non-exiting commands, which is not the goal of this test.
+
+#if defined(OS_CHROMEOS)
+// This test is flaky on ChromeOS, see bug http://crbug.com/754878.
+#define MAYBE_ShortcutsShouldTakeEffectInJsFullscreen \
+        DISABLED_ShortcutsShouldTakeEffectInJsFullscreen
+#else
+#define MAYBE_ShortcutsShouldTakeEffectInJsFullscreen \
+        ShortcutsShouldTakeEffectInJsFullscreen
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserCommandControllerInteractiveTest,
+                       MAYBE_ShortcutsShouldTakeEffectInJsFullscreen) {
+  ASSERT_NO_FATAL_FAILURE(SendShortcutsAndExpectNotPrevented(true));
+}
+#endif
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 22789838..f8ca4ec 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -245,12 +245,6 @@
   return GURL(std::string(kChromeUISettingsURL) + sub_page);
 }
 
-bool IsSettingsSubPage(const GURL& url, const std::string& sub_page) {
-  return (url.SchemeIs(content::kChromeUIScheme) &&
-          (url.host_piece() == chrome::kChromeUISettingsHost) &&
-          url.path_piece() == "/" + sub_page);
-}
-
 bool IsTrustedPopupWindowWithScheme(const Browser* browser,
                                     const std::string& scheme) {
   if (!browser->is_type_popup() || !browser->is_trusted_source())
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h
index 9c0031f..92a4fde 100644
--- a/chrome/browser/ui/chrome_pages.h
+++ b/chrome/browser/ui/chrome_pages.h
@@ -78,9 +78,6 @@
 // Constructs a settings GURL for the specified |sub_page|.
 GURL GetSettingsUrl(const std::string& sub_page);
 
-// Returns true if |url| is the URL for the settings subpage |sub_page|.
-bool IsSettingsSubPage(const GURL& url, const std::string& sub_page);
-
 // Returns true if |browser| is a trusted popup window containing a page with
 // matching |scheme| (or any trusted popup if |scheme| is empty).
 bool IsTrustedPopupWindowWithScheme(const Browser* browser,
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
index 594a3f7..a3a729a 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
@@ -129,7 +129,7 @@
   bool IsFullscreenOrPending() const override;
   void UpdateWindowIcon() override;
   void UpdateWindowTitle() override;
-  void UpdateShape(std::unique_ptr<SkRegion> region) override;
+  void UpdateShape(std::unique_ptr<ShapeRects> rects) override;
   void UpdateDraggableRegions(
       const std::vector<extensions::DraggableRegion>& regions) override;
   SkRegion* GetDraggableRegion() override;
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
index 595af1d..ae8dece3 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
@@ -540,7 +540,7 @@
   [window() setTitle:base::SysUTF16ToNSString(title)];
 }
 
-void NativeAppWindowCocoa::UpdateShape(std::unique_ptr<SkRegion> region) {
+void NativeAppWindowCocoa::UpdateShape(std::unique_ptr<ShapeRects> rects) {
   NOTIMPLEMENTED();
 }
 
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 198b67c..7c3565a 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -2003,34 +2003,6 @@
   display::Screen* screen = display::Screen::GetScreen();
   BOOL hasMultipleMonitors = screen && screen->GetNumDisplays() > 1;
 
-  if (base::FeatureList::IsEnabled(features::kContentFullscreen)) {
-    // Getting the current's window view and its boundaries.
-    NSWindow* window = [self window];
-    WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
-    NSView* view = contents->GetNativeView();
-    NSRect windowFrame = window.frame;
-    NSRect viewFrame = [view convertRect:view.bounds toView:nil];
-
-    // Moving the origin from the lower-left corner to the upper-left corner of
-    // the view and cropping out the scrollbar
-    viewFrame.origin.y = NSHeight(windowFrame) - NSMaxY(viewFrame);
-    viewFrame.size.width -= gfx::scrollbar_size();
-
-    // Taking a screenshot of the view and creating the custom view to display
-    CGImageRef windowScreenshot = (CGImageRef)[(id)CGWindowListCreateImage(
-        CGRectZero, kCGWindowListOptionIncludingWindow, [window windowNumber],
-        kCGWindowImageBoundsIgnoreFraming) autorelease];
-    CGImageRef viewScreenshot = (CGImageRef)[(id)CGImageCreateWithImageInRect(
-        windowScreenshot, [window convertRectToBacking:viewFrame]) autorelease];
-    FullscreenPlaceholderView* screenshotView =
-        [[[FullscreenPlaceholderView alloc]
-            initWithFrame:[[self tabContentArea] bounds]
-                    image:viewScreenshot] autorelease];
-    screenshotView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
-
-    [[self tabContentArea] addSubview:screenshotView];
-  }
-
   if (base::mac::IsAtLeastOS10_10() &&
       !(hasMultipleMonitors && ![NSScreen screensHaveSeparateSpaces])) {
     [self enterAppKitFullscreen];
diff --git a/chrome/browser/ui/cocoa/separate_fullscreen_window.h b/chrome/browser/ui/cocoa/separate_fullscreen_window.h
new file mode 100644
index 0000000..d6b211a
--- /dev/null
+++ b/chrome/browser/ui/cocoa/separate_fullscreen_window.h
@@ -0,0 +1,20 @@
+// Copyright (c) 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_UI_COCOA_SEPARATE_FULLSCREEN_WINDOW_H_
+#define CHROME_BROWSER_UI_COCOA_SEPARATE_FULLSCREEN_WINDOW_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "ui/base/cocoa/touch_bar_forward_declarations.h"
+
+@interface SeparateFullscreenWindow : NSWindow
+// This window class is instantiated to display a WebContentsViewCocoa as its
+// subview in fullscreen mode as part of ContentFullscreen to enable users to
+// interact with the main browser window and its tab when they're displaying
+// content in this separate window. Related types include
+// FullscreenLowPowerWindow.
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_SEPARATE_FULLSCREEN_WINDOW_H_
diff --git a/chrome/browser/ui/cocoa/separate_fullscreen_window.mm b/chrome/browser/ui/cocoa/separate_fullscreen_window.mm
new file mode 100644
index 0000000..89f07cf5
--- /dev/null
+++ b/chrome/browser/ui/cocoa/separate_fullscreen_window.mm
@@ -0,0 +1,17 @@
+// Copyright (c) 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.
+
+#import "chrome/browser/ui/cocoa/separate_fullscreen_window.h"
+
+@implementation SeparateFullscreenWindow
+
+- (BOOL)canBecomeKeyWindow {
+  return YES;
+}
+
+- (BOOL)canBecomeMainWindow {
+  return YES;
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h
index f04b529..7fe6d64 100644
--- a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h
+++ b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h
@@ -47,6 +47,13 @@
    // Set to true if the window is a popup.
    BOOL isPopup_;
 
+   // Reference to the FullscreenPlaceholderView displayed in the main window
+   // for the tab when our WebContentsView is in the SeparateFullscreenWindow.
+   NSView* fullscreenPlaceholderView_;
+   // Reference to the fullscreen window created to display the WebContents
+   // view separately.
+   NSWindow* separateFullscreenWindow_;
+
    base::scoped_nsobject<WebTextfieldTouchBarController> touchBarController_;
 }
 @property(readonly, nonatomic) content::WebContents* webContents;
diff --git a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
index 359e1a97..5ba9768 100644
--- a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
@@ -8,15 +8,20 @@
 
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #import "chrome/browser/themes/theme_properties.h"
 #import "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/ui/cocoa/fullscreen_placeholder_view.h"
+#include "chrome/browser/ui/cocoa/separate_fullscreen_window.h"
 #import "chrome/browser/ui/cocoa/themed_window.h"
 #import "chrome/browser/ui/cocoa/web_textfield_touch_bar_controller.h"
+#include "chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.h"
 #include "chrome/browser/ui/view_ids.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/grit/theme_resources.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
@@ -27,6 +32,7 @@
 #include "ui/base/cocoa/animation_utils.h"
 #import "ui/base/cocoa/touch_bar_forward_declarations.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/scrollbar_size.h"
 
 using content::WebContents;
 using content::WebContentsObserver;
@@ -62,7 +68,7 @@
 
   void DidToggleFullscreenModeForTab(bool entered_fullscreen,
                                      bool will_cause_resize) override {
-    [controller_ toggleFullscreenWidget:YES];
+    [controller_ toggleFullscreenWidget:entered_fullscreen];
   }
 
  private:
@@ -204,6 +210,15 @@
 
 @end  // @implementation TabContentsContainerView
 
+@interface TabContentsController (
+    SeparateFullscreenWindowDelegate)<NSWindowDelegate>
+
+- (NSView*)createScreenshotView;
+
+- (NSWindow*)createSeparateWindowForTab:(content::WebContents*)separatedTab;
+
+@end
+
 @implementation TabContentsController
 @synthesize webContents = contents_;
 @synthesize blockFullscreenResize = blockFullscreenResize_;
@@ -249,7 +264,9 @@
   content::RenderWidgetHostView* const fullscreenView =
       isEmbeddingFullscreenWidget_ ?
       contents_->GetFullscreenRenderWidgetHostView() : NULL;
-  if (fullscreenView) {
+  if (fullscreenPlaceholderView_) {
+    contentsNativeView = fullscreenPlaceholderView_;
+  } else if (fullscreenView) {
     contentsNativeView = fullscreenView->GetNativeView();
   } else {
     isEmbeddingFullscreenWidget_ = NO;
@@ -344,6 +361,17 @@
 - (void)toggleFullscreenWidget:(BOOL)enterFullscreen {
   isEmbeddingFullscreenWidget_ = enterFullscreen &&
       contents_ && contents_->GetFullscreenRenderWidgetHostView();
+  if (base::FeatureList::IsEnabled(features::kContentFullscreen)) {
+    if (enterFullscreen) {
+      fullscreenPlaceholderView_ = [self createScreenshotView];
+      separateFullscreenWindow_ = [self createSeparateWindowForTab:contents_];
+
+      [separateFullscreenWindow_ makeKeyAndOrderFront:nil];
+      [separateFullscreenWindow_ toggleFullScreen:nil];
+    } else {
+      [separateFullscreenWindow_ close];
+    }
+  }
   [self ensureContentsVisibleInSuperview:[[self view] superview]];
 }
 
@@ -411,3 +439,85 @@
 }
 
 @end
+
+@implementation TabContentsController (SeparateFullscreenWindowDelegate)
+
+- (void)windowDidEnterFullScreen:(NSNotification*)notification {
+  // Make the RenderWidgetHostViewCocoa the firstResponder for the
+  // SeparateFullscreenWindow.
+  contents_->Focus();
+}
+
+- (void)windowWillExitFullScreen:(NSNotification*)notification {
+  // Remove the screenshot view so that the WebContentsViewCocoa is
+  // retrieved and displayed again in the original window.
+  fullscreenPlaceholderView_ = nil;
+  [self ensureContentsVisibleInSuperview:[[self view] superview]];
+
+  // When exiting through the title bar Exit Fullscreen Window button, the
+  // WebContents must be notified of the change in fullscreen (like in
+  // FullscreenController::HandleUserPressedEscape).
+  contents_->ExitFullscreen(true);
+}
+
+- (void)windowDidExitFullScreen:(NSNotification*)notification {
+  // When exiting through the title bar Exit Fullscreen Window button, the
+  // SeparateFullscreenWindow doesn't close, so make sure it's closed.
+  [separateFullscreenWindow_ close];
+  separateFullscreenWindow_ = nil;
+}
+
+- (NSView*)createScreenshotView {
+  // Getting the current's window view and its boundaries.
+  NSWindow* window = [contents_->GetNativeView() window];
+  NSView* view = contents_->GetNativeView();
+  NSRect windowFrame = window.frame;
+  NSRect viewFrame = [view convertRect:view.bounds toView:nil];
+
+  // Moving the origin from the lower-left corner to the upper-left corner of
+  // the view and cropping out the scrollbar
+  viewFrame.origin.y = NSHeight(windowFrame) - NSMaxY(viewFrame);
+  viewFrame.size.width -= gfx::scrollbar_size();
+
+  // Taking a screenshot of the view and creating the custom view to display
+  CGImageRef windowScreenshot = (CGImageRef)[(id)CGWindowListCreateImage(
+      CGRectZero, kCGWindowListOptionIncludingWindow, [window windowNumber],
+      kCGWindowImageBoundsIgnoreFraming) autorelease];
+  CGImageRef viewScreenshot = (CGImageRef)[(id)CGImageCreateWithImageInRect(
+      windowScreenshot, [window convertRectToBacking:viewFrame]) autorelease];
+  FullscreenPlaceholderView* screenshotView =
+      [[[FullscreenPlaceholderView alloc] initWithFrame:[[self view] bounds]
+                                                  image:viewScreenshot]
+          autorelease];
+  screenshotView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
+
+  return screenshotView;
+}
+
+// Creates a new window with the tab without detaching it from its source
+// window.
+- (NSWindow*)createSeparateWindowForTab:(WebContents*)separatedTab {
+  DCHECK(separatedTab->GetNativeView());
+
+  NSView* separatedTabView = separatedTab->GetNativeView();
+  NSWindow* sourceWindow = [separatedTabView window];
+  NSRect windowRect =
+      [separatedTabView convertRect:[separatedTabView bounds] toView:nil];
+  SeparateFullscreenWindow* separateWindow = [[SeparateFullscreenWindow alloc]
+      initWithContentRect:[sourceWindow convertRectToScreen:windowRect]
+                styleMask:NSResizableWindowMask
+                  backing:NSBackingStoreBuffered
+                    defer:NO];
+
+  [separateWindow setDelegate:self];
+  [[separateWindow contentView] addSubview:separatedTabView];
+  [separateWindow
+      setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+
+  // Make TabContentsContainerView the first responder now as
+  // WebContentsViewCocoa is now in a separate window.
+  [sourceWindow makeFirstResponder:[self view]];
+
+  return separateWindow;
+}
+@end
diff --git a/chrome/browser/ui/cocoa/toolbar/app_toolbar_button.mm b/chrome/browser/ui/cocoa/toolbar/app_toolbar_button.mm
index ddc0eb5..99ff818c 100644
--- a/chrome/browser/ui/cocoa/toolbar/app_toolbar_button.mm
+++ b/chrome/browser/ui/cocoa/toolbar/app_toolbar_button.mm
@@ -4,7 +4,6 @@
 
 #import "chrome/browser/ui/cocoa/toolbar/app_toolbar_button.h"
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #import "chrome/browser/themes/theme_properties.h"
@@ -12,7 +11,7 @@
 #include "chrome/browser/ui/cocoa/animated_icon.h"
 #import "chrome/browser/ui/cocoa/themed_window.h"
 #import "chrome/browser/ui/cocoa/view_id_util.h"
-#include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/grit/chromium_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/material_design/material_design_controller.h"
@@ -33,8 +32,7 @@
   if ((self = [super initWithFrame:frame])) {
     [self commonInit];
 
-    base::CommandLine* commandLine = base::CommandLine::ForCurrentProcess();
-    if (commandLine->HasSwitch(switches::kEnableNewAppMenuIcon)) {
+    if (base::FeatureList::IsEnabled(features::kAnimatedAppMenuIcon)) {
       animatedIcon_.reset(new AnimatedIcon(kBrowserToolsAnimatedIcon, self));
       [self updateAnimatedIconColor];
 
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
index dace90e..fab299d 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
@@ -32,7 +32,10 @@
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/extension.h"
 
-#if !defined(OS_MACOSX)
+#if defined(OS_MACOSX)
+#include "base/feature_list.h"
+#include "chrome/common/chrome_features.h"
+#else
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #endif
@@ -96,7 +99,7 @@
 
 bool FullscreenController::IsFullscreenForTabOrPending(
     const WebContents* web_contents) const {
-  if (IsFullscreenForCapturedTab(web_contents))
+  if (IsFullscreenWithinTab(web_contents))
     return true;
   if (web_contents == exclusive_access_tab()) {
     DCHECK(web_contents ==
@@ -114,7 +117,7 @@
                                                      const GURL& origin) {
   DCHECK(web_contents);
 
-  if (MaybeToggleFullscreenForCapturedTab(web_contents, true)) {
+  if (MaybeToggleFullscreenWithinTab(web_contents, true)) {
     // During tab capture of fullscreen-within-tab views, the browser window
     // fullscreen state is unchanged, so return now.
     return;
@@ -158,7 +161,7 @@
 }
 
 void FullscreenController::ExitFullscreenModeForTab(WebContents* web_contents) {
-  if (MaybeToggleFullscreenForCapturedTab(web_contents, false)) {
+  if (MaybeToggleFullscreenWithinTab(web_contents, false)) {
     // During tab capture of fullscreen-within-tab views, the browser window
     // fullscreen state is unchanged, so return now.
     return;
@@ -199,7 +202,7 @@
 }
 
 void FullscreenController::OnTabDetachedFromView(WebContents* old_contents) {
-  if (!IsFullscreenForCapturedTab(old_contents))
+  if (!IsFullscreenWithinTab(old_contents))
     return;
 
   // A fullscreen-within-tab view undergoing screen capture has been detached
@@ -231,7 +234,7 @@
 }
 
 void FullscreenController::OnTabClosing(WebContents* web_contents) {
-  if (IsFullscreenForCapturedTab(web_contents))
+  if (IsFullscreenWithinTab(web_contents))
     web_contents->ExitFullscreen(
         /* will_cause_resize */ IsFullscreenCausedByTab());
   else
@@ -264,7 +267,7 @@
 bool FullscreenController::HandleUserPressedEscape() {
   WebContents* const active_web_contents =
       exclusive_access_manager()->context()->GetActiveWebContents();
-  if (IsFullscreenForCapturedTab(active_web_contents)) {
+  if (IsFullscreenWithinTab(active_web_contents)) {
     active_web_contents->ExitFullscreen(
         /* will_cause_resize */ IsFullscreenCausedByTab());
     return true;
@@ -415,17 +418,22 @@
   is_privileged_fullscreen_for_testing_ = is_privileged;
 }
 
-bool FullscreenController::MaybeToggleFullscreenForCapturedTab(
-    WebContents* web_contents, bool enter_fullscreen) {
+bool FullscreenController::MaybeToggleFullscreenWithinTab(
+    WebContents* web_contents,
+    bool enter_fullscreen) {
   if (enter_fullscreen) {
-    if (web_contents->GetCapturerCount() > 0) {
+    if (web_contents->GetCapturerCount() > 0
+#if defined(OS_MACOSX)
+        || base::FeatureList::IsEnabled(features::kContentFullscreen)
+#endif
+            ) {
       FullscreenWithinTabHelper::CreateForWebContents(web_contents);
-      FullscreenWithinTabHelper::FromWebContents(web_contents)->
-          SetIsFullscreenForCapturedTab(true);
+      FullscreenWithinTabHelper::FromWebContents(web_contents)
+          ->SetIsFullscreenWithinTab(true);
       return true;
     }
   } else {
-    if (IsFullscreenForCapturedTab(web_contents)) {
+    if (IsFullscreenWithinTab(web_contents)) {
       FullscreenWithinTabHelper::RemoveForWebContents(web_contents);
       return true;
     }
@@ -434,7 +442,7 @@
   return false;
 }
 
-bool FullscreenController::IsFullscreenForCapturedTab(
+bool FullscreenController::IsFullscreenWithinTab(
     const WebContents* web_contents) const {
   // Note: On Mac, some of the OnTabXXX() methods get called with a nullptr
   // value
@@ -442,7 +450,7 @@
   const FullscreenWithinTabHelper* const helper =
       web_contents ? FullscreenWithinTabHelper::FromWebContents(web_contents)
                    : nullptr;
-  if (helper && helper->is_fullscreen_for_captured_tab()) {
+  if (helper && helper->is_fullscreen_within_tab()) {
     DCHECK_NE(exclusive_access_tab(), web_contents);
     return true;
   }
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.h b/chrome/browser/ui/exclusive_access/fullscreen_controller.h
index 1edfeac..2449dec4 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.h
@@ -37,15 +37,25 @@
 // FullscreenWithinTab Note:
 // All fullscreen widgets are displayed within the tab contents area, and
 // FullscreenController will expand the browser window so that the tab contents
-// area fills the entire screen. However, special behavior applies when a tab is
-// being screen-captured. First, the browser window will not be
-// fullscreened. This allows the user to retain control of their desktop to work
-// in other browser tabs or applications while the fullscreen view is displayed
-// on a remote screen. Second, FullscreenController will auto-resize fullscreen
-// widgets to that of the capture video resolution when they are hidden (e.g.,
-// when a user has switched to another tab). This is both a performance and
-// quality improvement since scaling and letterboxing steps can be skipped in
-// the capture pipeline.
+// area fills the entire screen.
+// However, special behavior applies when a tab is screen-captured or the
+// content fullscreen feature is active.
+//
+// Screen-captured:
+// First, the browser window will not be fullscreened. This allows the user to
+// retain control of their desktop to work in other browser tabs or applications
+// while the fullscreen view is displayed on a remote screen. Second,
+// FullscreenController will auto-resize fullscreen widgets to that of the
+// capture video resolution when they are hidden (e.g., when a user has
+// switched to another tab). This is both a performance and quality improvement
+// since scaling and letterboxing steps can be skipped in the capture pipeline.
+//
+// Content-fullscreen (for macOS only):
+// First, the browser window will not be fullscreened. Second, the WebContents's
+// view will not be displayed in the browser window but rather in a
+// separate window, SeparateFullscreenWindow, which will be fullscreened and
+// moved to a new space. This enables the user to have both the browser window
+// and the fullscreen content displayed separately at the same time.
 
 // This class implements fullscreen behaviour.
 class FullscreenController : public ExclusiveAccessControllerBase {
@@ -155,13 +165,13 @@
 
   void SetPrivilegedFullscreenForTesting(bool is_privileged);
   // Returns true if |web_contents| was toggled into/out of fullscreen mode as a
-  // screen-captured tab. See 'FullscreenWithinTab Note'.
-  bool MaybeToggleFullscreenForCapturedTab(content::WebContents* web_contents,
-                                           bool enter_fullscreen);
+  // screen-captured tab or as a content-fullscreen tab.
+  // See 'FullscreenWithinTab Note'.
+  bool MaybeToggleFullscreenWithinTab(content::WebContents* web_contents,
+                                      bool enter_fullscreen);
   // Returns true if |web_contents| is in fullscreen mode as a screen-captured
   // tab. See 'FullscreenWithinTab Note'.
-  bool IsFullscreenForCapturedTab(const content::WebContents* web_contents)
-      const;
+  bool IsFullscreenWithinTab(const content::WebContents* web_contents) const;
 
   // Helper methods that should be used in a TAB context.
   GURL GetRequestingOrigin() const;
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.cc b/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.cc
index 374318a..2101c35 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.cc
@@ -8,7 +8,7 @@
 
 FullscreenWithinTabHelper::FullscreenWithinTabHelper(
     content::WebContents* ignored)
-    : is_fullscreen_for_captured_tab_(false) {}
+    : is_fullscreen_within_tab_(false) {}
 
 FullscreenWithinTabHelper::~FullscreenWithinTabHelper() {}
 
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.h b/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.h
index e201bb79..37e91b4 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_within_tab_helper.h
@@ -24,12 +24,10 @@
  public:
   ~FullscreenWithinTabHelper() override;
 
-  bool is_fullscreen_for_captured_tab() const {
-    return is_fullscreen_for_captured_tab_;
-  }
+  bool is_fullscreen_within_tab() const { return is_fullscreen_within_tab_; }
 
-  void SetIsFullscreenForCapturedTab(bool is_fullscreen) {
-    is_fullscreen_for_captured_tab_ = is_fullscreen;
+  void SetIsFullscreenWithinTab(bool is_fullscreen) {
+    is_fullscreen_within_tab_ = is_fullscreen;
   }
 
   // Immediately remove and destroy the FullscreenWithinTabHelper instance
@@ -40,7 +38,7 @@
   friend class content::WebContentsUserData<FullscreenWithinTabHelper>;
   explicit FullscreenWithinTabHelper(content::WebContents* ignored);
 
-  bool is_fullscreen_for_captured_tab_;
+  bool is_fullscreen_within_tab_;
 
   DISALLOW_COPY_AND_ASSIGN(FullscreenWithinTabHelper);
 };
diff --git a/chrome/browser/ui/input_method/input_method_engine.cc b/chrome/browser/ui/input_method/input_method_engine.cc
index 8ebbd4b..9bb8ca7 100644
--- a/chrome/browser/ui/input_method/input_method_engine.cc
+++ b/chrome/browser/ui/input_method/input_method_engine.cc
@@ -138,10 +138,10 @@
   composition_.CopyFrom(composition_text);
 
   // Use a black thin underline by default.
-  if (composition_.underlines.empty()) {
-    composition_.underlines.push_back(
-        ui::CompositionUnderline(0, composition_.text.length(), SK_ColorBLACK,
-                                 false /* thick */, SK_ColorTRANSPARENT));
+  if (composition_.ime_text_spans.empty()) {
+    composition_.ime_text_spans.push_back(
+        ui::ImeTextSpan(0, composition_.text.length(), SK_ColorBLACK,
+                        false /* thick */, SK_ColorTRANSPARENT));
   }
 
   ui::IMEInputContextHandlerInterface* input_context =
diff --git a/chrome/browser/ui/input_method/input_method_engine_base.cc b/chrome/browser/ui/input_method/input_method_engine_base.cc
index b030117..120c0440 100644
--- a/chrome/browser/ui/input_method/input_method_engine_base.cc
+++ b/chrome/browser/ui/input_method/input_method_engine_base.cc
@@ -210,26 +210,26 @@
   // TODO: Add support for displaying selected text in the composition string.
   for (std::vector<SegmentInfo>::const_iterator segment = segments.begin();
        segment != segments.end(); ++segment) {
-    ui::CompositionUnderline underline;
+    ui::ImeTextSpan ime_text_span;
 
     switch (segment->style) {
       case SEGMENT_STYLE_UNDERLINE:
-        underline.color = SK_ColorBLACK;
+        ime_text_span.color = SK_ColorBLACK;
         break;
       case SEGMENT_STYLE_DOUBLE_UNDERLINE:
-        underline.color = SK_ColorBLACK;
-        underline.thick = true;
+        ime_text_span.color = SK_ColorBLACK;
+        ime_text_span.thick = true;
         break;
       case SEGMENT_STYLE_NO_UNDERLINE:
-        underline.color = SK_ColorTRANSPARENT;
+        ime_text_span.color = SK_ColorTRANSPARENT;
         break;
       default:
         continue;
     }
 
-    underline.start_offset = segment->start;
-    underline.end_offset = segment->end;
-    composition_text_->underlines.push_back(underline);
+    ime_text_span.start_offset = segment->start;
+    ime_text_span.end_offset = segment->end;
+    composition_text_->ime_text_spans.push_back(ime_text_span);
   }
 
   // TODO(nona): Makes focus out mode configuable, if necessary.
diff --git a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
index 5e58c77..441bbec 100644
--- a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
+++ b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
@@ -33,7 +33,7 @@
 const char kHighContrastExtensionUrl[] =
     "https://chrome.google.com/webstore/detail/djcfdncoelnlbldjfhinnjlhdjlikmph";
 const char kDarkThemeSearchUrl[] =
-    "https://chrome.google.com/webstore/search-themes/dark";
+    "https://chrome.google.com/webstore/category/collection/dark_themes";
 const char kLearnMoreUrl[] =
     "https://groups.google.com/a/googleproductforums.com/d/topic/chrome/Xrco2HsXS-8/discussion";
 
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
index af8347b..92bf067b 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
@@ -23,6 +23,7 @@
 #include "components/zoom/zoom_controller.h"
 #include "extensions/browser/app_window/app_delegate.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/skia_util.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/widget/widget.h"
 
@@ -357,7 +358,17 @@
   return widget()->IsFullscreen();
 }
 
-void ChromeNativeAppWindowViews::UpdateShape(std::unique_ptr<SkRegion> region) {
+void ChromeNativeAppWindowViews::UpdateShape(
+    std::unique_ptr<ShapeRects> rects) {
+  shape_rects_ = std::move(rects);
+
+  // Build a region from the list of rects when it is supplied.
+  std::unique_ptr<SkRegion> region;
+  if (shape_rects_) {
+    region = base::MakeUnique<SkRegion>();
+    for (const gfx::Rect& input_rect : *shape_rects_.get())
+      region->op(gfx::RectToSkIRect(input_rect), SkRegion::kUnion_Op);
+  }
   shape_ = std::move(region);
   widget()->SetShape(shape() ? base::MakeUnique<SkRegion>(*shape()) : nullptr);
   widget()->OnSizeConstraintsChanged();
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
index 3234213..4e0679c 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
@@ -21,6 +21,7 @@
   ~ChromeNativeAppWindowViews() override;
 
   SkRegion* shape() { return shape_.get(); }
+  ShapeRects* shape_rects() { return shape_rects_.get(); }
 
  protected:
   // Called before views::Widget::Init() in InitializeDefaultWindow() to allow
@@ -60,7 +61,7 @@
   // NativeAppWindow implementation.
   void SetFullscreen(int fullscreen_types) override;
   bool IsFullscreenOrPending() const override;
-  void UpdateShape(std::unique_ptr<SkRegion> region) override;
+  void UpdateShape(std::unique_ptr<ShapeRects> rects) override;
   bool HasFrameColor() const override;
   SkColor ActiveFrameColor() const override;
   SkColor InactiveFrameColor() const override;
@@ -81,6 +82,8 @@
   // default shape, usually rectangular.
   std::unique_ptr<SkRegion> shape_;
 
+  std::unique_ptr<ShapeRects> shape_rects_;
+
   bool has_frame_color_;
   SkColor active_frame_color_;
   SkColor inactive_frame_color_;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
index 8efa79e..09a14e5 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
@@ -110,10 +110,10 @@
 }
 
 void ChromeNativeAppWindowViewsAura::UpdateShape(
-    std::unique_ptr<SkRegion> region) {
+    std::unique_ptr<ShapeRects> rects) {
   bool had_shape = !!shape();
 
-  ChromeNativeAppWindowViews::UpdateShape(std::move(region));
+  ChromeNativeAppWindowViews::UpdateShape(std::move(rects));
 
   aura::Window* native_window = widget()->GetNativeWindow();
   if (shape() && !had_shape) {
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h
index 9c84e8f..229eeb1 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h
@@ -36,7 +36,7 @@
   bool IsAlwaysOnTop() const override;
 
   // NativeAppWindow implementation.
-  void UpdateShape(std::unique_ptr<SkRegion> region) override;
+  void UpdateShape(std::unique_ptr<ShapeRects> rects) override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(ShapedAppWindowTargeterTest,
diff --git a/chrome/browser/ui/views/apps/shaped_app_window_targeter.cc b/chrome/browser/ui/views/apps/shaped_app_window_targeter.cc
index 6f0e7cd..ec273d2 100644
--- a/chrome/browser/ui/views/apps/shaped_app_window_targeter.cc
+++ b/chrome/browser/ui/views/apps/shaped_app_window_targeter.cc
@@ -21,6 +21,9 @@
   if (!shape)
     return false;
 
+  // TODO(varkha): Use app_window_->shape_rects() to obtain a list of hit-test
+  // rectangles. Use the rectangles directly rather than a mask which should
+  // allow this class to inherit directly from WindowTargeter.
   shape->getBoundaryPath(mask);
   return true;
 }
diff --git a/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc b/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
index 416cd077a..8eb38820 100644
--- a/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
+++ b/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
@@ -20,6 +20,8 @@
 #include "ui/wm/core/default_activation_client.h"
 #include "ui/wm/core/easy_resize_window_targeter.h"
 
+using extensions::AppWindow;
+
 class ShapedAppWindowTargeterTest : public aura::test::AuraTestBase {
  public:
   ShapedAppWindowTargeterTest()
@@ -85,9 +87,9 @@
     EXPECT_EQ(window, move.target());
   }
 
-  std::unique_ptr<SkRegion> region(new SkRegion);
-  region->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op);
-  app_window()->UpdateShape(std::move(region));
+  auto rects = base::MakeUnique<AppWindow::ShapeRects>();
+  rects->emplace_back();
+  app_window()->UpdateShape(std::move(rects));
   {
     // With an empty custom shape, all events within the window should fall
     // through to the root window.
@@ -108,10 +110,10 @@
   //  90 +--------+     +---------+
   //              |     |
   // 130          +-----+
-  region.reset(new SkRegion);
-  region->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op);
-  region->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op);
-  app_window()->UpdateShape(std::move(region));
+  rects = base::MakeUnique<AppWindow::ShapeRects>();
+  rects->emplace_back(40, 0, 20, 100);
+  rects->emplace_back(0, 40, 100, 20);
+  app_window()->UpdateShape(std::move(rects));
   {
     // With the custom shape, the events that don't fall within the custom shape
     // will go through to the root window.
@@ -178,10 +180,10 @@
     EXPECT_EQ(window, move.target());
   }
 
-  std::unique_ptr<SkRegion> region(new SkRegion);
-  region->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op);
-  region->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op);
-  app_window()->UpdateShape(std::move(region));
+  auto rects = base::MakeUnique<AppWindow::ShapeRects>();
+  rects->emplace_back(40, 0, 20, 100);
+  rects->emplace_back(0, 40, 100, 20);
+  app_window()->UpdateShape(std::move(rects));
   {
     // With the custom shape, the events that don't fall within the custom shape
     // will go through to the root window.
@@ -193,7 +195,7 @@
 
   // Remove the custom shape. This should restore the behaviour of targeting the
   // app window for events just outside its bounds (for a resizable window).
-  app_window()->UpdateShape(std::unique_ptr<SkRegion>());
+  app_window()->UpdateShape(std::unique_ptr<AppWindow::ShapeRects>());
   SetWindowResizable(true);
   {
     ui::MouseEvent move(move_outside);
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views.cc
index 172c67a..18b14d3 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views.cc
@@ -161,13 +161,6 @@
 
 base::string16 SaveCardBubbleViews::GetDialogButtonLabel(
     ui::DialogButton button) const {
-  if (!IsAutofillUpstreamShowNewUiExperimentEnabled()) {
-    // New UI experiment disabled:
-    return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK
-                                         ? IDS_AUTOFILL_SAVE_CARD_PROMPT_ACCEPT
-                                         : IDS_NO_THANKS);
-  }
-  // New UI experiment enabled:
   switch (GetCurrentFlowStep()) {
     // Local save has two buttons:
     case LOCAL_SAVE_ONLY_STEP:
@@ -176,14 +169,17 @@
                                          : IDS_NO_THANKS);
     // Upload save has one button but it can say three different things:
     case UPLOAD_SAVE_ONLY_STEP:
-      DCHECK(button == ui::DIALOG_BUTTON_OK);
-      return l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_CARD_PROMPT_ACCEPT);
+      return l10n_util::GetStringUTF16(
+          button == ui::DIALOG_BUTTON_OK ? IDS_AUTOFILL_SAVE_CARD_PROMPT_ACCEPT
+                                         : IDS_NO_THANKS);
     case UPLOAD_SAVE_CVC_FIX_FLOW_STEP_1_OFFER_UPLOAD:
-      DCHECK(button == ui::DIALOG_BUTTON_OK);
-      return l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_CARD_PROMPT_NEXT);
+      return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK
+                                           ? IDS_AUTOFILL_SAVE_CARD_PROMPT_NEXT
+                                           : IDS_NO_THANKS);
     case UPLOAD_SAVE_CVC_FIX_FLOW_STEP_2_REQUEST_CVC:
-      DCHECK(button == ui::DIALOG_BUTTON_OK);
-      return l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_CARD_PROMPT_CONFIRM);
+      return l10n_util::GetStringUTF16(
+          button == ui::DIALOG_BUTTON_OK ? IDS_AUTOFILL_SAVE_CARD_PROMPT_CONFIRM
+                                         : IDS_NO_THANKS);
     default:
       NOTREACHED();
       return base::string16();
@@ -315,17 +311,17 @@
       views::CreateSolidBorder(1, SkColorSetA(SK_ColorBLACK, 10)));
   description_view->AddChildView(card_type_icon);
 
-  // Old UI shows last four digits and expiration.  New UI shows network and
-  // last four digits, but no expiration.
+  // Old UI shows last four digits and expiration.  New UI shows network, last
+  // four digits, and expiration.
   if (IsAutofillUpstreamShowNewUiExperimentEnabled()) {
     description_view->AddChildView(
         new views::Label(card.NetworkAndLastFourDigits()));
   } else {
     description_view->AddChildView(new views::Label(
         base::string16(kMidlineEllipsis) + card.LastFourDigits()));
-    description_view->AddChildView(
-        new views::Label(card.AbbreviatedExpirationDateForDisplay()));
   }
+  description_view->AddChildView(
+      new views::Label(card.AbbreviatedExpirationDateForDisplay()));
 
   // If applicable, add the upload explanation label.  Appears below the card
   // info when new UI experiment is disabled.
diff --git a/chrome/browser/ui/views/first_run_bubble_browsertest.cc b/chrome/browser/ui/views/first_run_bubble_browsertest.cc
new file mode 100644
index 0000000..997cb49a
--- /dev/null
+++ b/chrome/browser/ui/views/first_run_bubble_browsertest.cc
@@ -0,0 +1,29 @@
+// 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/ui/views/first_run_bubble.h"
+
+#include <string>
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+
+class FirstRunBubbleBrowserTest : public DialogBrowserTest {
+ public:
+  FirstRunBubbleBrowserTest() {}
+
+  // DialogBrowserTest:
+  void ShowDialog(const std::string& name) override {
+    FirstRunBubble::Show(browser());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FirstRunBubbleBrowserTest);
+};
+
+// Invokes a dialog that tells the user they can type into the omnibox to search
+// with Google. See test_browser_dialog.h.
+IN_PROC_BROWSER_TEST_F(FirstRunBubbleBrowserTest, InvokeDialog_default) {
+  RunDialog();
+}
diff --git a/chrome/browser/ui/views/ime/input_ime_apitest_nonchromeos.cc b/chrome/browser/ui/views/ime/input_ime_apitest_nonchromeos.cc
index 43d7fefa..4ff7555e 100644
--- a/chrome/browser/ui/views/ime/input_ime_apitest_nonchromeos.cc
+++ b/chrome/browser/ui/views/ime/input_ime_apitest_nonchromeos.cc
@@ -90,9 +90,9 @@
   // Test the input.ime.setComposition API.
   ui::CompositionText composition;
   composition.text = base::UTF8ToUTF16("test_set_composition");
-  composition.underlines.push_back(
-      ui::CompositionUnderline(0, composition.text.length(), SK_ColorBLACK,
-                               false /* thick */, SK_ColorTRANSPARENT));
+  composition.ime_text_spans.push_back(
+      ui::ImeTextSpan(0, composition.text.length(), SK_ColorBLACK,
+                      false /* thick */, SK_ColorTRANSPARENT));
   composition.selection = gfx::Range(2, 2);
   const std::vector<ui::CompositionText>& composition_history =
       client->composition_history();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 5bb4f0a..e97fa6d 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -478,7 +478,9 @@
                         g_bottom_shadow.Get().height());
 
   ui::ClipRecorder clip_recorder(paint_info.context());
-  clip_recorder.ClipRect(contents_bounds);
+  clip_recorder.ClipRect(gfx::ScaleToRoundedRect(
+      contents_bounds, paint_info.paint_recording_scale_x(),
+      paint_info.paint_recording_scale_y()));
   {
     ui::PaintRecorder recorder(paint_info.context(), size());
     SkColor background_color = result_view_at(0)->GetColor(
diff --git a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
index fa54754..527dab5 100644
--- a/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_journey_logger_browsertest.cc
@@ -71,6 +71,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 class PaymentRequestJourneyLoggerNoSupportedPaymentMethodTest
@@ -99,8 +102,31 @@
       "PaymentRequest.CheckoutFunnel.NoShow",
       JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD, 1);
 
-  // Make sure that no events were logged.
-  histogram_tester.ExpectTotalCount("PaymentRequest.Events", 0);
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 class PaymentRequestJourneyLoggerMultipleShowTest
@@ -165,6 +191,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestJourneyLoggerMultipleShowTest,
@@ -205,7 +234,7 @@
   // Make sure the correct events were logged.
   std::vector<base::Bucket> buckets =
       histogram_tester.GetAllSamples("PaymentRequest.Events");
-  ASSERT_EQ(1U, buckets.size());
+  ASSERT_EQ(2U, buckets.size());
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
   EXPECT_TRUE(buckets[0].min &
@@ -224,6 +253,31 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
+  // We log the exact same events again.
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 class PaymentRequestJourneyLoggerAllSectionStatsTest
@@ -289,6 +343,9 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the correct number of suggestions shown for each section is logged
@@ -343,6 +400,9 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 class PaymentRequestJourneyLoggerNoShippingSectionStatsTest
@@ -410,6 +470,9 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the correct number of suggestions shown for each section is logged
@@ -465,6 +528,9 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 class PaymentRequestJourneyLoggerNoContactDetailSectionStatsTest
@@ -534,6 +600,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the correct number of suggestions shown for each section is logged
@@ -593,6 +662,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 class PaymentRequestNotShownTest : public PaymentRequestBrowserTestBase {
@@ -623,7 +695,9 @@
   ASSERT_EQ(1U, buckets.size());
   // Only USER_ABORTED and CAN_MAKE_PAYMENT_FALSE should be logged.
   EXPECT_EQ(JourneyLogger::EVENT_USER_ABORTED |
-                JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE,
+                JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE |
+                JourneyLogger::EVENT_REQUEST_METHOD_OTHER |
+                JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD,
             buckets[0].min);
 
   // Make sure that the metrics that required the Payment Request to be shown
@@ -686,6 +760,9 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCompleteSuggestionsForEverythingTest,
@@ -723,6 +800,9 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -766,6 +846,9 @@
   EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 }  // namespace payments
diff --git a/chrome/browser/ui/views/toolbar/app_menu_button.cc b/chrome/browser/ui/views/toolbar/app_menu_button.cc
index 025e164..75eb9aa 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu_button.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/views/toolbar/app_menu_button.h"
 
-#include "base/command_line.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
@@ -26,7 +25,7 @@
 #include "chrome/browser/ui/views/toolbar/app_menu.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
-#include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_features.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/canvas.h"
@@ -53,8 +52,7 @@
   SetInkDropMode(InkDropMode::ON);
   SetFocusPainter(nullptr);
 
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableNewAppMenuIcon)) {
+  if (base::FeatureList::IsEnabled(features::kAnimatedAppMenuIcon)) {
     toolbar_view_->browser()->tab_strip_model()->AddObserver(this);
     should_use_new_icon_ = true;
   }
diff --git a/chrome/browser/ui/webui/chromeos/login/DEPS b/chrome/browser/ui/webui/chromeos/login/DEPS
new file mode 100644
index 0000000..d7ca3f3
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/DEPS
@@ -0,0 +1,6 @@
+specific_include_rules = {
+  # TODO(mash): Remove. http://crbug.com/720917.
+  "oobe_display_chooser\.*": [
+    "+ui/events/devices/device_data_manager.h",
+  ]
+}
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
index 2acd08b..13491ab1 100644
--- a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h"
 
+#include "base/command_line.h"
 #include "base/i18n/timezone.h"
 #include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.h"
 #include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view_observer.h"
@@ -12,6 +13,10 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
 #include "components/login/localized_values_builder.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
@@ -26,12 +31,15 @@
 
 namespace chromeos {
 
-ArcTermsOfServiceScreenHandler::ArcTermsOfServiceScreenHandler()
-    : BaseScreenHandler(kScreenId) {
+ArcTermsOfServiceScreenHandler::ArcTermsOfServiceScreenHandler(OobeUI* owner)
+    : BaseScreenHandler(kScreenId), owner_(owner) {
   set_call_js_prefix(kJsScreenPath);
 }
 
 ArcTermsOfServiceScreenHandler::~ArcTermsOfServiceScreenHandler() {
+  owner_->RemoveObserver(this);
+  chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
+      this, FROM_HERE);
   system::TimezoneSettings::GetInstance()->RemoveObserver(this);
   for (auto& observer : observer_list_)
     observer.OnViewDestroyed(this);
@@ -44,17 +52,42 @@
               &ArcTermsOfServiceScreenHandler::HandleAccept);
 }
 
-void ArcTermsOfServiceScreenHandler::UpdateTimeZone() {
-  const std::string country_code = base::CountryCodeForCurrentTimezone();
-  if (country_code == last_applied_contry_code_)
+void ArcTermsOfServiceScreenHandler::MaybeLoadPlayStoreToS(
+    bool ignore_network_state) {
+  const chromeos::NetworkState* default_network =
+      chromeos::NetworkHandler::Get()
+          ->network_state_handler()
+          ->DefaultNetwork();
+  if (!ignore_network_state && !default_network)
     return;
-  last_applied_contry_code_ = country_code;
-  CallJS("setCountryCode", country_code);
+  const std::string country_code = base::CountryCodeForCurrentTimezone();
+  CallJS("loadPlayStoreToS", country_code);
+}
+
+void ArcTermsOfServiceScreenHandler::OnCurrentScreenChanged(
+    OobeScreen current_screen,
+    OobeScreen new_screen) {
+  if (new_screen != OobeScreen::SCREEN_GAIA_SIGNIN)
+    return;
+
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(chromeos::switches::kEnableArcOOBEOptIn))
+    return;
+
+  owner_->RemoveObserver(this);
+  MaybeLoadPlayStoreToS(false);
+  StartNetworkAndTimeZoneObserving();
 }
 
 void ArcTermsOfServiceScreenHandler::TimezoneChanged(
     const icu::TimeZone& timezone) {
-  UpdateTimeZone();
+  MaybeLoadPlayStoreToS(false);
+}
+
+void ArcTermsOfServiceScreenHandler::DefaultNetworkChanged(
+    const NetworkState* network) {
+  MaybeLoadPlayStoreToS(false);
 }
 
 void ArcTermsOfServiceScreenHandler::DeclareLocalizedValues(
@@ -140,10 +173,24 @@
   pref_handler_.reset();
 }
 
-void ArcTermsOfServiceScreenHandler::Initialize() {
-  if (!show_on_init_)
+void ArcTermsOfServiceScreenHandler::StartNetworkAndTimeZoneObserving() {
+  if (network_time_zone_observing_)
     return;
 
+  chromeos::NetworkHandler::Get()->network_state_handler()->AddObserver(
+      this, FROM_HERE);
+  system::TimezoneSettings::GetInstance()->AddObserver(this);
+  network_time_zone_observing_ = true;
+}
+
+void ArcTermsOfServiceScreenHandler::Initialize() {
+  if (!show_on_init_) {
+    // Send time zone information as soon as possible to able to pre-load the
+    // Play Store ToS.
+    owner_->AddObserver(this);
+    return;
+  }
+
   Show();
   show_on_init_ = false;
 }
@@ -158,11 +205,11 @@
   // ToS then prefs::kArcEnabled is automatically reset in ArcSessionManager.
   profile->GetPrefs()->SetBoolean(prefs::kArcEnabled, true);
 
-  system::TimezoneSettings::GetInstance()->AddObserver(this);
-
   ShowScreen(kScreenId);
 
-  UpdateTimeZone();
+  MaybeLoadPlayStoreToS(true);
+  StartNetworkAndTimeZoneObserving();
+
   pref_handler_.reset(new arc::ArcOptInPreferenceHandler(
       this, profile->GetPrefs()));
   pref_handler_->Start();
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
index f37747ee..4500256 100644
--- a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.h
@@ -13,6 +13,8 @@
 #include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler_observer.h"
 #include "chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view.h"
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chromeos/network/network_state_handler_observer.h"
 #include "chromeos/settings/timezone_settings.h"
 
 namespace arc {
@@ -26,9 +28,11 @@
     : public BaseScreenHandler,
       public ArcTermsOfServiceScreenView,
       public arc::ArcOptInPreferenceHandlerObserver,
-      public system::TimezoneSettings::Observer {
+      public OobeUI::Observer,
+      public system::TimezoneSettings::Observer,
+      public chromeos::NetworkStateHandlerObserver {
  public:
-  ArcTermsOfServiceScreenHandler();
+  explicit ArcTermsOfServiceScreenHandler(OobeUI* owner);
   ~ArcTermsOfServiceScreenHandler() override;
 
   // content::WebUIMessageHandler:
@@ -44,9 +48,16 @@
   void Show() override;
   void Hide() override;
 
+  // OobeUI::Observer:
+  void OnCurrentScreenChanged(OobeScreen current_screen,
+                              OobeScreen new_screen) override;
+
   // system::TimezoneSettings::Observer:
   void TimezoneChanged(const icu::TimeZone& timezone) override;
 
+  // chromeos::NetworkStateHandlerObserver:
+  void DefaultNetworkChanged(const NetworkState* network) override;
+
  private:
   // BaseScreenHandler:
   void Initialize() override;
@@ -55,7 +66,11 @@
   void HandleSkip();
   void HandleAccept(bool enable_backup_restore,
                     bool enable_location_services);
-  void UpdateTimeZone();
+  // Loads Play Store ToS content in case default network exists. If
+  // |ignore_network_state| is set then network state is not checked.
+  void MaybeLoadPlayStoreToS(bool ignore_network_state);
+
+  void StartNetworkAndTimeZoneObserving();
 
   // arc::ArcOptInPreferenceHandlerObserver:
   void OnMetricsModeChanged(bool enabled, bool managed) override;
@@ -64,11 +79,14 @@
 
   base::ObserverList<ArcTermsOfServiceScreenViewObserver, true> observer_list_;
 
+  // Unowned pointer;
+  OobeUI* const owner_;
+
   // Whether the screen should be shown right after initialization.
   bool show_on_init_ = false;
 
-  // To prevent redundant updates, keep last country code used for update.
-  std::string last_applied_contry_code_;
+  // Indicates that we already started network and time zone observing.
+  bool network_time_zone_observing_ = false;
 
   std::unique_ptr<arc::ArcOptInPreferenceHandler> pref_handler_;
 
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 810cc0b..4359367 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -331,6 +331,13 @@
     params.SetString("enterpriseEnrollmentDomain",
                      enterprise_enrollment_domain);
   }
+  params.SetBoolean("enterpriseManagedDevice",
+                    g_browser_process->platform_part()
+                        ->browser_policy_connector_chromeos()
+                        ->IsEnterpriseManaged());
+  params.SetBoolean(
+      "hasDeviceOwner",
+      user_manager::UserManager::Get()->GetOwnerAccountId().is_valid());
 
   params.SetString("chromeType", GetChromeType());
   params.SetString("clientId",
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index ccefe00..75d4680 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -291,7 +291,7 @@
   AddScreenHandler(
       base::MakeUnique<TermsOfServiceScreenHandler>(core_handler_));
 
-  AddScreenHandler(base::MakeUnique<ArcTermsOfServiceScreenHandler>());
+  AddScreenHandler(base::MakeUnique<ArcTermsOfServiceScreenHandler>(this));
 
   AddScreenHandler(base::MakeUnique<UserImageScreenHandler>());
 
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 89c8f21..10588b0 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -132,8 +132,18 @@
 const char kBackgroundLockScreenApps[] = "LOCK_SCREEN_APPS_STATE.BACKGROUND";
 const char kForegroundLockScreenApps[] = "LOCK_SCREEN_APPS_STATE.FOREGROUND";
 const char kAvailableLockScreenApps[] = "LOCK_SCREEN_APPS_STATE.AVAILABLE";
-const char kLaunchRequestedLockScreenApps[] =
-    "LOCK_SCREEN_APPS_STATE.LAUNCH_REQUESTED";
+
+// Constants for new lock screen note request type.
+const char kNewNoteRequestTap[] = "NEW_NOTE_REQUEST.TAP";
+const char kNewNoteRequestSwipe[] = "NEW_NOTE_REQUEST.SWIPE";
+const char kNewNoteRequestKeyboard[] = "NEW_NOTE_REQUEST.KEYBOARD";
+
+// Constants for reporting action taken on lock screen UI when lock screen app
+// window was in background.
+const char kRequestShutdownFromLockScreenAppUnlockUi[] =
+    "LOCK_SCREEN_APPS_UNLOCK_ACTION.SHUTDOWN";
+const char kRequestSignoutFromLockScreenAppUnlockUi[] =
+    "LOCK_SCREEN_APPS_UNLOCK_ACTION.SIGN_OUT";
 
 ash::WallpaperController* GetWallpaperController() {
   if (!ash::Shell::HasInstance())
@@ -546,6 +556,10 @@
               &SigninScreenHandler::HandleLaunchArcKioskApp);
   AddCallback("setLockScreenAppsState",
               &SigninScreenHandler::HandleSetLockScreenAppsState);
+  AddCallback("recordLockScreenAppUnlockAction",
+              &SigninScreenHandler::HandleRecordLockScreenAppUnlockUIAction);
+  AddCallback("requestNewLockScreenNote",
+              &SigninScreenHandler::HandleRequestNewNoteAction);
 }
 
 void SigninScreenHandler::Show(const LoginScreenContext& context) {
@@ -1519,6 +1533,44 @@
                                       weak_factory_.GetWeakPtr()));
 }
 
+void SigninScreenHandler::HandleRequestNewNoteAction(
+    const std::string& request_type) {
+  lock_screen_apps::StateController* state_controller =
+      lock_screen_apps::StateController::Get();
+
+  if (request_type == kNewNoteRequestTap) {
+    state_controller->HandleNewNoteRequestFromLockScreen(
+        lock_screen_apps::StateController::NewNoteRequestType::
+            kLockScreenUiTap);
+  } else if (request_type == kNewNoteRequestSwipe) {
+    state_controller->HandleNewNoteRequestFromLockScreen(
+        lock_screen_apps::StateController::NewNoteRequestType::
+            kLockScreenUiSwipe);
+  } else if (request_type == kNewNoteRequestKeyboard) {
+    state_controller->HandleNewNoteRequestFromLockScreen(
+        lock_screen_apps::StateController::NewNoteRequestType::
+            kLockScreenUiKeyboard);
+  } else {
+    NOTREACHED() << "Unknown request type " << request_type;
+  }
+}
+
+void SigninScreenHandler::HandleRecordLockScreenAppUnlockUIAction(
+    const std::string& action) {
+  lock_screen_apps::StateController* state_controller =
+      lock_screen_apps::StateController::Get();
+
+  if (action == kRequestShutdownFromLockScreenAppUnlockUi) {
+    state_controller->RecordLockScreenAppUnlockAction(
+        lock_screen_apps::StateController::LockScreenUnlockAction::kShutdown);
+  } else if (action == kRequestSignoutFromLockScreenAppUnlockUi) {
+    state_controller->RecordLockScreenAppUnlockAction(
+        lock_screen_apps::StateController::LockScreenUnlockAction::kSignOut);
+  } else {
+    NOTREACHED() << "Unknown action " << action;
+  }
+}
+
 void SigninScreenHandler::HandleSetLockScreenAppsState(
     const std::string& state) {
   lock_screen_apps::StateController* state_controller =
@@ -1528,8 +1580,6 @@
     state_controller->MoveToBackground();
   } else if (state == kForegroundLockScreenApps) {
     state_controller->MoveToForeground();
-  } else if (state == kLaunchRequestedLockScreenApps) {
-    state_controller->RequestNewLockScreenNote();
   }
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 6c2cad9..1a1a349 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -426,6 +426,8 @@
   void HandleFirstIncorrectPasswordAttempt(const AccountId& account_id);
   void HandleMaxIncorrectPasswordAttempts(const AccountId& account_id);
   void HandleSendFeedbackAndResyncUserData();
+  void HandleRequestNewNoteAction(const std::string& request_type);
+  void HandleRecordLockScreenAppUnlockUIAction(const std::string& action);
   void HandleSetLockScreenAppsState(const std::string& state);
 
   // Sends the list of |keyboard_layouts| available for the |locale| that is
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc b/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
index 3db6e62..a327c58 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
@@ -63,8 +63,13 @@
     CrosSettings::Initialize();
 
     NetworkHandler::Initialize();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetEthernetService() {
     ShillServiceClient::TestInterface* service_test =
         DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
+    service_test->ClearServices();
     service_test->AddService("/service/eth",
                              "eth" /* guid */,
                              "eth",
@@ -73,6 +78,16 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  void SetCellularService() {
+    ShillServiceClient::TestInterface* service_test =
+        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
+    service_test->ClearServices();
+    service_test->AddService("/service/cell", "cell" /* guid */, "cell",
+                             shill::kTypeCellular, shill::kStateOnline,
+                             true /* visible */);
+    base::RunLoop().RunUntilIdle();
+  }
+
   void TearDown() override {
     NetworkHandler::Shutdown();
 
@@ -101,6 +116,7 @@
 // 4. When update engine becomes idle downloading of the stable channel is
 // initiated.
 TEST_F(VersionUpdaterCrosTest, TwoOverlappingSetChannelRequests) {
+  SetEthernetService();
   version_updater_->SetChannel("beta-channel", true);
 
   {
@@ -152,4 +168,27 @@
   EXPECT_EQ(2, fake_update_engine_client_->request_update_check_call_count());
 }
 
+// Test that when interactively checking for update, cellular connection is
+// allowed in Chrome by default, so that the request will be sent to Update
+// Engine.
+TEST_F(VersionUpdaterCrosTest, InteractiveCellularUpdateAllowed) {
+  SetCellularService();
+  EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count());
+  version_updater_->CheckForUpdate(base::Bind(&CheckNotification),
+                                   VersionUpdater::PromoteCallback());
+  EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count());
+}
+
+// Test that after update over cellular one time permission is set successfully,
+// an update check will be triggered.
+TEST_F(VersionUpdaterCrosTest, CellularUpdateOneTimePermission) {
+  SetCellularService();
+  EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count());
+  const std::string& update_version = "9999.0.0";
+  const int64_t update_size = 99999;
+  version_updater_->SetUpdateOverCellularOneTimePermission(
+      base::Bind(&CheckNotification), update_version, update_size);
+  EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc b/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
index 17af03a..416111a 100644
--- a/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.cc
@@ -49,6 +49,10 @@
                                   IDS_MEDIA_ROUTER_ROUTE_DETAILS_SEEK_TITLE);
   html_source->AddLocalizedString("volumeTitle",
                                   IDS_MEDIA_ROUTER_ROUTE_DETAILS_VOLUME_TITLE);
+  html_source->AddLocalizedString(
+      "currentTimeLabel", IDS_MEDIA_ROUTER_ROUTE_DETAILS_CURRENT_TIME_LABEL);
+  html_source->AddLocalizedString(
+      "durationLabel", IDS_MEDIA_ROUTER_ROUTE_DETAILS_DURATION_LABEL);
 }
 
 void AddIssuesStrings(content::WebUIDataSource* html_source) {
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 7e3fbe8..d0325e68 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -617,6 +617,11 @@
       "grantExtensionPrinterAccess",
       base::Bind(&PrintPreviewHandler::HandleGrantExtensionPrinterAccess,
                  base::Unretained(this)));
+}
+
+void PrintPreviewHandler::OnJavascriptAllowed() {
+  // Now that the UI is initialized, any future account changes will require
+  // a printer list refresh.
   RegisterForGaiaCookieChanges();
 }
 
@@ -624,6 +629,7 @@
   // Normally the handler and print preview will be destroyed together, but
   // this is necessary for refresh or navigation from the chrome://print page.
   weak_factory_.InvalidateWeakPtrs();
+  UnregisterForGaiaCookieChanges();
 }
 
 WebContents* PrintPreviewHandler::preview_web_contents() const {
@@ -1439,7 +1445,7 @@
 }
 
 void PrintPreviewHandler::OnPrintPreviewReady(int preview_uid, int request_id) {
-  if (request_id < 0 || preview_callbacks_.empty()) {
+  if (request_id < 0 || preview_callbacks_.empty() || !IsJavascriptAllowed()) {
     // invalid ID or extra message
     BadMessageReceived();
     return;
@@ -1451,7 +1457,7 @@
 }
 
 void PrintPreviewHandler::OnPrintPreviewFailed() {
-  if (preview_callbacks_.empty()) {
+  if (preview_callbacks_.empty() || !IsJavascriptAllowed()) {
     BadMessageReceived();
     return;
   }
@@ -1466,7 +1472,7 @@
 }
 
 void PrintPreviewHandler::OnInvalidPrinterSettings() {
-  if (preview_callbacks_.empty()) {
+  if (preview_callbacks_.empty() || !IsJavascriptAllowed()) {
     BadMessageReceived();
     return;
   }
@@ -1479,6 +1485,11 @@
 void PrintPreviewHandler::SendPrintPresetOptions(bool disable_scaling,
                                                  int copies,
                                                  int duplex) {
+  if (preview_callbacks_.empty() || !IsJavascriptAllowed()) {
+    BadMessageReceived();
+    return;
+  }
+
   FireWebUIListener("print-preset-options", base::Value(disable_scaling),
                     base::Value(copies), base::Value(duplex));
 }
@@ -1486,6 +1497,11 @@
 void PrintPreviewHandler::SendPageCountReady(int page_count,
                                              int request_id,
                                              int fit_to_page_scaling) {
+  if (preview_callbacks_.empty() || !IsJavascriptAllowed()) {
+    BadMessageReceived();
+    return;
+  }
+
   FireWebUIListener("page-count-ready", base::Value(page_count),
                     base::Value(request_id), base::Value(fit_to_page_scaling));
 }
@@ -1493,6 +1509,11 @@
 void PrintPreviewHandler::SendPageLayoutReady(
     const base::DictionaryValue& layout,
     bool has_custom_page_size_style) {
+  if (preview_callbacks_.empty() || !IsJavascriptAllowed()) {
+    BadMessageReceived();
+    return;
+  }
+
   FireWebUIListener("page-layout-ready", layout,
                     base::Value(has_custom_page_size_style));
 }
@@ -1500,12 +1521,17 @@
 void PrintPreviewHandler::SendPagePreviewReady(int page_index,
                                                int preview_uid,
                                                int preview_response_id) {
+  if (!IsJavascriptAllowed()) {
+    BadMessageReceived();
+    return;
+  }
+
   FireWebUIListener("page-preview-ready", base::Value(page_index),
                     base::Value(preview_uid), base::Value(preview_response_id));
 }
 
 void PrintPreviewHandler::OnPrintPreviewCancelled() {
-  if (preview_callbacks_.empty()) {
+  if (preview_callbacks_.empty() || !IsJavascriptAllowed()) {
     BadMessageReceived();
     return;
   }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 72654d9a..9cf1bd3e 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -54,7 +54,7 @@
 
   // WebUIMessageHandler implementation.
   void RegisterMessages() override;
-
+  void OnJavascriptAllowed() override;
   void OnJavascriptDisallowed() override;
 
   // SelectFileDialog::Listener implementation.
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index e6f86a8..f482fab 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1497,7 +1497,6 @@
     {"cupsPrintersLearnMoreLabel",
      IDS_SETTINGS_PRINTING_CUPS_PRINTERS_LEARN_MORE_LABEL},
     {"addCupsPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_PRINTER},
-    {"cupsPrinterDetails", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_DETAILS},
     {"editPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_EDIT},
     {"removePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_REMOVE},
     {"searchLabel", IDS_SETTINGS_PRINTING_CUPS_SEARCH_LABEL},
diff --git a/chrome/common/DEPS b/chrome/common/DEPS
index f50eedd..a715de2c 100644
--- a/chrome/common/DEPS
+++ b/chrome/common/DEPS
@@ -24,7 +24,7 @@
   "+components/password_manager/core/common",
   "+components/policy/core/common",
   "+components/printing/common",
-  "+components/safe_browsing/csd.pb.h",
+  "+components/safe_browsing/proto/csd.pb.h",
   "+components/safe_browsing/web_ui/constants.h",
   "+components/signin/core/common",
   "+components/translate/core/common",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index e113fad..2dd561d 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -35,6 +35,8 @@
 #endif  // defined(OS_MACOSX)
 
 #if !defined(OS_ANDROID)
+const base::Feature kAnimatedAppMenuIcon{"AnimatedAppMenuIcon",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAppBanners {
   "AppBanners",
 #if defined(OS_CHROMEOS)
@@ -382,6 +384,12 @@
 // foreground tab's user experience.
 const base::Feature kStaggeredBackgroundTabOpen{
     "StaggeredBackgroundTabOpen", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// This controls whether we are running experiment with staggered background
+// tab open. For control group, this should be disabled. This depends on
+// |kStaggeredBackgroundTabOpen| above.
+const base::Feature kStaggeredBackgroundTabOpenExperiment{
+    "StaggeredBackgroundTabOpenExperiment", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
 // Enables or disables the creation of (legacy) supervised users. Does not
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 0405db6..64a25d55 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -30,6 +30,7 @@
 #endif  // defined(OS_MACOSX)
 
 #if !defined(OS_ANDROID)
+extern const base::Feature kAnimatedAppMenuIcon;
 extern const base::Feature kAppBanners;
 #endif  // !defined(OS_ANDROID)
 
@@ -197,6 +198,7 @@
 
 #if !defined(OS_ANDROID)
 extern const base::Feature kStaggeredBackgroundTabOpen;
+extern const base::Feature kStaggeredBackgroundTabOpenExperiment;
 #endif
 
 extern const base::Feature kSupervisedUserCreation;
diff --git a/chrome/common/extensions/api/common_extension_api_unittest.cc b/chrome/common/extensions/api/common_extension_api_unittest.cc
index 94b67b94c..711e1fb 100644
--- a/chrome/common/extensions/api/common_extension_api_unittest.cc
+++ b/chrome/common/extensions/api/common_extension_api_unittest.cc
@@ -252,7 +252,8 @@
                            .Set("manifest_version", 2)
                            .Build())
           .Build();
-  Feature* test_feature = api_feature_provider.GetFeature("alias_api_source");
+  const Feature* test_feature =
+      api_feature_provider.GetFeature("alias_api_source");
   ASSERT_TRUE(test_feature);
   ASSERT_FALSE(api.IsAnyFeatureAvailableToContext(
       *test_feature, extension.get(), Feature::UNBLESSED_EXTENSION_CONTEXT,
@@ -327,7 +328,7 @@
       api.add_fake_schema(key);
     ExtensionAPI::OverrideSharedInstanceForTest scope(&api);
 
-    Feature* test_feature =
+    const Feature* test_feature =
         api_feature_provider.GetFeature(test_data[i].api_full_name);
     ASSERT_TRUE(test_feature);
     EXPECT_EQ(test_data[i].expect_is_available,
@@ -753,18 +754,19 @@
   std::unique_ptr<ExtensionAPI> api(
       ExtensionAPI::CreateWithDefaultConfiguration());
 
-  SimpleFeature* browser_action = static_cast<SimpleFeature*>(
+  const SimpleFeature* browser_action = static_cast<const SimpleFeature*>(
       api->GetFeatureDependency("api:browserAction"));
-  SimpleFeature* browser_action_set_title = static_cast<SimpleFeature*>(
-      api->GetFeatureDependency("api:browserAction.setTitle"));
+  const SimpleFeature* browser_action_set_title =
+      static_cast<const SimpleFeature*>(
+          api->GetFeatureDependency("api:browserAction.setTitle"));
 
   struct {
-    SimpleFeature* feature;
+    const SimpleFeature* feature;
     // TODO(aa): More stuff to test over time.
   } test_data[] = {{browser_action}, {browser_action_set_title}};
 
   for (size_t i = 0; i < arraysize(test_data); ++i) {
-    SimpleFeature* feature = test_data[i].feature;
+    const SimpleFeature* feature = test_data[i].feature;
     ASSERT_TRUE(feature) << i;
 
     EXPECT_TRUE(feature->whitelist().empty());
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 3b455db41..62df9387 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -144,8 +144,9 @@
 // shutdown. Used to determine the exit type the last time the profile was open.
 const char kSessionExitType[] = "profile.exit_type";
 
-// Stores the total amount of active session time for the user.
-const char kSessionTimeTotal[] = "profile.total_time";
+// Stores the total amount of observed active session time for the user.
+// Observed time is active session time.
+const char kObservedSessionTime[] = "profile.observed_time";
 
 // The last time that the site engagement service recorded an engagement event
 // for this profile for any URL. Recorded only during shutdown. Used to prevent
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index f4928d6..da46f99f 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -56,7 +56,7 @@
 extern const char kRestoreOnStartup[];
 extern const char kSessionExitedCleanly[];
 extern const char kSessionExitType[];
-extern const char kSessionTimeTotal[];
+extern const char kObservedSessionTime[];
 extern const char kSiteEngagementLastUpdateTime[];
 extern const char kSupervisedUserApprovedExtensions[];
 extern const char kSupervisedUserCustodianEmail[];
diff --git a/chrome/common/safe_browsing/archive_analyzer_results.h b/chrome/common/safe_browsing/archive_analyzer_results.h
index a9635aa..b0c6e110 100644
--- a/chrome/common/safe_browsing/archive_analyzer_results.h
+++ b/chrome/common/safe_browsing/archive_analyzer_results.h
@@ -12,7 +12,7 @@
 
 #include "base/files/file_path.h"
 #include "build/build_config.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
@@ -33,4 +33,4 @@
 
 }  // namespace safe_browsing
 
-#endif  // CHROME_COMMON_SAFE_BROWSING_ARCHIVE_ANALYZER_RESULTS_H_
\ No newline at end of file
+#endif  // CHROME_COMMON_SAFE_BROWSING_ARCHIVE_ANALYZER_RESULTS_H_
diff --git a/chrome/common/safe_browsing/binary_feature_extractor.cc b/chrome/common/safe_browsing/binary_feature_extractor.cc
index 538a81e..896768a6 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor.cc
@@ -10,7 +10,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_mac.cc b/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
index 7d64fae..9edb648 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_mac.cc
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
index ec29387..f4fa75f 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_mac_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
index 48b1847..8cc9823 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/files/file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "crypto/sha2.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_win.cc b/chrome/common/safe_browsing/binary_feature_extractor_win.cc
index ffdb9186..52a11373 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_win.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_win.cc
@@ -13,7 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "chrome/common/safe_browsing/pe_image_reader_win.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc b/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
index 63974925..e72c7382 100644
--- a/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
+++ b/chrome/common/safe_browsing/binary_feature_extractor_win_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "net/cert/x509_cert_types.h"
 #include "net/cert/x509_certificate.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/common/safe_browsing/download_protection_util.h b/chrome/common/safe_browsing/download_protection_util.h
index dedc286..d2e4579 100644
--- a/chrome/common/safe_browsing/download_protection_util.h
+++ b/chrome/common/safe_browsing/download_protection_util.h
@@ -6,7 +6,7 @@
 #define CHROME_COMMON_SAFE_BROWSING_DOWNLOAD_PROTECTION_UTIL_H_
 
 #include "base/files/file_path.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 namespace download_protection_util {
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index 0d31d080..29f625b 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -19,7 +19,7 @@
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
 #include "chrome/common/safe_browsing/download_protection_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 #include "third_party/zlib/google/zip_reader.h"
diff --git a/chrome/common/safe_browsing/zip_analyzer.h b/chrome/common/safe_browsing/zip_analyzer.h
index 5b3c009..2e2a965ad 100644
--- a/chrome/common/safe_browsing/zip_analyzer.h
+++ b/chrome/common/safe_browsing/zip_analyzer.h
@@ -9,7 +9,7 @@
 #define CHROME_COMMON_SAFE_BROWSING_ZIP_ANALYZER_H_
 
 #include "base/files/file.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/installer/zucchini/BUILD.gn b/chrome/installer/zucchini/BUILD.gn
index 805ae5f..fe66f3d 100644
--- a/chrome/installer/zucchini/BUILD.gn
+++ b/chrome/installer/zucchini/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//chrome/process_version_rc_template.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
 
 static_library("zucchini_lib") {
@@ -79,6 +80,16 @@
   }
 }
 
+fuzzer_test("zucchini_patch_fuzzer") {
+  sources = [
+    "patch_fuzzer.cc",
+  ]
+  deps = [
+    ":zucchini_lib",
+    "//base",
+  ]
+}
+
 test("zucchini_unittests") {
   sources = [
     "buffer_sink_unittest.cc",
diff --git a/chrome/installer/zucchini/patch_fuzzer.cc b/chrome/installer/zucchini/patch_fuzzer.cc
new file mode 100644
index 0000000..e4d61c1
--- /dev/null
+++ b/chrome/installer/zucchini/patch_fuzzer.cc
@@ -0,0 +1,19 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include "base/optional.h"
+#include "chrome/installer/zucchini/buffer_view.h"
+#include "chrome/installer/zucchini/patch_reader.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  logging::SetMinLogLevel(3);  // Disable console spamming.
+  zucchini::ConstBufferView patch(data, size);
+  base::Optional<zucchini::EnsemblePatchReader> patch_reader =
+      zucchini::EnsemblePatchReader::Create(patch);
+  return 0;
+}
diff --git a/chrome/profiling/BUILD.gn b/chrome/profiling/BUILD.gn
index 01b0589c..d6b1f49 100644
--- a/chrome/profiling/BUILD.gn
+++ b/chrome/profiling/BUILD.gn
@@ -40,7 +40,9 @@
     deps = [
       "//base",
       "//chrome/common",
+      "//content/public/child",
       "//mojo/edk/system",
+      "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
     ]
   }
 
diff --git a/chrome/profiling/DEPS b/chrome/profiling/DEPS
index 08bf5f3..a79c379 100644
--- a/chrome/profiling/DEPS
+++ b/chrome/profiling/DEPS
@@ -1,5 +1,6 @@
-include_rules = [

-  "+content/public/child",

-  "+mojo/edk/embedder",

-  "+services/service_manager/public/cpp",

-]

+include_rules = [
+  "+content/public/child",
+  "+mojo/edk/embedder",
+  "+services/resource_coordinator/public",
+  "+services/service_manager/public/cpp",
+]
diff --git a/chrome/profiling/json_exporter.cc b/chrome/profiling/json_exporter.cc
index 9e0bf06..e490fa6 100644
--- a/chrome/profiling/json_exporter.cc
+++ b/chrome/profiling/json_exporter.cc
@@ -7,6 +7,8 @@
 #include <map>
 
 #include "base/strings/string_number_conversions.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h"
 
 namespace profiling {
 
@@ -54,21 +56,38 @@
       << "\"args\":{\"name\":\"Browser process\"}}";
 }
 
-// Writes the dictionary keys to preceed a "heaps_v2" trace argument. This is
-// "v2" heap dump format.
-void WriteHeapsV2Header(int pid, std::ostream& out) {
+// Writes the dictionary keys to preceed a "dumps" trace argument.
+void WriteDumpsHeader(int pid, std::ostream& out) {
   out << "{ \"pid\":" << pid << ",";
   out << "\"ph\":\"v\",";
   out << "\"name\":\"periodic_interval\",";
   out << "\"args\":{";
-  out << "\"dumps\":{";
-  out << "\"level_of_detail\":\"detailed\",";
+  out << "\"dumps\":{\n";
+}
+
+void WriteDumpsFooter(std::ostream& out) {
+  out << "}}}";  // dumps, args, event
+}
+
+// Writes the dictionary keys to preceed a "heaps_v2" trace argument inside a
+// "dumps". This is "v2" heap dump format.
+void WriteHeapsV2Header(std::ostream& out) {
+  out << "\"level_of_detail\":\"detailed\",\n";
   out << "\"heaps_v2\": {\n";
 }
 
 // Closes the dictionaries from the WriteHeapsV2Header function above.
 void WriteHeapsV2Footer(std::ostream& out) {
-  out << "}}}}";  // heaps_v2, dumps, args, event
+  out << "}";  // heaps_v2
+}
+
+void WriteMemoryMaps(
+    const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
+    std::ostream& out) {
+  base::trace_event::TracedValue traced_value;
+  memory_instrumentation::TracingObserver::MemoryMapsAsValueInto(maps,
+                                                                 &traced_value);
+  out << "\"process_mmaps\":" << traced_value.ToString();
 }
 
 // Inserts or retrieves the ID for a string in the string table.
@@ -200,13 +219,20 @@
 
 }  // namespace
 
-void ExportAllocationEventSetToJSON(int pid,
-                                    const AllocationEventSet& event_set,
-                                    std::ostream& out) {
+void ExportAllocationEventSetToJSON(
+    int pid,
+    const AllocationEventSet& event_set,
+    const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
+    std::ostream& out) {
   out << "{ \"traceEvents\": [";
   WriteProcessName(pid, out);
   out << ",\n";
-  WriteHeapsV2Header(pid, out);
+  WriteDumpsHeader(pid, out);
+
+  WriteMemoryMaps(maps, out);
+  out << ",\n";
+
+  WriteHeapsV2Header(out);
 
   StringTable string_table;
 
@@ -261,6 +287,7 @@
   out << "}}\n";  // End of allocators section.
 
   WriteHeapsV2Footer(out);
+  WriteDumpsFooter(out);
   out << "]}\n";
 }
 
diff --git a/chrome/profiling/json_exporter.h b/chrome/profiling/json_exporter.h
index 6f7ac767..fc5791c 100644
--- a/chrome/profiling/json_exporter.h
+++ b/chrome/profiling/json_exporter.h
@@ -6,14 +6,18 @@
 #define CHROME_PROFILING_JSON_EXPORTER_H_
 
 #include <iosfwd>
+#include <vector>
 
 #include "chrome/profiling/allocation_event.h"
+#include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 
 namespace profiling {
 
-void ExportAllocationEventSetToJSON(int pid,
-                                    const AllocationEventSet& set,
-                                    std::ostream& out);
+void ExportAllocationEventSetToJSON(
+    int pid,
+    const AllocationEventSet& set,
+    const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
+    std::ostream& out);
 
 }  // namespace profiling
 
diff --git a/chrome/profiling/json_exporter_unittest.cc b/chrome/profiling/json_exporter_unittest.cc
index 2f469ee..ffd1367 100644
--- a/chrome/profiling/json_exporter_unittest.cc
+++ b/chrome/profiling/json_exporter_unittest.cc
@@ -6,15 +6,20 @@
 
 #include <sstream>
 
+#include "base/gtest_prod_util.h"
 #include "base/json/json_reader.h"
+#include "base/process/process.h"
 #include "base/values.h"
 #include "chrome/profiling/backtrace_storage.h"
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace profiling {
 
 namespace {
 
+using MemoryMap = std::vector<memory_instrumentation::mojom::VmRegionPtr>;
+
 // Finds the first period_interval trace event in the given JSON trace.
 // Returns null on failure.
 const base::Value* FindFirstPeriodicInterval(const base::Value& root) {
@@ -33,6 +38,37 @@
   return nullptr;
 }
 
+// Finds the first vm region in the given periodic interval. Returns null on
+// failure.
+const base::Value* FindFirstRegionWithAnyName(
+    const base::Value* periodic_interval) {
+  auto found_args =
+      periodic_interval->FindKeyOfType("args", base::Value::Type::DICTIONARY);
+  if (found_args == periodic_interval->DictEnd())
+    return nullptr;
+  auto found_dumps =
+      found_args->second.FindKeyOfType("dumps", base::Value::Type::DICTIONARY);
+  if (found_dumps == found_args->second.DictEnd())
+    return nullptr;
+  auto found_mmaps = found_dumps->second.FindKeyOfType(
+      "process_mmaps", base::Value::Type::DICTIONARY);
+  if (found_mmaps == found_dumps->second.DictEnd())
+    return nullptr;
+  auto found_regions =
+      found_mmaps->second.FindKeyOfType("vm_regions", base::Value::Type::LIST);
+  if (found_regions == found_mmaps->second.DictEnd())
+    return nullptr;
+
+  for (const base::Value& cur : found_regions->second.GetList()) {
+    auto found_name = cur.FindKeyOfType("mf", base::Value::Type::STRING);
+    if (found_name == cur.DictEnd())
+      return nullptr;
+    if (found_name->second.GetString() != "")
+      return &cur;
+  }
+  return nullptr;
+}
+
 }  // namespace
 
 TEST(ProfilingJsonExporter, Simple) {
@@ -53,7 +89,7 @@
   events.insert(AllocationEvent(Address(0x3), 16, bt1));
 
   std::ostringstream stream;
-  ExportAllocationEventSetToJSON(1234, events, stream);
+  ExportAllocationEventSetToJSON(1234, events, MemoryMap(), stream);
   std::string json = stream.str();
 
   // JSON should parse.
@@ -84,4 +120,38 @@
                counts->GetList()[1].GetInt() == 1));
 }
 
+TEST(ProfilingJsonExporterTest, MemoryMaps) {
+  AllocationEventSet events;
+  std::vector<memory_instrumentation::mojom::VmRegionPtr> memory_maps =
+      memory_instrumentation::OSMetrics::GetProcessMemoryMaps(
+          base::Process::Current().Pid());
+  ASSERT_GT(memory_maps.size(), 2u);
+
+  std::ostringstream stream;
+  ExportAllocationEventSetToJSON(1234, events, memory_maps, stream);
+  std::string json = stream.str();
+
+  // JSON should parse.
+  base::JSONReader reader(base::JSON_PARSE_RFC);
+  std::unique_ptr<base::Value> root = reader.ReadToValue(stream.str());
+  ASSERT_EQ(base::JSONReader::JSON_NO_ERROR, reader.error_code())
+      << reader.GetErrorMessage();
+  ASSERT_TRUE(root.get());
+
+  const base::Value* periodic_interval = FindFirstPeriodicInterval(*root);
+  ASSERT_TRUE(periodic_interval) << "Array contains no periodic_interval";
+  const base::Value* region = FindFirstRegionWithAnyName(periodic_interval);
+  ASSERT_TRUE(region) << "Array contains no named vm regions";
+
+  auto start_address = region->FindKeyOfType("sa", base::Value::Type::STRING);
+  ASSERT_NE(start_address, region->DictEnd());
+  EXPECT_NE(start_address->second.GetString(), "");
+  EXPECT_NE(start_address->second.GetString(), "0");
+
+  auto size = region->FindKeyOfType("sz", base::Value::Type::STRING);
+  ASSERT_NE(size, region->DictEnd());
+  EXPECT_NE(size->second.GetString(), "");
+  EXPECT_NE(size->second.GetString(), "0");
+}
+
 }  // namespace profiling
diff --git a/chrome/profiling/memlog_connection_manager.cc b/chrome/profiling/memlog_connection_manager.cc
index f1e5d70..ce3f9ba 100644
--- a/chrome/profiling/memlog_connection_manager.cc
+++ b/chrome/profiling/memlog_connection_manager.cc
@@ -91,8 +91,10 @@
                                  base::Unretained(this), pid));
 }
 
-void MemlogConnectionManager::DumpProcess(base::ProcessId pid,
-                                          base::File output_file) {
+void MemlogConnectionManager::DumpProcess(
+    base::ProcessId pid,
+    const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
+    base::File output_file) {
   base::AutoLock l(connections_lock_);
 
   // Lock all connections to prevent deallocations of atoms from
@@ -114,7 +116,8 @@
   Connection* connection = it->second.get();
 
   std::ostringstream oss;
-  ExportAllocationEventSetToJSON(pid, connection->tracker.live_allocs(), oss);
+  ExportAllocationEventSetToJSON(pid, connection->tracker.live_allocs(), maps,
+                                 oss);
   std::string reply = oss.str();
   output_file.WriteAtCurrentPos(reply.c_str(), reply.size());
 }
diff --git a/chrome/profiling/memlog_connection_manager.h b/chrome/profiling/memlog_connection_manager.h
index 4339d0f..26e4635 100644
--- a/chrome/profiling/memlog_connection_manager.h
+++ b/chrome/profiling/memlog_connection_manager.h
@@ -6,6 +6,7 @@
 #define CHROME_PROFILING_MEMLOG_CONNECTION_MANAGER_H_
 
 #include <string>
+#include <vector>
 
 #include "base/containers/flat_map.h"
 #include "base/files/file.h"
@@ -15,11 +16,14 @@
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
 #include "chrome/profiling/backtrace_storage.h"
+#include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 
 namespace base {
+
 class SequencedTaskRunner;
 class SingleThreadTaskRunner;
-}
+
+}  // namespace base
 
 namespace profiling {
 
@@ -36,8 +40,13 @@
                           BacktraceStorage* backtrace_storage);
   ~MemlogConnectionManager();
 
-  // Dumps the memory log for the given process into |output_file|.
-  void DumpProcess(base::ProcessId pid, base::File output_file);
+  // Dumps the memory log for the given process into |output_file|.  This must
+  // be provided the memory map for the given process since that is not tracked
+  // as part of the normal allocation process.
+  void DumpProcess(
+      base::ProcessId pid,
+      const std::vector<memory_instrumentation::mojom::VmRegionPtr>& maps,
+      base::File output_file);
 
   void OnNewConnection(base::ScopedPlatformFile file, base::ProcessId pid);
 
diff --git a/chrome/profiling/memlog_impl.cc b/chrome/profiling/memlog_impl.cc
index 8b61c672..cc772e8 100644
--- a/chrome/profiling/memlog_impl.cc
+++ b/chrome/profiling/memlog_impl.cc
@@ -4,9 +4,12 @@
 
 #include "chrome/profiling/memlog_impl.h"
 
+#include "base/trace_event/memory_dump_request_args.h"
 #include "chrome/profiling/memlog_receiver_pipe.h"
 #include "content/public/child/child_thread.h"
+#include "content/public/common/service_names.mojom.h"
 #include "mojo/public/cpp/system/platform_handle.h"
+#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 
 namespace profiling {
 
@@ -14,7 +17,8 @@
     : io_runner_(content::ChildThread::Get()->GetIOTaskRunner()),
       connection_manager_(
           new MemlogConnectionManager(io_runner_, &backtrace_storage_),
-          DeleteOnRunner(FROM_HERE, io_runner_.get())) {}
+          DeleteOnRunner(FROM_HERE, io_runner_.get())),
+      weak_factory_(this) {}
 
 MemlogImpl::~MemlogImpl() {}
 
@@ -42,10 +46,48 @@
     return;
   }
   base::File file(platform_file);
+
+  // Need a memory map to make sense of the dump. The dump will be triggered
+  // in the memory map global dump callback.
+  // TODO(brettw) this should be a OnceCallback to avoid base::Passed.
+  memory_instrumentation::MemoryInstrumentation::GetInstance()
+      ->GetVmRegionsForHeapProfiler(base::Bind(
+          &MemlogImpl::OnGetVmRegionsComplete, weak_factory_.GetWeakPtr(), pid,
+          base::Passed(std::move(file))));
+}
+
+void MemlogImpl::OnGetVmRegionsComplete(
+    base::ProcessId pid,
+    base::File file,
+    bool success,
+    memory_instrumentation::mojom::GlobalMemoryDumpPtr dump) {
+  if (!success) {
+    LOG(ERROR) << "Global dump failed";
+    return;
+  }
+
+  // Find the process's memory dump we want.
+  // TODO(bug 752621) we should be asking and getting the memory map of only
+  // the process we want rather than querying all processes and filtering.
+  memory_instrumentation::mojom::ProcessMemoryDump* process_dump = nullptr;
+  for (const auto& proc : dump->process_dumps) {
+    if (proc->pid == pid) {
+      process_dump = &*proc;
+      break;
+    }
+  }
+  if (!process_dump) {
+    LOG(ERROR) << "Don't have a memory dump for PID " << pid;
+    return;
+  }
+
   io_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&MemlogConnectionManager::DumpProcess,
-                                base::Unretained(connection_manager_.get()),
-                                pid, std::move(file)));
+      FROM_HERE,
+      base::BindOnce(
+          &MemlogConnectionManager::DumpProcess,
+          base::Unretained(connection_manager_.get()), pid,
+          std::move(process_dump->os_dump->memory_maps_for_heap_profiler),
+          std::move(file)));
 }
 
 }  // namespace profiling
diff --git a/chrome/profiling/memlog_impl.h b/chrome/profiling/memlog_impl.h
index 50cafa7..4a8d8919 100644
--- a/chrome/profiling/memlog_impl.h
+++ b/chrome/profiling/memlog_impl.h
@@ -5,12 +5,15 @@
 #ifndef CHROME_PROFILING_MEMLOG_IMPL_H_
 #define CHROME_PROFILING_MEMLOG_IMPL_H_
 
+#include "base/files/file.h"
 #include "base/files/platform_file.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/common/profiling/memlog.mojom.h"
 #include "chrome/profiling/backtrace_storage.h"
 #include "chrome/profiling/memlog_connection_manager.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 
 namespace profiling {
@@ -46,10 +49,19 @@
     base::SequencedTaskRunner* runner;
   };
 
+  void OnGetVmRegionsComplete(
+      base::ProcessId pid,
+      base::File file,
+      bool success,
+      memory_instrumentation::mojom::GlobalMemoryDumpPtr dump);
+
   scoped_refptr<base::SequencedTaskRunner> io_runner_;
   BacktraceStorage backtrace_storage_;
   std::unique_ptr<MemlogConnectionManager, DeleteOnRunner> connection_manager_;
 
+  // Must be last.
+  base::WeakPtrFactory<MemlogImpl> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MemlogImpl);
 };
 
diff --git a/chrome/profiling/profiling_service.h b/chrome/profiling/profiling_service.h
index 9724fbb..fabc75e 100644
--- a/chrome/profiling/profiling_service.h
+++ b/chrome/profiling/profiling_service.h
@@ -21,7 +21,7 @@
 // ServiceManager) to set manage the global state as well as the bound
 // interface.
 //
-// Currently the servcie only manages a memlog interface, but there could be
+// Currently the service only manages a memlog interface, but there could be
 // more in the future.
 //
 // The expectation is each memlog sender will bind a connection to this service
diff --git a/chrome/renderer/safe_browsing/DEPS b/chrome/renderer/safe_browsing/DEPS
index 4db2367..5f5942167 100644
--- a/chrome/renderer/safe_browsing/DEPS
+++ b/chrome/renderer/safe_browsing/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
   "+components/safe_browsing/common",
-  "+components/safe_browsing/csd.pb.h",
+  "+components/safe_browsing/proto/csd.pb.h",
   "+components/safe_browsing/renderer",
   "+components/safe_browsing/features.h",
   "+third_party/smhasher",
diff --git a/chrome/renderer/safe_browsing/phishing_classifier.cc b/chrome/renderer/safe_browsing/phishing_classifier.cc
index 9c793c16..b4c21b47 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier.cc
@@ -22,7 +22,7 @@
 #include "chrome/renderer/safe_browsing/phishing_term_feature_extractor.h"
 #include "chrome/renderer/safe_browsing/phishing_url_feature_extractor.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "crypto/sha2.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
index 148fcec..3bac8333 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_browsertest.cc
@@ -18,7 +18,7 @@
 #include "chrome/renderer/safe_browsing/murmurhash3_util.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
 #include "chrome/test/base/chrome_render_view_test.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "crypto/sha2.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
index 4149a7e..61406c8 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
@@ -16,7 +16,7 @@
 #include "chrome/renderer/safe_browsing/phishing_classifier.h"
 #include "chrome/renderer/safe_browsing/scorer.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/navigation_state.h"
 #include "content/public/renderer/render_frame.h"
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
index 03783b5a..552a4a1 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
@@ -13,7 +13,7 @@
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "chrome/test/base/chrome_unit_test_suite.h"
 #include "components/safe_browsing/common/safebrowsing_messages.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index efc312a35..eb7e033b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2071,6 +2071,7 @@
         "../browser/ui/views/bookmarks/bookmark_editor_view_browsertest.cc",
         "../browser/ui/views/extensions/extension_message_bubble_view_browsertest.cc",
         "../browser/ui/views/external_protocol_dialog_browsertest.cc",
+        "../browser/ui/views/first_run_bubble_browsertest.cc",
         "../browser/ui/views/frame/browser_non_client_frame_view_browsertest_win.cc",
         "../browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc",
         "../browser/ui/views/importer/import_lock_dialog_view_browsertest.cc",
@@ -2352,6 +2353,7 @@
         "//services/preferences/public/interfaces",
         "//services/service_manager/public/cpp",
         "//ui/login:resources",
+        "//url",
       ]
 
       if (use_dbus) {
@@ -3873,8 +3875,11 @@
   }
 
   if ((is_linux && !is_chromeos) || is_win) {
-    sources +=
-        [ "../browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc" ]
+    sources += [
+      "../browser/feature_engagement/feature_tracker_unittest.cc",
+      "../browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc",
+      "../browser/feature_engagement/session_duration_updater_unittest.cc",
+    ]
     deps += [ "//components/feature_engagement/test:test_support" ]
   }
 
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index a99fb82..ae18082 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -106,7 +106,10 @@
     # Xvfb doesn't support maximization.
     'ChromeDriverTest.testWindowMaximize',
 ]
-_OS_SPECIFIC_FILTER['mac'] = []
+_OS_SPECIFIC_FILTER['mac'] = [
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1927
+    'MobileEmulationCapabilityTest.testTapElement',
+]
 
 _DESKTOP_NEGATIVE_FILTER = [
     # Desktop doesn't support touch (without --touch-events).
diff --git a/chrome/test/data/android/webvr_instrumentation/html/test_webvr_disabled_without_flag_set.html b/chrome/test/data/android/webvr_instrumentation/html/test_webvr_disabled_without_flag_set.html
new file mode 100644
index 0000000..598cded
--- /dev/null
+++ b/chrome/test/data/android/webvr_instrumentation/html/test_webvr_disabled_without_flag_set.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<!--
+Tests that the WebVR API is not present if the flag to enable it is not set.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webvr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script>
+    <script src="../resources/webvr_e2e.js"></script>
+  </body>
+  <script>
+    var t = async_test("WebVR API is not visible");
+    if (navigator.getVRDisplays) {
+      t.step( () => {
+        assert_unreached("API is visible");
+      });
+    }
+    t.done();
+  </script>
+</html>
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 39bca75..20e8069 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -77,6 +77,7 @@
     "ntp4.js",
     "polymer_browser_test_base.js",
     "sandboxstatus_browsertest.js",
+    "settings/about_a11y_test.js",
     "settings/accessibility_audit_rules.js",
     "settings/accessibility_browsertest.js",
     "settings/accessibility_test.js",
diff --git a/chrome/test/data/webui/settings/about_a11y_test.js b/chrome/test/data/webui/settings/about_a11y_test.js
new file mode 100644
index 0000000..33b42a7
--- /dev/null
+++ b/chrome/test/data/webui/settings/about_a11y_test.js
@@ -0,0 +1,48 @@
+// 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.
+
+/**
+ * @fileoverview Define accessibility tests for the ABOUT route.
+ */
+
+/** @const {string} Path to root from chrome/test/data/webui/settings/. */
+var ROOT_PATH = '../../../../../';
+
+// SettingsAccessibilityTest fixture.
+GEN_INCLUDE([
+  ROOT_PATH + 'chrome/test/data/webui/settings/accessibility_browsertest.js',
+]);
+
+AccessibilityTest.define('SettingsAccessibilityTest', {
+  /** @override */
+  name: 'ABOUT',
+  /** @override */
+  axeOptions: {
+    'rules': {
+      // TODO(hcarmona): enable 'region' after addressing violation.
+      'region': {enabled: false},
+    }
+  },
+  /** @override */
+  setup: function() {
+    settings.router.navigateTo(settings.routes.ABOUT);
+    Polymer.dom.flush();
+  },
+  /** @override */
+  tests: {
+    'Accessible with No Changes': function() {}
+  },
+  /** @override */
+  violationFilter: {
+    // TODO(quacht): remove this exception once the color contrast issue is
+    // solved.
+    // http://crbug.com/748608
+    'color-contrast': function(nodeResult) {
+      return nodeResult.element.id === 'prompt';
+    },
+    'aria-valid-attr': function(nodeResult) {
+      return nodeResult.element.hasAttribute('aria-active-attribute');
+    },
+  }
+});
\ No newline at end of file
diff --git a/chrome/test/data/webui/settings/accessibility_test.js b/chrome/test/data/webui/settings/accessibility_test.js
index cb6865dc..cd87ea6 100644
--- a/chrome/test/data/webui/settings/accessibility_test.js
+++ b/chrome/test/data/webui/settings/accessibility_test.js
@@ -121,8 +121,10 @@
       axeOptions.runOnly.values : AccessibilityTest.ruleIds;
   rules.forEach((ruleId) => {
     // Skip rules disabled in axeOptions.
-    if (ruleId in axeOptions.rules && !axeOptions.rules[ruleId].enabled)
+    if (axeOptions.rules && ruleId in axeOptions.rules &&
+        !axeOptions.rules[ruleId].enabled) {
       return;
+    }
 
     let newTestDef = Object.assign({}, testDef);
     newTestDef.name += '_' + ruleId;
diff --git a/chrome/tools/safe_browsing/DEPS b/chrome/tools/safe_browsing/DEPS
index 322a686..5574d3b4 100644
--- a/chrome/tools/safe_browsing/DEPS
+++ b/chrome/tools/safe_browsing/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  "+components/safe_browsing/csd.pb.h",
+  "+components/safe_browsing/proto/csd.pb.h",
 ]
diff --git a/chrome/tools/safe_browsing/sb_sigutil.cc b/chrome/tools/safe_browsing/sb_sigutil.cc
index c33ffa7..81973a4 100644
--- a/chrome/tools/safe_browsing/sb_sigutil.cc
+++ b/chrome/tools/safe_browsing/sb_sigutil.cc
@@ -16,7 +16,7 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 
 // Command-line switch for the executable to extract a signature from.
 static const char kExecutable[] = "executable";
diff --git a/chrome/utility/safe_browsing/mac/DEPS b/chrome/utility/safe_browsing/mac/DEPS
index 322a686..5574d3b4 100644
--- a/chrome/utility/safe_browsing/mac/DEPS
+++ b/chrome/utility/safe_browsing/mac/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  "+components/safe_browsing/csd.pb.h",
+  "+components/safe_browsing/proto/csd.pb.h",
 ]
diff --git a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
index 6ba4e583..00d8ad6 100644
--- a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
+++ b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
@@ -16,7 +16,7 @@
 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
 #include "chrome/utility/safe_browsing/mac/dmg_iterator.h"
 #include "chrome/utility/safe_browsing/mac/read_stream.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
index 2903ad2..d911508 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -258,8 +258,11 @@
         if (DEBUG) Log.d(TAG, "onWindowFocusChanged(%b)", hasFocus);
         super.onWindowFocusChanged(hasFocus);
         if (hasFocus) {
+            // switch to fullscreen (immersive) mode
             getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
         }
     }
 
diff --git a/chromecast/media/audio/cast_audio_output_stream_unittest.cc b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
index 1a66c0293..e62e493a 100644
--- a/chromecast/media/audio/cast_audio_output_stream_unittest.cc
+++ b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
@@ -518,6 +518,7 @@
   EXPECT_TRUE(audio_device);
   EXPECT_EQ(FakeMediaPipelineBackend::kStateRunning, GetBackend()->state());
 
+  stream->Stop();
   stream->Close();
 }
 
diff --git a/chromecast/media/cma/backend/BUILD.gn b/chromecast/media/cma/backend/BUILD.gn
index 3d4b52e..f2b0134 100644
--- a/chromecast/media/cma/backend/BUILD.gn
+++ b/chromecast/media/cma/backend/BUILD.gn
@@ -8,6 +8,8 @@
 
 source_set("backend") {
   sources = [
+    "audio_decoder_wrapper.cc",
+    "audio_decoder_wrapper.h",
     "media_pipeline_backend_factory.h",
     "media_pipeline_backend_factory_impl.cc",
     "media_pipeline_backend_factory_impl.h",
diff --git a/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input.h b/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input.h
index 2d77008c5..918c4a2 100644
--- a/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input.h
+++ b/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input.h
@@ -70,8 +70,10 @@
   // will continue to play sound while this input is paused.
   void SetPaused(bool paused);
 
-  // Sets the volume multiplier for this input. If |multiplier| is outside the
-  // range [0.0, 1.0], it is clamped to that range.
+  // Sets the volume multiplier for this input. If |multiplier| is less than 0,
+  // it is clamped to 0. Volume multipliers greater than 1.0 are allowed, but
+  // the total volume for the stream (including stream type volume) is clamped
+  // to 1.0.
   void SetVolumeMultiplier(float multiplier);
 
  private:
diff --git a/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.cc b/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.cc
index 34d95f26..92aa527 100644
--- a/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.cc
+++ b/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.cc
@@ -561,19 +561,19 @@
 void StreamMixerAlsaInputImpl::SetVolumeMultiplier(float multiplier) {
   RUN_ON_MIXER_THREAD(SetVolumeMultiplier, multiplier);
   DCHECK(!IsDeleting());
-  stream_volume_multiplier_ = std::max(0.0f, std::min(multiplier, 1.0f));
+  stream_volume_multiplier_ = std::max(0.0f, multiplier);
+  float effective_volume = EffectiveVolume();
   LOG(INFO) << device_id_ << "(" << this
             << "): stream volume = " << stream_volume_multiplier_
-            << ", effective multiplier = " << EffectiveVolume();
+            << ", effective multiplier = " << effective_volume;
   slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs);
-  slew_volume_.SetVolume(EffectiveVolume());
+  slew_volume_.SetVolume(effective_volume);
 }
 
 void StreamMixerAlsaInputImpl::SetContentTypeVolume(float volume, int fade_ms) {
   DCHECK(mixer_task_runner_->BelongsToCurrentThread());
   type_volume_multiplier_ = std::max(0.0f, std::min(volume, 1.0f));
-  float effective_volume = stream_volume_multiplier_ * type_volume_multiplier_ *
-                           mute_volume_multiplier_;
+  float effective_volume = EffectiveVolume();
   LOG(INFO) << device_id_ << "(" << this
             << "): type volume = " << type_volume_multiplier_
             << ", effective multiplier = " << effective_volume;
@@ -589,8 +589,7 @@
 void StreamMixerAlsaInputImpl::SetMuted(bool muted) {
   DCHECK(mixer_task_runner_->BelongsToCurrentThread());
   mute_volume_multiplier_ = muted ? 0.0f : 1.0f;
-  float effective_volume = stream_volume_multiplier_ * type_volume_multiplier_ *
-                           mute_volume_multiplier_;
+  float effective_volume = EffectiveVolume();
   LOG(INFO) << device_id_ << "(" << this
             << "): mute volume = " << mute_volume_multiplier_
             << ", effective multiplier = " << effective_volume;
@@ -599,8 +598,9 @@
 }
 
 float StreamMixerAlsaInputImpl::EffectiveVolume() {
-  return stream_volume_multiplier_ * type_volume_multiplier_ *
-         mute_volume_multiplier_;
+  float volume = stream_volume_multiplier_ * type_volume_multiplier_ *
+                 mute_volume_multiplier_;
+  return std::max(0.0f, std::min(volume, 1.0f));
 }
 
 void StreamMixerAlsaInputImpl::VolumeScaleAccumulate(bool repeat_transition,
diff --git a/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h b/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h
index a85b858c..5daf21d 100644
--- a/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h
+++ b/chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h
@@ -107,8 +107,7 @@
   void SetPaused(bool paused);
 
   // Sets the volume multiplier for this stream. If |multiplier| < 0, sets the
-  // volume multiplier to 0. If |multiplier| > 1, sets the volume multiplier
-  // to 1.
+  // volume multiplier to 0.
   void SetVolumeMultiplier(float multiplier);
 
   // Prevents any further calls to the delegate (ie, called when the delegate
diff --git a/chromecast/media/cma/backend/audio_decoder_wrapper.cc b/chromecast/media/cma/backend/audio_decoder_wrapper.cc
new file mode 100644
index 0000000..7aea08d
--- /dev/null
+++ b/chromecast/media/cma/backend/audio_decoder_wrapper.cc
@@ -0,0 +1,65 @@
+// 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 "chromecast/media/cma/backend/audio_decoder_wrapper.h"
+
+#include "base/logging.h"
+#include "chromecast/media/cma/backend/media_pipeline_backend_manager.h"
+
+namespace chromecast {
+namespace media {
+
+AudioDecoderWrapper::AudioDecoderWrapper(
+    MediaPipelineBackendManager* backend_manager,
+    AudioDecoder* decoder,
+    AudioContentType type)
+    : backend_manager_(backend_manager),
+      decoder_(decoder),
+      content_type_(type),
+      global_volume_multiplier_(1.0f),
+      stream_volume_multiplier_(1.0f) {
+  DCHECK(backend_manager_);
+  DCHECK(decoder_);
+
+  backend_manager_->AddAudioDecoder(this);
+}
+
+AudioDecoderWrapper::~AudioDecoderWrapper() {
+  backend_manager_->RemoveAudioDecoder(this);
+}
+
+void AudioDecoderWrapper::SetGlobalVolumeMultiplier(float multiplier) {
+  global_volume_multiplier_ = multiplier;
+  decoder_->SetVolume(stream_volume_multiplier_ * global_volume_multiplier_);
+}
+
+void AudioDecoderWrapper::SetDelegate(Delegate* delegate) {
+  decoder_->SetDelegate(delegate);
+}
+
+MediaPipelineBackend::BufferStatus AudioDecoderWrapper::PushBuffer(
+    CastDecoderBuffer* buffer) {
+  return decoder_->PushBuffer(buffer);
+}
+
+bool AudioDecoderWrapper::SetConfig(const AudioConfig& config) {
+  return decoder_->SetConfig(config);
+}
+
+bool AudioDecoderWrapper::SetVolume(float multiplier) {
+  stream_volume_multiplier_ = std::max(0.0f, std::min(multiplier, 1.0f));
+  return decoder_->SetVolume(stream_volume_multiplier_ *
+                             global_volume_multiplier_);
+}
+
+AudioDecoderWrapper::RenderingDelay AudioDecoderWrapper::GetRenderingDelay() {
+  return decoder_->GetRenderingDelay();
+}
+
+void AudioDecoderWrapper::GetStatistics(Statistics* statistics) {
+  decoder_->GetStatistics(statistics);
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/audio_decoder_wrapper.h b/chromecast/media/cma/backend/audio_decoder_wrapper.h
new file mode 100644
index 0000000..f1929d8
--- /dev/null
+++ b/chromecast/media/cma/backend/audio_decoder_wrapper.h
@@ -0,0 +1,50 @@
+// 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 CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_DECODER_WRAPPER_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_DECODER_WRAPPER_H_
+
+#include "base/macros.h"
+#include "chromecast/public/media/media_pipeline_backend.h"
+
+namespace chromecast {
+namespace media {
+
+enum class AudioContentType;
+class MediaPipelineBackendManager;
+
+class AudioDecoderWrapper : public MediaPipelineBackend::AudioDecoder {
+ public:
+  AudioDecoderWrapper(MediaPipelineBackendManager* backend_manager,
+                      MediaPipelineBackend::AudioDecoder* decoder,
+                      AudioContentType type);
+  ~AudioDecoderWrapper() override;
+
+  void SetGlobalVolumeMultiplier(float multiplier);
+
+  AudioContentType content_type() const { return content_type_; }
+
+ private:
+  // MediaPipelineBackend::AudioDecoder implementation:
+  void SetDelegate(Delegate* delegate) override;
+  BufferStatus PushBuffer(CastDecoderBuffer* buffer) override;
+  bool SetConfig(const AudioConfig& config) override;
+  bool SetVolume(float multiplier) override;
+  RenderingDelay GetRenderingDelay() override;
+  void GetStatistics(Statistics* statistics) override;
+
+  MediaPipelineBackendManager* const backend_manager_;
+  MediaPipelineBackend::AudioDecoder* const decoder_;
+  const AudioContentType content_type_;
+
+  float global_volume_multiplier_;
+  float stream_volume_multiplier_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioDecoderWrapper);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_DECODER_WRAPPER_H_
diff --git a/chromecast/media/cma/backend/media_pipeline_backend_manager.cc b/chromecast/media/cma/backend/media_pipeline_backend_manager.cc
index f3807ce..4d6c6b5e 100644
--- a/chromecast/media/cma/backend/media_pipeline_backend_manager.cc
+++ b/chromecast/media/cma/backend/media_pipeline_backend_manager.cc
@@ -7,10 +7,13 @@
 #include <algorithm>
 #include <limits>
 
+#include "base/bind.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "chromecast/chromecast_features.h"
+#include "chromecast/media/cma/backend/audio_decoder_wrapper.h"
 #include "chromecast/media/cma/backend/media_pipeline_backend_wrapper.h"
+#include "chromecast/public/volume_control.h"
 
 namespace chromecast {
 namespace media {
@@ -27,13 +30,19 @@
     : media_task_runner_(std::move(media_task_runner)),
       playing_noneffects_audio_streams_count_(0),
       allow_volume_feedback_observers_(
-          new base::ObserverListThreadSafe<AllowVolumeFeedbackObserver>()) {
+          new base::ObserverListThreadSafe<AllowVolumeFeedbackObserver>()),
+      global_volume_multipliers_({{AudioContentType::kMedia, 1.0f},
+                                  {AudioContentType::kAlarm, 1.0f},
+                                  {AudioContentType::kCommunication, 1.0f}},
+                                 base::KEEP_FIRST_OF_DUPES),
+      weak_factory_(this) {
   for (int i = 0; i < NUM_DECODER_TYPES; ++i) {
     decoder_count_[i] = 0;
   }
 }
 
 MediaPipelineBackendManager::~MediaPipelineBackendManager() {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
 }
 
 std::unique_ptr<MediaPipelineBackend>
@@ -103,5 +112,38 @@
   wrapper->LogicalResume();
 }
 
+void MediaPipelineBackendManager::SetGlobalVolumeMultiplier(
+    AudioContentType type,
+    float multiplier) {
+  if (!media_task_runner_->BelongsToCurrentThread()) {
+    media_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&MediaPipelineBackendManager::SetGlobalVolumeMultiplier,
+                   weak_factory_.GetWeakPtr(), type, multiplier));
+    return;
+  }
+
+  DCHECK_GE(multiplier, 0.0f);
+  global_volume_multipliers_[type] = multiplier;
+  for (auto* a : audio_decoders_) {
+    if (a->content_type() == type) {
+      a->SetGlobalVolumeMultiplier(multiplier);
+    }
+  }
+}
+
+void MediaPipelineBackendManager::AddAudioDecoder(
+    AudioDecoderWrapper* decoder) {
+  DCHECK(decoder);
+  audio_decoders_.insert(decoder);
+  decoder->SetGlobalVolumeMultiplier(
+      global_volume_multipliers_[decoder->content_type()]);
+}
+
+void MediaPipelineBackendManager::RemoveAudioDecoder(
+    AudioDecoderWrapper* decoder) {
+  audio_decoders_.erase(decoder);
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/cma/backend/media_pipeline_backend_manager.h b/chromecast/media/cma/backend/media_pipeline_backend_manager.h
index e112b54..f8d1693b 100644
--- a/chromecast/media/cma/backend/media_pipeline_backend_manager.h
+++ b/chromecast/media/cma/backend/media_pipeline_backend_manager.h
@@ -9,8 +9,11 @@
 #include <memory>
 #include <vector>
 
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list_threadsafe.h"
 #include "base/single_thread_task_runner.h"
 #include "chromecast/public/media/media_pipeline_backend.h"
@@ -19,6 +22,9 @@
 namespace chromecast {
 namespace media {
 
+enum class AudioContentType;
+class AudioDecoderWrapper;
+
 // This class tracks all created media backends, tracking whether or not volume
 // feedback sounds should be enabled based on the currently active backends.
 // Volume feedback sounds are only enabled when there are no active audio
@@ -53,20 +59,30 @@
     return media_task_runner_.get();
   }
 
-  // Adds/removes an observer for when folume feedback sounds are allowed.
+  // Adds/removes an observer for when volume feedback sounds are allowed.
   // An observer must be removed on the same thread that added it.
   void AddAllowVolumeFeedbackObserver(AllowVolumeFeedbackObserver* observer);
   void RemoveAllowVolumeFeedbackObserver(AllowVolumeFeedbackObserver* observer);
 
-  // Logically pause/resume a backend instance, without actually pausing or
+  // Logically pauses/resumes a backend instance, without actually pausing or
   // resuming it. This is used by multiroom output to avoid playback stutter on
   // resume. |backend| must have been created via a call to this instance's
   // CreateMediaPipelineBackend().
   void LogicalPause(MediaPipelineBackend* backend);
   void LogicalResume(MediaPipelineBackend* backend);
 
+  // Sets a global multiplier for output volume for streams fo the given |type|.
+  // The multiplier may be any value >= 0; if the resulting volume for an
+  // individual stream would be > 1.0, that stream's volume is clamped to 1.0.
+  // The default multiplier is 1.0. May be called on any thread.
+  void SetGlobalVolumeMultiplier(AudioContentType type, float multiplier);
+
  private:
   friend class MediaPipelineBackendWrapper;
+  friend class AudioDecoderWrapper;
+
+  void AddAudioDecoder(AudioDecoderWrapper* decoder);
+  void RemoveAudioDecoder(AudioDecoderWrapper* decoder);
 
   // Backend wrapper instances must use these APIs when allocating and releasing
   // decoder objects, so we can enforce global limit on #concurrent decoders.
@@ -87,6 +103,11 @@
   scoped_refptr<base::ObserverListThreadSafe<AllowVolumeFeedbackObserver>>
       allow_volume_feedback_observers_;
 
+  base::flat_set<AudioDecoderWrapper*> audio_decoders_;
+  base::flat_map<AudioContentType, float> global_volume_multipliers_;
+
+  base::WeakPtrFactory<MediaPipelineBackendManager> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaPipelineBackendManager);
 };
 
diff --git a/chromecast/media/cma/backend/media_pipeline_backend_wrapper.cc b/chromecast/media/cma/backend/media_pipeline_backend_wrapper.cc
index 7ea3f9a..462ab2d 100644
--- a/chromecast/media/cma/backend/media_pipeline_backend_wrapper.cc
+++ b/chromecast/media/cma/backend/media_pipeline_backend_wrapper.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "chromecast/media/cma/backend/audio_decoder_wrapper.h"
 #include "chromecast/media/cma/backend/media_pipeline_backend_manager.h"
 #include "chromecast/public/cast_media_shlib.h"
 
@@ -20,9 +21,9 @@
     : backend_(base::WrapUnique(
           media::CastMediaShlib::CreateMediaPipelineBackend(params))),
       backend_manager_(backend_manager),
+      content_type_(params.content_type),
       sfx_backend_(params.audio_type ==
                    media::MediaPipelineDeviceParams::kAudioStreamSoundEffects),
-      have_audio_decoder_(false),
       have_video_decoder_(false),
       playing_(false) {
   DCHECK(backend_);
@@ -30,7 +31,7 @@
 }
 
 MediaPipelineBackendWrapper::~MediaPipelineBackendWrapper() {
-  if (have_audio_decoder_)
+  if (audio_decoder_)
     backend_manager_->DecrementDecoderCount(
         sfx_backend_ ? DecoderType::SFX_DECODER : DecoderType::AUDIO_DECODER);
   if (have_video_decoder_)
@@ -38,7 +39,7 @@
 
   if (playing_) {
     LOG(WARNING) << "Destroying media backend while still in 'playing' state";
-    if (have_audio_decoder_ && !sfx_backend_) {
+    if (audio_decoder_ && !sfx_backend_) {
       backend_manager_->UpdatePlayingAudioCount(-1);
     }
   }
@@ -54,14 +55,19 @@
 
 MediaPipelineBackend::AudioDecoder*
 MediaPipelineBackendWrapper::CreateAudioDecoder() {
-  DCHECK(!have_audio_decoder_);
+  DCHECK(!audio_decoder_);
 
   if (!backend_manager_->IncrementDecoderCount(
           sfx_backend_ ? DecoderType::SFX_DECODER : DecoderType::AUDIO_DECODER))
     return nullptr;
-  have_audio_decoder_ = true;
-
-  return backend_->CreateAudioDecoder();
+  MediaPipelineBackend::AudioDecoder* real_decoder =
+      backend_->CreateAudioDecoder();
+  if (!real_decoder) {
+    return nullptr;
+  }
+  audio_decoder_ = base::MakeUnique<AudioDecoderWrapper>(
+      backend_manager_, real_decoder, content_type_);
+  return audio_decoder_.get();
 }
 
 MediaPipelineBackend::VideoDecoder*
@@ -121,7 +127,7 @@
     return;
   }
   playing_ = playing;
-  if (have_audio_decoder_ && !sfx_backend_) {
+  if (audio_decoder_ && !sfx_backend_) {
     backend_manager_->UpdatePlayingAudioCount(playing_ ? 1 : -1);
   }
 }
diff --git a/chromecast/media/cma/backend/media_pipeline_backend_wrapper.h b/chromecast/media/cma/backend/media_pipeline_backend_wrapper.h
index 848c7ce..879063e 100644
--- a/chromecast/media/cma/backend/media_pipeline_backend_wrapper.h
+++ b/chromecast/media/cma/backend/media_pipeline_backend_wrapper.h
@@ -18,6 +18,8 @@
 namespace chromecast {
 namespace media {
 
+enum class AudioContentType;
+class AudioDecoderWrapper;
 class MediaPipelineBackendManager;
 
 class MediaPipelineBackendWrapper : public MediaPipelineBackend {
@@ -45,9 +47,11 @@
 
   const std::unique_ptr<MediaPipelineBackend> backend_;
   MediaPipelineBackendManager* const backend_manager_;
+  const AudioContentType content_type_;
+
+  std::unique_ptr<AudioDecoderWrapper> audio_decoder_;
 
   bool sfx_backend_;
-  bool have_audio_decoder_;
   bool have_video_decoder_;
   bool playing_;
 
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 6d58162..4e49ab5 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -130,6 +130,8 @@
     "cryptohome/system_salt_getter.h",
     "dbus/arc_obb_mounter_client.cc",
     "dbus/arc_obb_mounter_client.h",
+    "dbus/arc_oemcrypto_client.cc",
+    "dbus/arc_oemcrypto_client.h",
     "dbus/audio_node.cc",
     "dbus/audio_node.h",
     "dbus/auth_policy_client.cc",
@@ -161,6 +163,8 @@
     "dbus/easy_unlock_client.h",
     "dbus/fake_arc_obb_mounter_client.cc",
     "dbus/fake_arc_obb_mounter_client.h",
+    "dbus/fake_arc_oemcrypto_client.cc",
+    "dbus/fake_arc_oemcrypto_client.h",
     "dbus/fake_auth_policy_client.cc",
     "dbus/fake_auth_policy_client.h",
     "dbus/fake_cras_audio_client.cc",
diff --git a/chromeos/dbus/arc_oemcrypto_client.cc b/chromeos/dbus/arc_oemcrypto_client.cc
new file mode 100644
index 0000000..a54ba140
--- /dev/null
+++ b/chromeos/dbus/arc_oemcrypto_client.cc
@@ -0,0 +1,73 @@
+// 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 "chromeos/dbus/arc_oemcrypto_client.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_proxy.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+class ArcOemCryptoClientImpl : public ArcOemCryptoClient {
+ public:
+  ArcOemCryptoClientImpl() : weak_ptr_factory_(this) {}
+  ~ArcOemCryptoClientImpl() override {}
+
+  // ArcOemCryptoClient override:
+  void BootstrapMojoConnection(base::ScopedFD fd,
+                               VoidDBusMethodCallback callback) override {
+    dbus::MethodCall method_call(arc_oemcrypto::kArcOemCryptoServiceInterface,
+                                 arc_oemcrypto::kBootstrapMojoConnection);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendFileDescriptor(fd.release());
+    proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+                       base::Bind(&ArcOemCryptoClientImpl::OnVoidDBusMethod,
+                                  weak_ptr_factory_.GetWeakPtr(),
+                                  base::Passed(std::move(callback))));
+  }
+
+ protected:
+  // DBusClient override.
+  void Init(dbus::Bus* bus) override {
+    proxy_ = bus->GetObjectProxy(
+        arc_oemcrypto::kArcOemCryptoServiceName,
+        dbus::ObjectPath(arc_oemcrypto::kArcOemCryptoServicePath));
+  }
+
+ private:
+  // Runs the callback with the method call result.
+  void OnVoidDBusMethod(VoidDBusMethodCallback callback,
+                        dbus::Response* response) {
+    std::move(callback).Run(response ? DBUS_METHOD_CALL_SUCCESS
+                                     : DBUS_METHOD_CALL_FAILURE);
+  }
+
+  dbus::ObjectProxy* proxy_ = nullptr;
+
+  base::WeakPtrFactory<ArcOemCryptoClientImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcOemCryptoClientImpl);
+};
+
+}  // namespace
+
+ArcOemCryptoClient::ArcOemCryptoClient() {}
+
+ArcOemCryptoClient::~ArcOemCryptoClient() {}
+
+// static
+ArcOemCryptoClient* ArcOemCryptoClient::Create() {
+  return new ArcOemCryptoClientImpl();
+}
+
+}  // namespace chromeos
diff --git a/chromeos/dbus/arc_oemcrypto_client.h b/chromeos/dbus/arc_oemcrypto_client.h
new file mode 100644
index 0000000..5b6cb60
--- /dev/null
+++ b/chromeos/dbus/arc_oemcrypto_client.h
@@ -0,0 +1,42 @@
+// 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 CHROMEOS_DBUS_ARC_OEMCRYPTO_CLIENT_H_
+#define CHROMEOS_DBUS_ARC_OEMCRYPTO_CLIENT_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
+#include "chromeos/dbus/dbus_method_call_status.h"
+
+namespace chromeos {
+
+// ArcOemCryptoClient is used to communicate with the ArcOemCrypto service
+// which performs Widevine L1 DRM operations for ARC. The only purpose of
+// the D-Bus service is to bootstrap a Mojo IPC connection.
+// All methods should be called from the origin thread (UI thread) which
+// initializes the DBusThreadManager instance.
+class CHROMEOS_EXPORT ArcOemCryptoClient : public DBusClient {
+ public:
+  ArcOemCryptoClient();
+  ~ArcOemCryptoClient() override;
+
+  // Factory function, creates a new instance and returns ownership.
+  // For normal usage, access the singleton via DBusThreadManager::Get().
+  static ArcOemCryptoClient* Create();
+
+  // Bootstraps the Mojo IPC connection between Chrome and the service daemon.
+  // This should pass in the child end of a Mojo pipe.
+  virtual void BootstrapMojoConnection(base::ScopedFD fd,
+                                       VoidDBusMethodCallback callback) = 0;
+};
+}  // namespace chromeos
+
+#endif  // CHROMEOS_DBUS_ARC_OEMCRYPTO_CLIENT_H_
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc
index d5a5486..93353d7 100644
--- a/chromeos/dbus/dbus_clients_browser.cc
+++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "chromeos/dbus/arc_obb_mounter_client.h"
+#include "chromeos/dbus/arc_oemcrypto_client.h"
 #include "chromeos/dbus/auth_policy_client.h"
 #include "chromeos/dbus/cros_disks_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
@@ -13,6 +14,7 @@
 #include "chromeos/dbus/debug_daemon_client.h"
 #include "chromeos/dbus/easy_unlock_client.h"
 #include "chromeos/dbus/fake_arc_obb_mounter_client.h"
+#include "chromeos/dbus/fake_arc_oemcrypto_client.h"
 #include "chromeos/dbus/fake_auth_policy_client.h"
 #include "chromeos/dbus/fake_debug_daemon_client.h"
 #include "chromeos/dbus/fake_easy_unlock_client.h"
@@ -36,6 +38,11 @@
     arc_obb_mounter_client_.reset(new FakeArcObbMounterClient);
 
   if (use_real_clients)
+    arc_oemcrypto_client_.reset(ArcOemCryptoClient::Create());
+  else
+    arc_oemcrypto_client_.reset(new FakeArcOemCryptoClient);
+
+  if (use_real_clients)
     auth_policy_client_.reset(AuthPolicyClient::Create());
   else
     auth_policy_client_.reset(new FakeAuthPolicyClient);
@@ -86,6 +93,7 @@
   DCHECK(DBusThreadManager::IsInitialized());
 
   arc_obb_mounter_client_->Init(system_bus);
+  arc_oemcrypto_client_->Init(system_bus);
   auth_policy_client_->Init(system_bus);
   cros_disks_client_->Init(system_bus);
   debug_daemon_client_->Init(system_bus);
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h
index a484eed..340f65e 100644
--- a/chromeos/dbus/dbus_clients_browser.h
+++ b/chromeos/dbus/dbus_clients_browser.h
@@ -17,6 +17,7 @@
 namespace chromeos {
 
 class ArcObbMounterClient;
+class ArcOemCryptoClient;
 class AuthPolicyClient;
 class CrosDisksClient;
 class DebugDaemonClient;
@@ -43,6 +44,7 @@
   friend class DBusThreadManagerSetter;
 
   std::unique_ptr<ArcObbMounterClient> arc_obb_mounter_client_;
+  std::unique_ptr<ArcOemCryptoClient> arc_oemcrypto_client_;
   std::unique_ptr<AuthPolicyClient> auth_policy_client_;
   std::unique_ptr<CrosDisksClient> cros_disks_client_;
   std::unique_ptr<DebugDaemonClient> debug_daemon_client_;
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index 88571830..9112f42 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -13,6 +13,7 @@
 #include "base/threading/thread.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/arc_obb_mounter_client.h"
+#include "chromeos/dbus/arc_oemcrypto_client.h"
 #include "chromeos/dbus/auth_policy_client.h"
 #include "chromeos/dbus/biod/biod_client.h"
 #include "chromeos/dbus/cras_audio_client.h"
@@ -115,6 +116,11 @@
                           : nullptr;
 }
 
+ArcOemCryptoClient* DBusThreadManager::GetArcOemCryptoClient() {
+  return clients_browser_ ? clients_browser_->arc_oemcrypto_client_.get()
+                          : nullptr;
+}
+
 AuthPolicyClient* DBusThreadManager::GetAuthPolicyClient() {
   return clients_browser_ ? clients_browser_->auth_policy_client_.get()
                           : nullptr;
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index b16c9eeb..33fd70b 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -25,6 +25,7 @@
 
 // Style Note: Clients are sorted by names.
 class ArcObbMounterClient;
+class ArcOemCryptoClient;
 class AuthPolicyClient;
 class BiodClient;
 class CrasAudioClient;
@@ -121,6 +122,7 @@
   // TODO(jamescook): Replace this with calls to FooClient::Get().
   // http://crbug.com/647367
   ArcObbMounterClient* GetArcObbMounterClient();
+  ArcOemCryptoClient* GetArcOemCryptoClient();
   AuthPolicyClient* GetAuthPolicyClient();
   BiodClient* GetBiodClient();
   CrasAudioClient* GetCrasAudioClient();
diff --git a/chromeos/dbus/dbus_thread_manager_unittest.cc b/chromeos/dbus/dbus_thread_manager_unittest.cc
index ba141028..201de8e6 100644
--- a/chromeos/dbus/dbus_thread_manager_unittest.cc
+++ b/chromeos/dbus/dbus_thread_manager_unittest.cc
@@ -21,6 +21,7 @@
 
   // Clients were created.
   EXPECT_TRUE(manager->GetArcObbMounterClient());
+  EXPECT_TRUE(manager->GetArcOemCryptoClient());
   EXPECT_TRUE(manager->GetCrasAudioClient());
   EXPECT_TRUE(manager->GetCrosDisksClient());
   EXPECT_TRUE(manager->GetCryptohomeClient());
@@ -73,6 +74,7 @@
 
   // Clients for the browser were created.
   EXPECT_TRUE(manager->GetArcObbMounterClient());
+  EXPECT_TRUE(manager->GetArcOemCryptoClient());
   EXPECT_TRUE(manager->GetCrosDisksClient());
   EXPECT_TRUE(manager->GetDebugDaemonClient());
   EXPECT_TRUE(manager->GetEasyUnlockClient());
@@ -108,6 +110,7 @@
 
   // Clients for other processes were not created.
   EXPECT_FALSE(manager->GetArcObbMounterClient());
+  EXPECT_FALSE(manager->GetArcOemCryptoClient());
   EXPECT_FALSE(manager->GetCrosDisksClient());
   EXPECT_FALSE(manager->GetDebugDaemonClient());
   EXPECT_FALSE(manager->GetEasyUnlockClient());
diff --git a/chromeos/dbus/fake_arc_oemcrypto_client.cc b/chromeos/dbus/fake_arc_oemcrypto_client.cc
new file mode 100644
index 0000000..4716d75
--- /dev/null
+++ b/chromeos/dbus/fake_arc_oemcrypto_client.cc
@@ -0,0 +1,28 @@
+// 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 "chromeos/dbus/fake_arc_oemcrypto_client.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace chromeos {
+
+FakeArcOemCryptoClient::FakeArcOemCryptoClient() {}
+
+FakeArcOemCryptoClient::~FakeArcOemCryptoClient() {}
+
+void FakeArcOemCryptoClient::Init(dbus::Bus* bus) {}
+
+void FakeArcOemCryptoClient::BootstrapMojoConnection(
+    base::ScopedFD fd,
+    VoidDBusMethodCallback callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), DBUS_METHOD_CALL_FAILURE));
+}
+
+}  // namespace chromeos
diff --git a/chromeos/dbus/fake_arc_oemcrypto_client.h b/chromeos/dbus/fake_arc_oemcrypto_client.h
new file mode 100644
index 0000000..183fa94
--- /dev/null
+++ b/chromeos/dbus/fake_arc_oemcrypto_client.h
@@ -0,0 +1,31 @@
+// 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 CHROMEOS_DBUS_FAKE_ARC_OEMCRYPTO_CLIENT_H_
+#define CHROMEOS_DBUS_FAKE_ARC_OEMCRYPTO_CLIENT_H_
+
+#include "chromeos/dbus/arc_oemcrypto_client.h"
+
+namespace chromeos {
+
+// A fake implementation of ArcOemCryptoClient.
+class CHROMEOS_EXPORT FakeArcOemCryptoClient : public ArcOemCryptoClient {
+ public:
+  FakeArcOemCryptoClient();
+  ~FakeArcOemCryptoClient() override;
+
+  // DBusClient override:
+  void Init(dbus::Bus* bus) override;
+
+  // ArcOemCryptoClient override:
+  void BootstrapMojoConnection(base::ScopedFD fd,
+                               VoidDBusMethodCallback callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeArcOemCryptoClient);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_DBUS_FAKE_ARC_OEMCRYPTO_CLIENT_H_
diff --git a/chromeos/dbus/fake_update_engine_client.cc b/chromeos/dbus/fake_update_engine_client.cc
index 1ab1ab3..db0a9c3 100644
--- a/chromeos/dbus/fake_update_engine_client.cc
+++ b/chromeos/dbus/fake_update_engine_client.cc
@@ -102,7 +102,9 @@
 void FakeUpdateEngineClient::SetUpdateOverCellularOneTimePermission(
     const std::string& target_version,
     int64_t target_size,
-    const UpdateOverCellularOneTimePermissionCallback& callback) {}
+    const UpdateOverCellularOneTimePermissionCallback& callback) {
+  callback.Run(true);
+}
 
 void FakeUpdateEngineClient::set_default_status(
     const UpdateEngineClient::Status& status) {
diff --git a/components/arc/common/typemaps.gni b/components/arc/common/typemaps.gni
index 32ca073c..8902a9b 100644
--- a/components/arc/common/typemaps.gni
+++ b/components/arc/common/typemaps.gni
@@ -11,5 +11,6 @@
   "//components/arc/common/intent_helper.typemap",
   "//components/arc/common/video_common.typemap",
   "//components/arc/common/video_encode_accelerator.typemap",
+  "//components/arc/common/voice_interaction_framework.typemap",
   "//components/arc/common/volume_mounter.typemap",
 ]
diff --git a/components/arc/common/voice_interaction_framework.mojom b/components/arc/common/voice_interaction_framework.mojom
index ad26b90..192e543 100644
--- a/components/arc/common/voice_interaction_framework.mojom
+++ b/components/arc/common/voice_interaction_framework.mojom
@@ -2,14 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 8
+// Next MinVersion: 9
 
 module arc.mojom;
 
 import "screen_rect.mojom";
 
+[Extensible]
+enum VoiceInteractionState {
+  NOT_READY = 0,
+  STOPPED,
+  RUNNING
+};
+
 // Handles voice interaction queries from Android.
-// Next method ID: 5
+// Next method ID: 6
 interface VoiceInteractionFrameworkHost {
   // Returns a screenshot of currently focused window or empty array if
   // no window is focused. |data| represents the image encoded in JPEG
@@ -28,6 +35,9 @@
 
   // Notifies Chrome whether voice interaction session is running.
   [MinVersion=6]SetVoiceInteractionRunning@4(bool running);
+
+  // Notifies Chrome the state of voice interaction session.
+  [MinVersion=8]SetVoiceInteractionState@5(VoiceInteractionState state);
 };
 
 // Indicates voice interaction configuration status.
@@ -69,6 +79,6 @@
   [MinVersion=5] StartVoiceInteractionSetupWizard@6();
 
   // Queries voice interaction settings status.
-  [MinVersion=7] GetVoiceInteractionSettings@7() => 
+  [MinVersion=7] GetVoiceInteractionSettings@7() =>
     (VoiceInteractionStatus status);
-};
\ No newline at end of file
+};
diff --git a/components/arc/common/voice_interaction_framework.typemap b/components/arc/common/voice_interaction_framework.typemap
new file mode 100644
index 0000000..8254719
--- /dev/null
+++ b/components/arc/common/voice_interaction_framework.typemap
@@ -0,0 +1,13 @@
+# 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.
+
+mojom = "//components/arc/common/voice_interaction_framework.mojom"
+public_headers = [ "//ash/public/cpp/voice_interaction_state.h" ]
+public_deps = [
+  "//ash/public/cpp:ash_public_cpp",
+]
+traits_headers =
+    [ "//components/arc/voice_interaction/voice_interaction_struct_traits.h" ]
+sources = []
+type_mappings = [ "arc.mojom.VoiceInteractionState=ash::VoiceInteractionState" ]
diff --git a/components/arc/ime/arc_ime_bridge_impl.cc b/components/arc/ime/arc_ime_bridge_impl.cc
index c7f17e2..304788b 100644
--- a/components/arc/ime/arc_ime_bridge_impl.cc
+++ b/components/arc/ime/arc_ime_bridge_impl.cc
@@ -59,14 +59,14 @@
 std::vector<mojom::CompositionSegmentPtr> ConvertSegments(
     const ui::CompositionText& composition) {
   std::vector<mojom::CompositionSegmentPtr> segments;
-  for (const ui::CompositionUnderline& underline : composition.underlines) {
+  for (const ui::ImeTextSpan& ime_text_span : composition.ime_text_spans) {
     mojom::CompositionSegmentPtr segment = mojom::CompositionSegment::New();
-    segment->start_offset = underline.start_offset;
-    segment->end_offset = underline.end_offset;
+    segment->start_offset = ime_text_span.start_offset;
+    segment->end_offset = ime_text_span.end_offset;
     segment->emphasized =
-        (underline.thick ||
-         (composition.selection.start() == underline.start_offset &&
-          composition.selection.end() == underline.end_offset));
+        (ime_text_span.thick ||
+         (composition.selection.start() == ime_text_span.start_offset &&
+          composition.selection.end() == ime_text_span.end_offset));
     segments.push_back(std::move(segment));
   }
   return segments;
diff --git a/components/arc/voice_interaction/OWNERS b/components/arc/voice_interaction/OWNERS
new file mode 100644
index 0000000..bb65116
--- /dev/null
+++ b/components/arc/voice_interaction/OWNERS
@@ -0,0 +1,2 @@
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/components/arc/voice_interaction/voice_interaction_struct_traits.h b/components/arc/voice_interaction/voice_interaction_struct_traits.h
new file mode 100644
index 0000000..c519f96
--- /dev/null
+++ b/components/arc/voice_interaction/voice_interaction_struct_traits.h
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ARC_VOICE_INTERACTION_VOICE_INTERACTION_STRUCT_TRAITS_H_
+#define COMPONENTS_ARC_VOICE_INTERACTION_VOICE_INTERACTION_STRUCT_TRAITS_H_
+
+#include "ash/public/cpp/voice_interaction_state.h"
+#include "components/arc/common/voice_interaction_framework.mojom.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<arc::mojom::VoiceInteractionState,
+                  ash::VoiceInteractionState> {
+  static arc::mojom::VoiceInteractionState ToMojom(
+      ash::VoiceInteractionState state) {
+    switch (state) {
+      case ash::VoiceInteractionState::NOT_READY:
+        return arc::mojom::VoiceInteractionState::NOT_READY;
+      case ash::VoiceInteractionState::STOPPED:
+        return arc::mojom::VoiceInteractionState::STOPPED;
+      case ash::VoiceInteractionState::RUNNING:
+        return arc::mojom::VoiceInteractionState::RUNNING;
+    }
+
+    NOTREACHED() << "Invalid state: " << static_cast<int>(state);
+    return arc::mojom::VoiceInteractionState::NOT_READY;
+  }
+
+  static bool FromMojom(arc::mojom::VoiceInteractionState mojom_state,
+                        ash::VoiceInteractionState* state) {
+    switch (mojom_state) {
+      case arc::mojom::VoiceInteractionState::NOT_READY:
+        *state = ash::VoiceInteractionState::NOT_READY;
+        return true;
+      case arc::mojom::VoiceInteractionState::STOPPED:
+        *state = ash::VoiceInteractionState::STOPPED;
+        return true;
+      case arc::mojom::VoiceInteractionState::RUNNING:
+        *state = ash::VoiceInteractionState::RUNNING;
+        return true;
+    }
+
+    NOTREACHED() << "Invalid state: " << static_cast<int>(mojom_state);
+    *state = ash::VoiceInteractionState::NOT_READY;
+    return false;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // COMPONENTS_ARC_VOICE_INTERACTION_VOICE_INTERACTION_STRUCT_TRAITS_H_
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index be8c652..dbb5b23 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -17,6 +17,8 @@
     "address_rewriter.cc",
     "address_rewriter.h",
     "address_rewriter_rules.cc",
+    "address_validation_util.cc",
+    "address_validation_util.h",
     "autocomplete_history_manager.cc",
     "autocomplete_history_manager.h",
     "autofill-inl.h",
@@ -56,6 +58,8 @@
     "autofill_profile.h",
     "autofill_profile_comparator.cc",
     "autofill_profile_comparator.h",
+    "autofill_profile_validator.cc",
+    "autofill_profile_validator.h",
     "autofill_provider.cc",
     "autofill_provider.h",
     "autofill_scanner.cc",
@@ -321,6 +325,7 @@
     "address_i18n_unittest.cc",
     "address_rewriter_unittest.cc",
     "address_unittest.cc",
+    "address_validation_util_unittest.cc",
     "autocomplete_history_manager_unittest.cc",
     "autofill_country_unittest.cc",
     "autofill_data_model_unittest.cc",
@@ -335,6 +340,7 @@
     "autofill_metrics_unittest.cc",
     "autofill_profile_comparator_unittest.cc",
     "autofill_profile_unittest.cc",
+    "autofill_profile_validator_unittest.cc",
     "autofill_type_unittest.cc",
     "contact_info_unittest.cc",
     "country_combobox_model_unittest.cc",
diff --git a/components/autofill/core/browser/address_validation_util.cc b/components/autofill/core/browser/address_validation_util.cc
new file mode 100644
index 0000000..76b056dc
--- /dev/null
+++ b/components/autofill/core/browser/address_validation_util.cc
@@ -0,0 +1,155 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/address_validation_util.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/address_i18n.h"
+#include "components/autofill/core/browser/country_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_validator.h"
+
+namespace autofill {
+
+namespace {
+
+using ::i18n::addressinput::COUNTRY;
+using ::i18n::addressinput::ADMIN_AREA;
+using ::i18n::addressinput::LOCALITY;
+using ::i18n::addressinput::DEPENDENT_LOCALITY;
+using ::i18n::addressinput::SORTING_CODE;
+using ::i18n::addressinput::POSTAL_CODE;
+using ::i18n::addressinput::STREET_ADDRESS;
+using ::i18n::addressinput::RECIPIENT;
+
+using ::i18n::addressinput::AddressData;
+using ::i18n::addressinput::AddressField;
+using ::i18n::addressinput::AddressProblem;
+using ::i18n::addressinput::FieldProblemMap;
+
+using ::i18n::addressinput::INVALID_FORMAT;
+using ::i18n::addressinput::MISMATCHING_VALUE;
+using ::i18n::addressinput::MISSING_REQUIRED_FIELD;
+using ::i18n::addressinput::UNEXPECTED_FIELD;
+using ::i18n::addressinput::UNKNOWN_VALUE;
+
+const AddressField kFields[] = {COUNTRY, ADMIN_AREA, POSTAL_CODE};
+const AddressProblem kProblems[] = {UNEXPECTED_FIELD, MISSING_REQUIRED_FIELD,
+                                    UNKNOWN_VALUE, INVALID_FORMAT,
+                                    MISMATCHING_VALUE};
+
+// If the |address_field| is valid, set the validity state of the
+// |address_field| in the |profile| to the |state| and return true.
+// Otherwise, return false.
+bool SetValidityStateForAddressField(AutofillProfile* profile,
+                                     AddressField address_field,
+                                     AutofillProfile::ValidityState state) {
+  ServerFieldType server_field = i18n::TypeForField(address_field,
+                                                    /*billing=*/false);
+  if (server_field == UNKNOWN_TYPE)
+    return false;
+  DCHECK(profile);
+  profile->SetValidityState(server_field, state);
+  return true;
+}
+
+// Set the validity state of all address fields in the |profile| to |state|.
+void SetAllValidityStates(AutofillProfile* profile,
+                          AutofillProfile::ValidityState state) {
+  DCHECK(profile);
+  for (auto field : kFields)
+    SetValidityStateForAddressField(profile, field, state);
+}
+
+// Returns all relevant pairs of (field, problem), where field is in
+// |kFields|, and problem is in |kProblems|.
+FieldProblemMap* CreateFieldProblemMap() {
+  FieldProblemMap* filter = new FieldProblemMap();
+  for (auto field : kFields) {
+    for (auto problem : kProblems) {
+      filter->insert(std::make_pair(field, problem));
+    }
+  }
+  return filter;
+}
+
+// GetFilter() will make sure that the validation only returns problems that
+// are relevant.
+const FieldProblemMap* GetFilter() {
+  static const FieldProblemMap* const filter = CreateFieldProblemMap();
+  return filter;
+}
+
+// Initializes |address| data from the address info in the |profile|.
+void InitializeAddressFromProfile(const AutofillProfile& profile,
+                                  AddressData* address) {
+  address->region_code =
+      base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY));
+  address->administrative_area =
+      base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE));
+  address->postal_code =
+      base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_ZIP));
+}
+
+}  // namespace
+
+namespace address_validation_util {
+
+AutofillProfile::ValidityState ValidateAddress(
+    AutofillProfile* profile,
+    AddressValidator* address_validator) {
+  DCHECK(address_validator);
+  if (!profile)
+    return AutofillProfile::UNVALIDATED;
+
+  if (!base::ContainsValue(
+          CountryDataMap::GetInstance()->country_codes(),
+          base::UTF16ToUTF8(profile->GetRawInfo(ADDRESS_HOME_COUNTRY)))) {
+    // If the country code is not in the database, the country code and the
+    // profile are invalid, and other fields cannot be validated, because it is
+    // unclear which, if any, rule should apply.
+    SetAllValidityStates(profile, AutofillProfile::UNVALIDATED);
+    SetValidityStateForAddressField(profile, COUNTRY, AutofillProfile::INVALID);
+    return AutofillProfile::INVALID;
+  }
+
+  AddressData address;
+  InitializeAddressFromProfile(*profile, &address);
+
+  AutofillProfile::ValidityState profile_validity;
+  FieldProblemMap problems;
+  // status denotes if the rule was successfully loaded before validation.
+  AddressValidator::Status status =
+      address_validator->ValidateAddress(address, GetFilter(), &problems);
+
+  if (status == AddressValidator::SUCCESS) {
+    // The rules were found and applied. Initialize all fields to VALID here and
+    // update the fields with problems below.
+    profile_validity = AutofillProfile::VALID;
+    SetAllValidityStates(profile, AutofillProfile::VALID);
+  } else {
+    // If the rules are not yet available, ValidateAddress can still check for
+    // MISSING_REQUIRED_FIELD. In this case, the address fields will be either
+    // UNVALIDATED or INVALID.
+    profile_validity = AutofillProfile::UNVALIDATED;
+    SetAllValidityStates(profile, AutofillProfile::UNVALIDATED);
+    SetValidityStateForAddressField(profile, COUNTRY, AutofillProfile::VALID);
+  }
+
+  for (auto problem : problems) {
+    if (SetValidityStateForAddressField(profile, problem.first,
+                                        AutofillProfile::INVALID)) {
+      profile_validity = AutofillProfile::INVALID;
+    }
+  }
+  return profile_validity;
+}
+
+}  // namespace address_validation_util
+}  // namespace autofill
diff --git a/components/autofill/core/browser/address_validation_util.h b/components/autofill/core/browser/address_validation_util.h
new file mode 100644
index 0000000..c58c6959
--- /dev/null
+++ b/components/autofill/core/browser/address_validation_util.h
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_VALIDATION_UTIL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_VALIDATION_UTIL_H_
+
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
+
+namespace autofill {
+namespace address_validation_util {
+
+// Validates the address fields of the |profile|.
+// Returns the ValidityState of the |profile| according to its address fields.
+AutofillProfile::ValidityState ValidateAddress(
+    AutofillProfile* profile,
+    AddressValidator* address_validator);
+
+}  // namespace address_validation_util
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_VALIDATION_UTIL_H_
diff --git a/components/autofill/core/browser/address_validation_util_unittest.cc b/components/autofill/core/browser/address_validation_util_unittest.cc
new file mode 100644
index 0000000..c45f682
--- /dev/null
+++ b/components/autofill/core/browser/address_validation_util_unittest.cc
@@ -0,0 +1,235 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/address_validation_util.h"
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/null_storage.h"
+#include "third_party/libaddressinput/src/cpp/test/testdata_source.h"
+
+namespace autofill {
+
+using ::i18n::addressinput::Source;
+using ::i18n::addressinput::Storage;
+using ::i18n::addressinput::NullStorage;
+using ::i18n::addressinput::TestdataSource;
+
+// Used to load region rules for this test.
+class ValidationTestDataSource : public TestdataSource {
+ public:
+  ValidationTestDataSource() : TestdataSource(true) {}
+
+  ~ValidationTestDataSource() override {}
+
+  void Get(const std::string& key, const Callback& data_ready) const override {
+    data_ready(
+        true, key,
+        new std::string(
+            "{"
+            "\"data/CA\": "
+            "{\"lang\": \"en\", \"upper\": \"ACNOSZ\", "
+            "\"zipex\": \"H3Z 2Y7,V8X 3X4,T0L 1K0,T0H 1A0\", "
+            "\"name\": \"CANADA\", "
+            "\"fmt\": \"%N%n%O%n%A%n%C %S %Z\", \"id\": \"data/CA\", "
+            "\"languages\": \"en~fr\", \"sub_keys\": \"NB~QC\", \"key\": "
+            "\"CA\", "
+            "\"require\": \"ACSZ\", \"sub_names\": \"New Brunswick~Quebec\", "
+            "\"sub_zips\": \"E~G|H|J\"}, "
+            "\"data/CA--fr\": "
+            "{\"lang\": \"fr\", \"upper\": \"ACNOSZ\", "
+            "\"zipex\": \"H3Z 2Y7,V8X 3X4,T0L 1K0,T0H 1A0\", "
+            "\"name\": \"CANADA\", "
+            "\"fmt\": \"%N%n%O%n%A%n%C %S %Z\", \"require\": \"ACSZ\", "
+            "\"sub_keys\": \"NB~QC\", \"key\": \"CA\", "
+            "\"id\": \"data/CA--fr\", "
+            "\"sub_names\":\"Nouveau-Brunswick~Québec\","
+            "\"sub_zips\": \"E~G|H|J\"}, "
+            "\"data/CA/QC\": "
+            "{\"lang\": \"en\", \"key\": \"QC\", "
+            "\"id\": \"data/CA/QC\", \"zip\": \"G|H|J\", \"name\": \"Quebec\"},"
+            "\"data/CA/QC--fr\": "
+            "{\"lang\": \"fr\", \"key\": \"QC\", \"id\": \"data/CA/QC--fr\", "
+            "\"zip\": \"G|H|J\", \"name\": \"Québec\"}, "
+            "\"data/CA/NB\": "
+            "{\"lang\": \"en\", \"key\": \"NB\", \"id\": \"data/CA/NB\", "
+            "\"zip\": \"E\", \"name\": \"New Brunswick\"}, "
+            "\"data/CA/NB--fr\": "
+            "{\"lang\": \"fr\", \"key\": \"NB\", \"id\": \"data/CA/NB--fr\", "
+            "\"zip\": \"E\", \"name\": \"Nouveau-Brunswick\"}"
+            "}"));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ValidationTestDataSource);
+};
+
+class AutofillAddressValidationTest : public testing::Test, LoadRulesListener {
+ public:
+  AutofillAddressValidationTest()
+      : validator_(std::unique_ptr<Source>(new ValidationTestDataSource()),
+                   std::unique_ptr<Storage>(new NullStorage),
+                   this) {
+    validator_.LoadRules("CA");
+  }
+
+  AutofillProfile::ValidityState ValidateAddressTest(AutofillProfile* profile) {
+    return address_validation_util::ValidateAddress(profile, &validator_);
+  }
+
+  ~AutofillAddressValidationTest() override {}
+
+ private:
+  AddressValidator validator_;
+
+  // LoadRulesListener implementation.
+  void OnAddressValidationRulesLoaded(const std::string& country_code,
+                                      bool success) override {}
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillAddressValidationTest);
+};
+
+TEST_F(AutofillAddressValidationTest, ValidateNULLProfile) {
+  EXPECT_EQ(AutofillProfile::UNVALIDATED, ValidateAddressTest(nullptr));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateFullValidProfile) {
+  // This is a valid profile according to the rules in ValidationTestDataSource:
+  // Address Line 1: "666 Notre-Dame Ouest",
+  // Address Line 2: "Apt 8", City: "Montreal", Province: "QC",
+  // Postal Code: "H3B 2T9", Country Code: "CA",
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  EXPECT_EQ(AutofillProfile::VALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::VALID, profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateFullProfile_CountryCodeNotExist) {
+  // This is a profile with invalid country code, therefore it cannot be
+  // validated according to ValidationTestDataSource.
+  const std::string country_code = "PP";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, base::UTF8ToUTF16(country_code));
+  EXPECT_EQ(AutofillProfile::INVALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::INVALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::UNVALIDATED,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::UNVALIDATED,
+            profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateFullProfile_RuleNotAvailable) {
+  // This is a profile with valid country code, but the rule is not available in
+  // the ValidationTestDataSource.
+  const std::string country_code = "US";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, base::UTF8ToUTF16(country_code));
+  EXPECT_EQ(AutofillProfile::UNVALIDATED, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::UNVALIDATED,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::UNVALIDATED,
+            profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateAddress_AdminAreaNotExists) {
+  const std::string admin_area_code = "QQ";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16(admin_area_code));
+
+  EXPECT_EQ(AutofillProfile::INVALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::INVALID,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::VALID, profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateAddress_AdminAreaFullName) {
+  const std::string admin_area = "Quebec";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16(admin_area));
+
+  EXPECT_EQ(AutofillProfile::VALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::VALID, profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateAddress_AdminAreaSmallCode) {
+  const std::string admin_area = "qc";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16(admin_area));
+
+  EXPECT_EQ(AutofillProfile::VALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::VALID, profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateAddress_AdminAreaSpecialLetter) {
+  const std::string admin_area = "Québec";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16(admin_area));
+
+  EXPECT_EQ(AutofillProfile::VALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::VALID, profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateAddress_ValidZipNoSpace) {
+  // TODO(crbug/752614): postal codes in lower case letters should also be
+  // considered as valid. Now, they are considered as INVALID.
+  const std::string postal_code = "H3C6S3";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16(postal_code));
+
+  EXPECT_EQ(AutofillProfile::VALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::VALID, profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+TEST_F(AutofillAddressValidationTest, ValidateAddress_InvalidZip) {
+  const std::string postal_code = "ABC 123";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16(postal_code));
+
+  EXPECT_EQ(AutofillProfile::INVALID, ValidateAddressTest(&profile));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(AutofillProfile::VALID,
+            profile.GetValidityState(ADDRESS_HOME_STATE));
+  EXPECT_EQ(AutofillProfile::INVALID,
+            profile.GetValidityState(ADDRESS_HOME_ZIP));
+}
+
+// TODO(crbug/754727): add tests for a non-default language.
+// Ex: Nouveau-Brunswick for Canada.
+
+// TODO(crbug/754729): Add tests for a country whose default language is a
+// non-Western one, such as China.
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_profile_validator.cc b/components/autofill/core/browser/autofill_profile_validator.cc
new file mode 100644
index 0000000..5fd3b327
--- /dev/null
+++ b/components/autofill/core/browser/autofill_profile_validator.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/autofill_profile_validator.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/cancelable_callback.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/address_validation_util.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_validator.h"
+
+namespace autofill {
+namespace {
+
+using ::i18n::addressinput::COUNTRY;
+using ::i18n::addressinput::ADMIN_AREA;
+using ::i18n::addressinput::LOCALITY;
+using ::i18n::addressinput::DEPENDENT_LOCALITY;
+using ::i18n::addressinput::SORTING_CODE;
+using ::i18n::addressinput::POSTAL_CODE;
+using ::i18n::addressinput::STREET_ADDRESS;
+using ::i18n::addressinput::RECIPIENT;
+
+const int kRulesLoadingTimeoutSeconds = 5;
+
+}  // namespace
+
+AutofillProfileValidator::ValidationRequest::ValidationRequest(
+    AutofillProfile* profile,
+    autofill::AddressValidator* validator,
+    AutofillProfileValidatorCallback on_validated)
+    : profile_(profile),
+      validator_(validator),
+      on_validated_(std::move(on_validated)),
+      has_responded_(false),
+      on_timeout_(base::Bind(&ValidationRequest::OnRulesLoaded,
+                             base::Unretained(this))) {
+  DCHECK(profile_);
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, on_timeout_.callback(),
+      base::TimeDelta::FromSeconds(kRulesLoadingTimeoutSeconds));
+}
+
+AutofillProfileValidator::ValidationRequest::~ValidationRequest() {
+  on_timeout_.Cancel();
+}
+
+void AutofillProfileValidator::ValidationRequest::OnRulesLoaded() {
+  on_timeout_.Cancel();
+  // Check if the timeout happened before the rules were loaded.
+  if (has_responded_)
+    return;
+  has_responded_ = true;
+  AutofillProfile::ValidityState profile_validity =
+      address_validation_util::ValidateAddress(profile_, validator_);
+  std::move(on_validated_).Run(profile_validity);
+}
+
+AutofillProfileValidator::AutofillProfileValidator(
+    std::unique_ptr<Source> source,
+    std::unique_ptr<Storage> storage)
+    : address_validator_(std::move(source), std::move(storage), this) {}
+
+AutofillProfileValidator::~AutofillProfileValidator() {}
+
+void AutofillProfileValidator::ValidateProfile(
+    AutofillProfile* profile,
+    AutofillProfileValidatorCallback cb) {
+  if (!profile) {
+    // An null profile is an unvalidated profile.
+    std::move(cb).Run(AutofillProfile::UNVALIDATED);
+    return;
+  }
+  std::unique_ptr<ValidationRequest> request(
+      base::MakeUnique<ValidationRequest>(profile, &address_validator_,
+                                          std::move(cb)));
+
+  // If the |region_code| is not a valid code according to our source, calling
+  // LoadRules would result in calling OnAddressValidationRulesLoaded with
+  // success = false. Thus, we can handle illegitimate |region_code|'s as well.
+  std::string region_code =
+      base::UTF16ToUTF8(profile->GetRawInfo(ADDRESS_HOME_COUNTRY));
+  if (address_validator_.AreRulesLoadedForRegion(region_code)) {
+    request->OnRulesLoaded();
+  } else {
+    // Setup the variables to start validation when the rules are loaded.
+    pending_requests_[region_code].push_back(std::move(request));
+
+    // Start loading the rules for the region. If the rules were already in the
+    // process of being loaded, this call will do nothing.
+    address_validator_.LoadRules(region_code);
+  }
+}
+
+void AutofillProfileValidator::OnAddressValidationRulesLoaded(
+    const std::string& region_code,
+    bool success) {
+  // Even if success = false, we can still validate address partially. We can
+  // check for missing fields or unexpected fields.
+
+  // Check if there is any request for that region code.
+  auto it = pending_requests_.find(region_code);
+  if (it != pending_requests_.end()) {
+    for (auto& request : it->second) {
+      request->OnRulesLoaded();
+    }
+    pending_requests_.erase(it);
+  }
+}
+
+bool AutofillProfileValidator::AreRulesLoadedForRegion(
+    const std::string& region_code) {
+  return address_validator_.AreRulesLoadedForRegion(region_code);
+}
+
+void AutofillProfileValidator::LoadRulesForRegion(
+    const std::string& region_code) {
+  address_validator_.LoadRules(region_code);
+}
+}  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_profile_validator.h b/components/autofill/core/browser/autofill_profile_validator.h
new file mode 100644
index 0000000..e1d4bab
--- /dev/null
+++ b/components/autofill/core/browser/autofill_profile_validator.h
@@ -0,0 +1,108 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROFILE_VALIDATOR_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROFILE_VALIDATOR_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "components/autofill/core/browser/address_i18n.h"
+#include "components/autofill/core/browser/address_validation_util.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/preload_supplier.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
+
+namespace autofill {
+
+using ::i18n::addressinput::BuildCallback;
+using ::i18n::addressinput::PreloadSupplier;
+using ::i18n::addressinput::Source;
+using ::i18n::addressinput::Storage;
+
+using AutofillProfileValidatorCallback =
+    base::OnceCallback<void(AutofillProfile::ValidityState)>;
+
+// AutofillProfileValidator Loads Rules from the server and validates an
+// autofill profile. For a given autofill profile, it will set the ValidityState
+// of several fields in the profile such as country, administrative area, etc.
+// See implementation for more details.
+class AutofillProfileValidator : public autofill::LoadRulesListener {
+ public:
+  // Takes ownership of |source| and |storage|.
+  AutofillProfileValidator(
+      std::unique_ptr<::i18n::addressinput::Source> source,
+      std::unique_ptr<::i18n::addressinput::Storage> storage);
+
+  ~AutofillProfileValidator() override;
+
+  // If the rule corresponding to the |profile| is loaded, this validates the
+  // profile, synchronously. If it is not loaded yet, it sets up a
+  // task to validate the profile when the rule is loaded (asynchronous). If the
+  // loading has not yet started, it will also start loading the rules.
+  void ValidateProfile(AutofillProfile* profile,
+                       AutofillProfileValidatorCallback cb);
+
+ private:
+  // ValidationRequest loads Rules from the server and validates various fields
+  // in an autofill profile.
+  class ValidationRequest {
+   public:
+    ValidationRequest(AutofillProfile* profile,
+                      autofill::AddressValidator* validator,
+                      AutofillProfileValidatorCallback on_validated);
+
+    ~ValidationRequest();
+
+    // Validates various fields of the |profile_|, and calls |on_validated_|.
+    void OnRulesLoaded();
+
+   private:
+    // Not owned. Not Null. Outlives this object.
+    AutofillProfile* profile_;
+    // Not owned. Outlives this object.
+    autofill::AddressValidator* validator_;
+
+    AutofillProfileValidatorCallback on_validated_;
+
+    bool has_responded_ = false;
+    base::CancelableCallback<void()> on_timeout_;
+
+    DISALLOW_COPY_AND_ASSIGN(ValidationRequest);
+  };
+
+  friend class AutofillProfileValidatorTest;
+
+  // Returns whether the rules for the specified |region_code| is loaded.
+  bool AreRulesLoadedForRegion(const std::string& region_code);
+
+  // Starts loading the rules for the specified |region_code|.
+  void LoadRulesForRegion(const std::string& region_code);
+
+  // Implementation of the LoadRulesListener interface. Called when the address
+  // rules for the |region_code| have finished loading.
+  void OnAddressValidationRulesLoaded(const std::string& region_code,
+                                      bool success) override;
+
+  // A map of the region code and the pending requests for that region code.
+  std::map<std::string, std::vector<std::unique_ptr<ValidationRequest>>>
+      pending_requests_;
+
+  // The address validator used to load rules.
+  autofill::AddressValidator address_validator_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillProfileValidator);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROFILE_VALIDATOR_H_
diff --git a/components/autofill/core/browser/autofill_profile_validator_unittest.cc b/components/autofill/core/browser/autofill_profile_validator_unittest.cc
new file mode 100644
index 0000000..e17c0196
--- /dev/null
+++ b/components/autofill/core/browser/autofill_profile_validator_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/autofill_profile_validator.h"
+
+#include <stddef.h>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/null_storage.h"
+#include "third_party/libaddressinput/src/cpp/test/testdata_source.h"
+
+namespace autofill {
+
+using ::i18n::addressinput::NullStorage;
+using ::i18n::addressinput::TestdataSource;
+
+using ::i18n::addressinput::COUNTRY;
+using ::i18n::addressinput::ADMIN_AREA;
+using ::i18n::addressinput::LOCALITY;
+using ::i18n::addressinput::DEPENDENT_LOCALITY;
+using ::i18n::addressinput::SORTING_CODE;
+using ::i18n::addressinput::POSTAL_CODE;
+using ::i18n::addressinput::STREET_ADDRESS;
+using ::i18n::addressinput::RECIPIENT;
+
+// Used to load region rules for this test.
+class ValidationTestDataSource : public TestdataSource {
+ public:
+  ValidationTestDataSource() : TestdataSource(true) {}
+
+  ~ValidationTestDataSource() override {}
+
+  void Get(const std::string& key, const Callback& data_ready) const override {
+    data_ready(
+        true, key,
+        new std::string(
+            "{"
+            "\"data/CA\": "
+            "{\"lang\": \"en\", \"upper\": \"ACNOSZ\", "
+            "\"zipex\": \"H3Z 2Y7,V8X 3X4,T0L 1K0,T0H 1A0\", "
+            "\"name\": \"CANADA\", "
+            "\"fmt\": \"%N%n%O%n%A%n%C %S %Z\", \"id\": \"data/CA\", "
+            "\"languages\": \"en\", \"sub_keys\": \"QC\", \"key\": "
+            "\"CA\", "
+            "\"require\": \"ACSZ\", \"sub_names\": \"Quebec\", "
+            "\"sub_zips\": \"G|H|J\"}, "
+            "\"data/CA/QC\": "
+            "{\"lang\": \"en\", \"key\": \"QC\", "
+            "\"id\": \"data/CA/QC\", \"zip\": \"G|H|J\", \"name\": \"Quebec\"}"
+            "}"));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ValidationTestDataSource);
+};
+
+class AutofillProfileValidatorTest : public testing::Test {
+ public:
+  AutofillProfileValidatorTest()
+      : validator_(new AutofillProfileValidator(
+            std::unique_ptr<Source>(new ValidationTestDataSource()),
+            std::unique_ptr<Storage>(new NullStorage))),
+        onvalidated_cb_(
+            base::BindOnce(&AutofillProfileValidatorTest::OnValidated,
+                           base::Unretained(this))) {}
+
+ protected:
+  const std::unique_ptr<AutofillProfileValidator> validator_;
+
+  ~AutofillProfileValidatorTest() override {}
+
+  void OnValidated(AutofillProfile::ValidityState profile_valid) {
+    EXPECT_EQ(expected_validity_state_, profile_valid);
+  }
+
+  void set_expected_status(AutofillProfile::ValidityState profile_valid) {
+    expected_validity_state_ = profile_valid;
+  }
+
+  bool AreRulesLoadedForRegion(std::string region_code) {
+    return validator_->AreRulesLoadedForRegion(region_code);
+  }
+
+  void LoadRulesForRegion(std::string region_code) {
+    validator_->LoadRulesForRegion(region_code);
+  }
+
+  AutofillProfileValidatorCallback onvalidated_cb_;
+
+ private:
+  AutofillProfile::ValidityState expected_validity_state_;
+
+  base::test::ScopedTaskEnvironment scoped_task_scheduler;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillProfileValidatorTest);
+};
+
+// Validate a Null profile.
+TEST_F(AutofillProfileValidatorTest, ValidateNullProfile) {
+  set_expected_status(AutofillProfile::UNVALIDATED);
+  validator_->ValidateProfile(nullptr, std::move(onvalidated_cb_));
+}
+// Validate a valid profile, for which the rules are not loaded, yet.
+TEST_F(AutofillProfileValidatorTest, ValidateFullValidProfile_RulesNotLoaded) {
+  // This is a valid profile, and the rules are loaded in the constructors
+  // Province: "QC", Country: "CA"
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  set_expected_status(AutofillProfile::VALID);
+
+  std::string country_code =
+      base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY));
+  EXPECT_EQ(false, AreRulesLoadedForRegion(country_code));
+
+  validator_->ValidateProfile(&profile, std::move(onvalidated_cb_));
+}
+
+// Validate a Full Profile, for which the rules are already loaded.
+TEST_F(AutofillProfileValidatorTest, ValidateAddress_RulesLoaded) {
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  set_expected_status(AutofillProfile::VALID);
+
+  std::string country_code =
+      base::UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY));
+  LoadRulesForRegion(country_code);
+  EXPECT_EQ(true, AreRulesLoadedForRegion(country_code));
+
+  validator_->ValidateProfile(&profile, std::move(onvalidated_cb_));
+}
+
+// When country code is invalid, the profile is invalid.
+TEST_F(AutofillProfileValidatorTest, ValidateAddress_CountryCodeNotExists) {
+  const std::string country_code = "PP";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, base::UTF8ToUTF16(country_code));
+  set_expected_status(AutofillProfile::INVALID);
+
+  EXPECT_EQ(false, AreRulesLoadedForRegion(country_code));
+
+  validator_->ValidateProfile(&profile, std::move(onvalidated_cb_));
+}
+
+// When country code is valid, but the rule is not in the source, the profile
+// is unvalidated.
+TEST_F(AutofillProfileValidatorTest, ValidateAddress_RuleNotExists) {
+  const std::string country_code = "US";
+  AutofillProfile profile(autofill::test::GetFullValidProfile());
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, base::UTF8ToUTF16(country_code));
+  set_expected_status(AutofillProfile::UNVALIDATED);
+
+  EXPECT_EQ(false, AreRulesLoadedForRegion(country_code));
+
+  validator_->ValidateProfile(&profile, std::move(onvalidated_cb_));
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index f6a122ef..aa3b5952 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -183,6 +183,14 @@
     profile->SetRawInfo(type, base::UTF8ToUTF16(value));
 }
 
+AutofillProfile GetFullValidProfile() {
+  AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/");
+  SetProfileInfo(&profile, "Alice", "", "Wonderland", "alice@wonderland.ca",
+                 "Fiction", "666 Notre-Dame Ouest", "Apt 8", "Montreal", "QC",
+                 "H3B 2T9", "CA", "15141112233");
+  return profile;
+}
+
 AutofillProfile GetFullProfile() {
   AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/");
   SetProfileInfo(&profile,
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index 65817d8..708f3a64 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -65,6 +65,9 @@
 void CreateTestAddressFormData(FormData* form,
                                std::vector<ServerFieldTypeSet>* types);
 
+// Returns a full profile with valid info.
+AutofillProfile GetFullValidProfile();
+
 // Returns a profile full of dummy info.
 AutofillProfile GetFullProfile();
 
diff --git a/components/crash/content/browser/child_process_crash_observer_android.cc b/components/crash/content/browser/child_process_crash_observer_android.cc
index b9ae22bb..b59a0dd 100644
--- a/components/crash/content/browser/child_process_crash_observer_android.cc
+++ b/components/crash/content/browser/child_process_crash_observer_android.cc
@@ -45,8 +45,8 @@
       FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
       base::Bind(&CrashDumpManager::ProcessMinidumpFileFromChild,
                  base::Unretained(CrashDumpManager::GetInstance()),
-                 crash_dump_dir_, pid, process_type, termination_status,
-                 app_state));
+                 crash_dump_dir_, child_process_id, process_type,
+                 termination_status, app_state));
 }
 
 }  // namespace breakpad
diff --git a/components/feature_engagement/public/event_constants.cc b/components/feature_engagement/public/event_constants.cc
index 72ea6bd..b2fb65c 100644
--- a/components/feature_engagement/public/event_constants.cc
+++ b/components/feature_engagement/public/event_constants.cc
@@ -10,11 +10,11 @@
 
 #if defined(OS_WIN) || defined(OS_LINUX)
 const char kOmniboxInteraction[] = "omnibox_used";
+const char kNewTabSessionTimeMet[] = "new_tab_session_time_met";
 
 const char kHistoryDeleted[] = "history_deleted";
 const char kIncognitoWindowOpened[] = "incognito_window_opened";
 
-const char kSessionTime[] = "session_time";
 #endif  // defined(OS_WIN) || defined(OS_LINUX)
 
 #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
diff --git a/components/feature_engagement/public/event_constants.h b/components/feature_engagement/public/event_constants.h
index 0b93423..e6d514b2 100644
--- a/components/feature_engagement/public/event_constants.h
+++ b/components/feature_engagement/public/event_constants.h
@@ -17,6 +17,9 @@
 
 // The user has interacted with the omnibox.
 extern const char kOmniboxInteraction[];
+// The user has satisfied the session time requirement to show the NewTabPromo
+// by accumulating 2 hours of active session time (one-off event).
+extern const char kNewTabSessionTimeMet[];
 
 // All the events declared below are the string names
 // of deferred onboarding events for the Incognito Window
@@ -26,12 +29,6 @@
 // The user has opened an incognito window.
 extern const char kIncognitoWindowOpened[];
 
-// All the events declared below are the string names
-// of common deferred onboarding events
-
-// The user has accumulated 2 hours of active session time (one-off event).
-extern const char kSessionTime[];
-
 #endif  // defined(OS_WIN) || defined(OS_LINUX)
 
 #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
diff --git a/components/feedback/system_logs/system_logs_fetcher.h b/components/feedback/system_logs/system_logs_fetcher.h
index 1799d4f..238bb60 100644
--- a/components/feedback/system_logs/system_logs_fetcher.h
+++ b/components/feedback/system_logs/system_logs_fetcher.h
@@ -51,7 +51,8 @@
   // Adds a source to use when fetching.
   void AddSource(std::unique_ptr<SystemLogsSource> source);
 
-  // Starts the fetch process.
+  // Starts the fetch process. After the fetch completes, this instance calls
+  // |callback|, then schedules itself to be deleted.
   void Fetch(const SysLogsFetcherCallback& callback);
 
  private:
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index 1405106..513d447 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -240,7 +240,7 @@
  protected:
   InfoBarDelegate();
 
-  InfoBar* infobar() { return infobar_; }
+  InfoBar* infobar() const { return infobar_; }
 
  private:
   // The InfoBar associated with us.
diff --git a/components/omnibox/browser/history_url_provider_unittest.cc b/components/omnibox/browser/history_url_provider_unittest.cc
index bbc8e0c..5011aec 100644
--- a/components/omnibox/browser/history_url_provider_unittest.cc
+++ b/components/omnibox/browser/history_url_provider_unittest.cc
@@ -1178,9 +1178,7 @@
     }
     autocomplete_->scoring_params_ = test_cases[i].scoring_params;
 
-    // Test the experiment (scoring enabled). When scoring is disabled, it uses
-    // the default experimental scoring.
-    autocomplete_->scoring_params_.experimental_scoring_enabled = true;
+    // Test the experimental scoring params.
     ASSERT_NO_FATAL_FAILURE(RunTest(ASCIIToUTF16(test_cases[i].input),
                                     std::string(), false, output, max_matches));
     for (int j = 0; j < max_matches; ++j) {
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 1413e0d..22e7b00 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -428,20 +428,11 @@
 
 void OmniboxFieldTrial::GetExperimentalHUPScoringParams(
     HUPScoringParams* scoring_params) {
-  scoring_params->experimental_scoring_enabled = false;
-
   VariationParams params;
   if (!variations::GetVariationParams(kBundledExperimentFieldTrialName,
                                       &params))
     return;
 
-  VariationParams::const_iterator it = params.find(kHUPNewScoringEnabledParam);
-  if (it != params.end()) {
-    int enabled = 0;
-    if (base::StringToInt(it->second, &enabled))
-      scoring_params->experimental_scoring_enabled = (enabled != 0);
-  }
-
   InitializeScoreBuckets(params, kHUPNewScoringTypedCountRelevanceCapParam,
       kHUPNewScoringTypedCountHalfLifeTimeParam,
       kHUPNewScoringTypedCountScoreBucketsParam,
@@ -757,8 +748,6 @@
 const char OmniboxFieldTrial::kPhysicalWebAfterTypingRule[] =
     "PhysicalWebAfterTyping";
 
-const char OmniboxFieldTrial::kHUPNewScoringEnabledParam[] =
-    "HUPExperimentalScoringEnabled";
 const char OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam[] =
     "TypedCountRelevanceCap";
 const char OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam[] =
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 3dfa3734..bc94a4b 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -109,9 +109,7 @@
     bool use_decay_factor_;
   };
 
-  HUPScoringParams() : experimental_scoring_enabled(false) {}
-
-  bool experimental_scoring_enabled;
+  HUPScoringParams() {}
 
   ScoreBuckets typed_count_buckets;
 
@@ -262,8 +260,7 @@
   // bundled omnibox field trial.
 
   // Initializes the HUP |scoring_params| based on the active HUP scoring
-  // experiment.  If there is no such experiment, this function simply sets
-  // |scoring_params|->experimental_scoring_enabled to false.
+  // experiment.
   static void GetDefaultHUPScoringParams(HUPScoringParams* scoring_params);
   static void GetExperimentalHUPScoringParams(HUPScoringParams* scoring_params);
 
@@ -469,7 +466,6 @@
   static const char kPhysicalWebAfterTypingRule[];
 
   // Parameter names used by the HUP new scoring experiments.
-  static const char kHUPNewScoringEnabledParam[];
   static const char kHUPNewScoringTypedCountRelevanceCapParam[];
   static const char kHUPNewScoringTypedCountHalfLifeTimeParam[];
   static const char kHUPNewScoringTypedCountScoreBucketsParam[];
diff --git a/components/omnibox/browser/omnibox_field_trial_unittest.cc b/components/omnibox/browser/omnibox_field_trial_unittest.cc
index f4d9183..3890a55 100644
--- a/components/omnibox/browser/omnibox_field_trial_unittest.cc
+++ b/components/omnibox/browser/omnibox_field_trial_unittest.cc
@@ -396,7 +396,6 @@
 TEST_F(OmniboxFieldTrialTest, HUPNewScoringFieldTrial) {
   {
     std::map<std::string, std::string> params;
-    params[std::string(OmniboxFieldTrial::kHUPNewScoringEnabledParam)] = "1";
     params[std::string(
         OmniboxFieldTrial::kHUPNewScoringTypedCountRelevanceCapParam)] = "56";
     params[std::string(
@@ -419,7 +418,6 @@
 
   HUPScoringParams scoring_params;
   OmniboxFieldTrial::GetExperimentalHUPScoringParams(&scoring_params);
-  EXPECT_TRUE(scoring_params.experimental_scoring_enabled);
   EXPECT_EQ(56, scoring_params.typed_count_buckets.relevance_cap());
   EXPECT_EQ(77, scoring_params.typed_count_buckets.half_life_days());
   ASSERT_EQ(3u, scoring_params.typed_count_buckets.buckets().size());
@@ -441,7 +439,6 @@
 TEST_F(OmniboxFieldTrialTest, HUPNewScoringFieldTrialWithDecayFactor) {
   {
     std::map<std::string, std::string> params;
-    params[OmniboxFieldTrial::kHUPNewScoringEnabledParam] = "1";
     params[OmniboxFieldTrial::kHUPNewScoringTypedCountHalfLifeTimeParam] = "10";
     params[OmniboxFieldTrial::kHUPNewScoringTypedCountUseDecayFactorParam] =
         "1";
@@ -455,7 +452,6 @@
 
   HUPScoringParams scoring_params;
   OmniboxFieldTrial::GetExperimentalHUPScoringParams(&scoring_params);
-  EXPECT_TRUE(scoring_params.experimental_scoring_enabled);
   EXPECT_EQ(10, scoring_params.typed_count_buckets.half_life_days());
   ASSERT_EQ(3u, scoring_params.typed_count_buckets.buckets().size());
   ASSERT_TRUE(scoring_params.typed_count_buckets.use_decay_factor());
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index 6359865..a55f986 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
 #include "components/payments/content/can_make_payment_query_factory.h"
 #include "components/payments/content/origin_security_checker.h"
 #include "components/payments/content/payment_details_validation.h"
@@ -110,6 +111,30 @@
   state_ = base::MakeUnique<PaymentRequestState>(
       spec_.get(), this, delegate_->GetApplicationLocale(),
       delegate_->GetPersonalDataManager(), delegate_.get(), &journey_logger_);
+
+  journey_logger_.SetRequestedInformation(
+      spec_->request_shipping(), spec_->request_payer_email(),
+      spec_->request_payer_phone(), spec_->request_payer_name());
+
+  // Log metrics around which payment methods are requested by the merchant.
+  GURL google_pay_url(kGooglePayMethodName);
+  GURL android_pay_url(kAndroidPayMethodName);
+  // Looking for payment methods that are NOT google-related payment methods.
+  auto non_google_it =
+      std::find_if(spec_->url_payment_method_identifiers().begin(),
+                   spec_->url_payment_method_identifiers().end(),
+                   [google_pay_url, android_pay_url](const GURL& url) {
+                     return url != google_pay_url && url != android_pay_url;
+                   });
+  journey_logger_.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/!spec_->supported_card_networks().empty(),
+      /*requested_method_google=*/
+      base::ContainsValue(spec_->url_payment_method_identifiers(),
+                          google_pay_url) ||
+          base::ContainsValue(spec_->url_payment_method_identifiers(),
+                              android_pay_url),
+      /*requested_method_other=*/non_google_it !=
+          spec_->url_payment_method_identifiers().end());
 }
 
 void PaymentRequest::Show() {
@@ -124,7 +149,6 @@
     LOG(ERROR) << "A PaymentRequest UI is already showing";
     journey_logger_.SetNotShown(
         JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
-    has_recorded_completion_ = true;
     client_->OnError(mojom::PaymentErrorReason::USER_CANCEL);
     OnConnectionTerminated();
     return;
@@ -133,7 +157,6 @@
   if (!state_->AreRequestedMethodsSupported()) {
     journey_logger_.SetNotShown(
         JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
-    has_recorded_completion_ = true;
     client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED);
     if (observer_for_testing_)
       observer_for_testing_->OnNotSupportedError();
@@ -142,9 +165,6 @@
   }
 
   journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
-  journey_logger_.SetRequestedInformation(
-      spec_->request_shipping(), spec_->request_payer_email(),
-      spec_->request_payer_phone(), spec_->request_payer_name());
 
   delegate_->ShowDialog(this);
 }
diff --git a/components/payments/content/payment_request_spec.cc b/components/payments/content/payment_request_spec.cc
index c831329..e328318 100644
--- a/components/payments/content/payment_request_spec.cc
+++ b/components/payments/content/payment_request_spec.cc
@@ -129,6 +129,8 @@
 }  // namespace
 
 const char kBasicCardMethodName[] = "basic-card";
+const char kGooglePayMethodName[] = "https://google.com/pay";
+const char kAndroidPayMethodName[] = "https://android.com/pay";
 
 PaymentRequestSpec::PaymentRequestSpec(
     mojom::PaymentOptionsPtr options,
diff --git a/components/payments/content/payment_request_spec.h b/components/payments/content/payment_request_spec.h
index 4745acf..01a9e8cc 100644
--- a/components/payments/content/payment_request_spec.h
+++ b/components/payments/content/payment_request_spec.h
@@ -23,8 +23,11 @@
 
 class PaymentInstrument;
 
-// Identifier for the basic card payment method in the PaymentMethodData.
+// Identifier for the basic card payment method and google-related payment
+// methods in the PaymentMethodData.
 extern const char kBasicCardMethodName[];
+extern const char kGooglePayMethodName[];
+extern const char kAndroidPayMethodName[];
 
 // The spec contains all the options that the merchant has specified about this
 // Payment Request. It's a (mostly) read-only view, which can be updated in
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc
index 14c89929..1cf94f3 100644
--- a/components/payments/core/journey_logger.cc
+++ b/components/payments/core/journey_logger.cc
@@ -136,6 +136,20 @@
     SetEventOccurred(EVENT_REQUEST_PAYER_NAME);
 }
 
+void JourneyLogger::SetRequestedPaymentMethodTypes(
+    bool requested_basic_card,
+    bool requested_method_google,
+    bool requested_method_other) {
+  if (requested_basic_card)
+    SetEventOccurred(EVENT_REQUEST_METHOD_BASIC_CARD);
+
+  if (requested_method_google)
+    SetEventOccurred(EVENT_REQUEST_METHOD_GOOGLE);
+
+  if (requested_method_other)
+    SetEventOccurred(EVENT_REQUEST_METHOD_OTHER);
+}
+
 void JourneyLogger::SetCompleted() {
   RecordJourneyStatsHistograms(COMPLETION_STATUS_COMPLETED);
 }
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h
index 6b8104a..779b276 100644
--- a/components/payments/core/journey_logger.h
+++ b/components/payments/core/journey_logger.h
@@ -69,23 +69,44 @@
   // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
   // GENERATED_JAVA_CLASS_NAME_OVERRIDE: Event
   enum Event {
+    // Initiated means the PaymentRequest object was constructed.
     EVENT_INITIATED = 0,
+    // PaymentRequest was triggered via .show() and a native UI was shown.
     EVENT_SHOWN = 1 << 0,
     EVENT_PAY_CLICKED = 1 << 1,
     EVENT_RECEIVED_INSTRUMENT_DETAILS = 1 << 2,
+    // PaymentRequest was triggered via .show() and no UI was shown because we
+    // skipped directly to the payment app.
     EVENT_SKIPPED_SHOW = 1 << 3,
+    // .complete() was called by the merchant, completing the flow.
     EVENT_COMPLETED = 1 << 4,
+    // The user aborted the flow by either dismissing it explicitely, or
+    // navigating away (if possible).
     EVENT_USER_ABORTED = 1 << 5,
+    // Other reasons for aborting include the merchant calling .abort(), the
+    // merchant triggering a navigation, the tab closing, the browser closing,
+    // etc. See implementation for details.
     EVENT_OTHER_ABORTED = 1 << 6,
     EVENT_HAD_INITIAL_FORM_OF_PAYMENT = 1 << 7,
     EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS = 1 << 8,
+    // canMakePayment was called with a result of "true" or "false",
+    // respectively. An absence of both events means canMakePayment was not
+    // called, or the user was in incognito mode.
     EVENT_CAN_MAKE_PAYMENT_TRUE = 1 << 9,
     EVENT_CAN_MAKE_PAYMENT_FALSE = 1 << 10,
+    // Correspond to the merchant specifying requestShipping, requestPayerName,
+    // requestPayerEmail, requestPayerPhone.
     EVENT_REQUEST_SHIPPING = 1 << 11,
     EVENT_REQUEST_PAYER_NAME = 1 << 12,
     EVENT_REQUEST_PAYER_EMAIL = 1 << 13,
     EVENT_REQUEST_PAYER_PHONE = 1 << 14,
-    EVENT_ENUM_MAX = 32768,
+    // The merchant requested at least one basic-card method.
+    EVENT_REQUEST_METHOD_BASIC_CARD = 1 << 15,
+    // The merchant requested a Google payment method.
+    EVENT_REQUEST_METHOD_GOOGLE = 1 << 16,
+    // The merchant requested a non-Google, non-basic-card payment method.
+    EVENT_REQUEST_METHOD_OTHER = 1 << 17,
+    EVENT_ENUM_MAX = 262144,
   };
 
   // The reason why the Payment Request was aborted.
@@ -152,6 +173,14 @@
                                bool requested_phone,
                                bool requested_name);
 
+  // Records the requested payment method types. A value should be true if at
+  // least one payment method in the category (basic-card, google payment method
+  // or other url-based payment method, respectively) is requested.
+  // TODO(crbug.com/754811): Add support for non-basic-card, non-URL methods.
+  void SetRequestedPaymentMethodTypes(bool requested_basic_card,
+                                      bool requested_method_google,
+                                      bool requested_method_other);
+
   // Records that the Payment Request was completed successfully, and starts the
   // logging of all the journey metrics.
   void SetCompleted();
diff --git a/components/payments/core/journey_logger_unittest.cc b/components/payments/core/journey_logger_unittest.cc
index acb1e4a..389a2bf7 100644
--- a/components/payments/core/journey_logger_unittest.cc
+++ b/components/payments/core/journey_logger_unittest.cc
@@ -338,6 +338,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
@@ -364,6 +367,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -378,6 +384,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
@@ -404,6 +413,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -418,6 +430,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
@@ -444,6 +459,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -459,6 +477,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for all the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 1,
@@ -485,6 +506,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -499,6 +523,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
@@ -525,6 +552,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -539,6 +569,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
@@ -565,6 +598,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -579,6 +615,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
@@ -605,6 +644,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -620,6 +662,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had suggestions for none of the requested sections.
   logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_PAYMENT_METHOD, 0,
@@ -646,6 +691,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -661,6 +709,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/false, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had incomplete suggestions for the requested
   // sections.
@@ -688,6 +739,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -703,6 +757,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had incomplete suggestions for one of the requested
   // sections.
@@ -732,6 +789,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the completion status metrics based on whether the user had
@@ -747,6 +807,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/false);
 
   // Simulate that the user had incomplete suggestions for one of the requested
   // sections.
@@ -776,6 +839,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the metrics are logged correctly for two simultaneous Payment
@@ -792,10 +858,16 @@
   logger1.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/true,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger1.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/true,
+      /*requested_method_other=*/true);
   logger2.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
   logger2.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/false,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger2.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/false, /*requested_method_google=*/false,
+      /*requested_method_other=*/true);
 
   logger1.SetCanMakePaymentValue(true);
 
@@ -827,6 +899,9 @@
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
   EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
   // logger1
   EXPECT_TRUE(buckets[1].min & JourneyLogger::EVENT_SHOWN);
   EXPECT_FALSE(buckets[1].min &
@@ -842,6 +917,9 @@
   EXPECT_TRUE(buckets[1].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
   EXPECT_TRUE(buckets[1].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
   EXPECT_FALSE(buckets[1].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_TRUE(buckets[1].min & JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD);
+  EXPECT_TRUE(buckets[1].min & JourneyLogger::EVENT_REQUEST_METHOD_GOOGLE);
+  EXPECT_TRUE(buckets[1].min & JourneyLogger::EVENT_REQUEST_METHOD_OTHER);
 }
 
 // Tests that the Payment Request UKMs are logged correctly when the user aborts
@@ -857,6 +935,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/true,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/false,
+      /*requested_method_other=*/false);
 
   // Simulate that the user aborts after being shown the Payment Request and
   // clicking pay.
@@ -886,7 +967,8 @@
   ASSERT_NE(nullptr, step_metric);
   EXPECT_EQ(JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_PAY_CLICKED |
                 JourneyLogger::EVENT_REQUEST_SHIPPING |
-                JourneyLogger::EVENT_REQUEST_PAYER_EMAIL,
+                JourneyLogger::EVENT_REQUEST_PAYER_EMAIL |
+                JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD,
             step_metric->value);
 }
 
@@ -903,6 +985,9 @@
   logger.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/true,
       /*requested_phone=*/false, /*requested_name=*/false);
+  logger.SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/true, /*requested_method_google=*/false,
+      /*requested_method_other=*/false);
 
   // Simulate that the user aborts after being shown the Payment Request.
   logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
@@ -928,7 +1013,8 @@
       ukm::TestUkmRecorder::FindMetric(entry, internal::kUKMEventsMetricName);
   ASSERT_NE(nullptr, step_metric);
   EXPECT_EQ(JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_REQUEST_SHIPPING |
-                JourneyLogger::EVENT_REQUEST_PAYER_EMAIL,
+                JourneyLogger::EVENT_REQUEST_PAYER_EMAIL |
+                JourneyLogger::EVENT_REQUEST_METHOD_BASIC_CARD,
             step_metric->value);
 }
 
diff --git a/components/safe_browsing/BUILD.gn b/components/safe_browsing/BUILD.gn
index f09e7c6..41b9f45 100644
--- a/components/safe_browsing/BUILD.gn
+++ b/components/safe_browsing/BUILD.gn
@@ -6,7 +6,13 @@
 
 proto_library("csd_proto") {
   sources = [
-    "csd.proto",
+    "proto/csd.proto",
+  ]
+}
+
+proto_library("webui_proto") {
+  sources = [
+    "proto/webui.proto",
   ]
 }
 
diff --git a/components/safe_browsing/browser/DEPS b/components/safe_browsing/browser/DEPS
index d98a2a4d..b219d834 100644
--- a/components/safe_browsing/browser/DEPS
+++ b/components/safe_browsing/browser/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
   "+components/history/core/browser",
-  "+components/safe_browsing/csd.pb.h",
+  "+components/safe_browsing/proto/csd.pb.h",
   "+content/public/browser",
   "+ipc/ipc_message.h",
   "+net/cookies",
diff --git a/components/safe_browsing/browser/threat_details.h b/components/safe_browsing/browser/threat_details.h
index 9e6918a..849105c0 100644
--- a/components/safe_browsing/browser/threat_details.h
+++ b/components/safe_browsing/browser/threat_details.h
@@ -21,7 +21,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "components/safe_browsing/common/safebrowsing_types.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents_observer.h"
diff --git a/components/safe_browsing/password_protection/password_protection_service.h b/components/safe_browsing/password_protection/password_protection_service.h
index 802fbff..f6ef8d41 100644
--- a/components/safe_browsing/password_protection/password_protection_service.h
+++ b/components/safe_browsing/password_protection/password_protection_service.h
@@ -18,7 +18,7 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "base/values.h"
 #include "components/history/core/browser/history_service_observer.h"
-#include "components/safe_browsing/csd.pb.h"
+#include "components/safe_browsing/proto/csd.pb.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
diff --git a/components/safe_browsing/csd.proto b/components/safe_browsing/proto/csd.proto
similarity index 100%
rename from components/safe_browsing/csd.proto
rename to components/safe_browsing/proto/csd.proto
diff --git a/components/safe_browsing/web_ui/webui.proto b/components/safe_browsing/proto/webui.proto
similarity index 100%
rename from components/safe_browsing/web_ui/webui.proto
rename to components/safe_browsing/proto/webui.proto
diff --git a/components/safe_browsing/web_ui/BUILD.gn b/components/safe_browsing/web_ui/BUILD.gn
index 707f14b6..7bd2a139 100644
--- a/components/safe_browsing/web_ui/BUILD.gn
+++ b/components/safe_browsing/web_ui/BUILD.gn
@@ -12,11 +12,11 @@
 
   deps = [
     ":constants",
-    ":webui_proto",
     "//base",
     "//components/resources:components_resources_grit",
     "//components/resources:components_scaled_resources_grit",
     "//components/safe_browsing:features",
+    "//components/safe_browsing:webui_proto",
     "//components/safe_browsing/common:safe_browsing_prefs",
     "//components/safe_browsing_db:v4_local_database_manager",
     "//components/strings:components_strings_grit",
@@ -33,9 +33,3 @@
     "constants.h",
   ]
 }
-
-proto_library("webui_proto") {
-  sources = [
-    "webui.proto",
-  ]
-}
diff --git a/components/safe_browsing/web_ui/safe_browsing_ui.h b/components/safe_browsing/web_ui/safe_browsing_ui.h
index 89abc3a..54fef1e 100644
--- a/components/safe_browsing/web_ui/safe_browsing_ui.h
+++ b/components/safe_browsing/web_ui/safe_browsing_ui.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_SAFE_BROWSING_WEBUI_SAFE_BROWSING_UI_H_
 
 #include "base/macros.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_data_source.h"
diff --git a/components/safe_browsing_db/BUILD.gn b/components/safe_browsing_db/BUILD.gn
index bedfc8e..f4ac984 100644
--- a/components/safe_browsing_db/BUILD.gn
+++ b/components/safe_browsing_db/BUILD.gn
@@ -204,7 +204,7 @@
     ":v4_protocol_manager_util",
     ":v4_store",
     "//base",
-    "//components/safe_browsing/web_ui:webui_proto",
+    "//components/safe_browsing:webui_proto",
     "//content/public/browser",
   ]
 }
@@ -233,7 +233,7 @@
     ":v4_protocol_manager_util",
     "//base",
     "//components/data_use_measurement/core",
-    "//components/safe_browsing/web_ui:webui_proto",
+    "//components/safe_browsing:webui_proto",
     "//content/public/browser",
     "//net",
     "//url",
@@ -255,7 +255,7 @@
     ":v4_protocol_manager_util",
     ":v4_update_protocol_manager",
     "//base",
-    "//components/safe_browsing/web_ui:webui_proto",
+    "//components/safe_browsing:webui_proto",
     "//content/public/browser",
     "//net",
     "//url",
@@ -310,7 +310,7 @@
     ":v4_protocol_manager_util",
     ":v4_rice",
     "//base",
-    "//components/safe_browsing/web_ui:webui_proto",
+    "//components/safe_browsing:webui_proto",
     "//crypto",
   ]
 }
@@ -338,8 +338,8 @@
     ":v4_protocol_manager_util",
     "//base",
     "//components/data_use_measurement/core",
+    "//components/safe_browsing:webui_proto",
     "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/safe_browsing/web_ui:webui_proto",
     "//net",
     "//url",
   ]
diff --git a/components/safe_browsing_db/DEPS b/components/safe_browsing_db/DEPS
index 1c63417..c5e7e01 100644
--- a/components/safe_browsing_db/DEPS
+++ b/components/safe_browsing_db/DEPS
@@ -2,7 +2,7 @@
   "+components/data_use_measurement/core",
   "+components/safe_browsing/common/safe_browsing_prefs.h",
   "+components/safe_browsing/features.h",
-  "+components/safe_browsing/web_ui/webui.pb.h",
+  "+components/safe_browsing/proto/webui.pb.h",
   "+components/variations",
   "+components/version_info",
   "+content/public/browser",
diff --git a/components/safe_browsing_db/v4_database.cc b/components/safe_browsing_db/v4_database.cc
index 9e8a74f0..f380624 100644
--- a/components/safe_browsing_db/v4_database.cc
+++ b/components/safe_browsing_db/v4_database.cc
@@ -11,7 +11,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "components/safe_browsing_db/v4_database.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/components/safe_browsing_db/v4_database.h b/components/safe_browsing_db/v4_database.h
index 576e894..aea51b0 100644
--- a/components/safe_browsing_db/v4_database.h
+++ b/components/safe_browsing_db/v4_database.h
@@ -16,7 +16,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
 #include "components/safe_browsing_db/v4_store.h"
 
diff --git a/components/safe_browsing_db/v4_get_hash_protocol_manager.h b/components/safe_browsing_db/v4_get_hash_protocol_manager.h
index 6ae5c302..22510c9c 100644
--- a/components/safe_browsing_db/v4_get_hash_protocol_manager.h
+++ b/components/safe_browsing_db/v4_get_hash_protocol_manager.h
@@ -24,7 +24,7 @@
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "components/safe_browsing_db/safebrowsing.pb.h"
 #include "components/safe_browsing_db/util.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
diff --git a/components/safe_browsing_db/v4_local_database_manager.h b/components/safe_browsing_db/v4_local_database_manager.h
index 29d7544..c2cd94f4 100644
--- a/components/safe_browsing_db/v4_local_database_manager.h
+++ b/components/safe_browsing_db/v4_local_database_manager.h
@@ -12,7 +12,7 @@
 #include <unordered_set>
 
 #include "base/memory/weak_ptr.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/safe_browsing_db/hit_report.h"
 #include "components/safe_browsing_db/v4_database.h"
diff --git a/components/safe_browsing_db/v4_store.cc b/components/safe_browsing_db/v4_store.cc
index e7bce82..2d51d61c 100644
--- a/components/safe_browsing_db/v4_store.cc
+++ b/components/safe_browsing_db/v4_store.cc
@@ -12,7 +12,7 @@
 #include "base/metrics/sparse_histogram.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "components/safe_browsing_db/v4_rice.h"
 #include "components/safe_browsing_db/v4_store.pb.h"
 #include "crypto/secure_hash.h"
diff --git a/components/safe_browsing_db/v4_store.h b/components/safe_browsing_db/v4_store.h
index 3ebd47a..392d841c 100644
--- a/components/safe_browsing_db/v4_store.h
+++ b/components/safe_browsing_db/v4_store.h
@@ -13,7 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
 
 namespace safe_browsing {
diff --git a/components/safe_browsing_db/v4_update_protocol_manager.h b/components/safe_browsing_db/v4_update_protocol_manager.h
index db01b070..73f076f6 100644
--- a/components/safe_browsing_db/v4_update_protocol_manager.h
+++ b/components/safe_browsing_db/v4_update_protocol_manager.h
@@ -22,7 +22,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/safe_browsing/web_ui/webui.pb.h"
+#include "components/safe_browsing/proto/webui.pb.h"
 #include "components/safe_browsing_db/safebrowsing.pb.h"
 #include "components/safe_browsing_db/util.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
diff --git a/components/security_state/content/content_utils.cc b/components/security_state/content/content_utils.cc
index 37dee4f..c532fc1 100644
--- a/components/security_state/content/content_utils.cc
+++ b/components/security_state/content/content_utils.cc
@@ -99,7 +99,7 @@
         content::SecurityStyleExplanation(
             l10n_util::GetStringUTF8(IDS_SHA1),
             l10n_util::GetStringUTF8(IDS_SHA1_DESCRIPTION),
-            !!security_info.certificate,
+            security_info.certificate,
             blink::WebMixedContentContextType::kNotMixedContent));
   }
 
@@ -108,7 +108,7 @@
         content::SecurityStyleExplanation(
             l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING),
             l10n_util::GetStringUTF8(IDS_SUBJECT_ALT_NAME_MISSING_DESCRIPTION),
-            !!security_info.certificate,
+            security_info.certificate,
             blink::WebMixedContentContextType::kNotMixedContent));
   }
 
@@ -124,7 +124,7 @@
         l10n_util::GetStringUTF8(IDS_CERTIFICATE_CHAIN_ERROR),
         l10n_util::GetStringFUTF8(
             IDS_CERTIFICATE_CHAIN_ERROR_DESCRIPTION_FORMAT, error_string),
-        !!security_info.certificate,
+        security_info.certificate,
         blink::WebMixedContentContextType::kNotMixedContent);
 
     if (is_cert_status_minor_error) {
@@ -155,7 +155,7 @@
               l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE),
               l10n_util::GetStringFUTF8(
                   IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION, issuer_name),
-              !!security_info.certificate,
+              security_info.certificate,
               blink::WebMixedContentContextType::kNotMixedContent));
     }
   }
@@ -275,7 +275,7 @@
         content::SecurityStyleExplanation(
             l10n_util::GetStringUTF8(IDS_MIXED_ACTIVE_CONTENT_SUMMARY),
             l10n_util::GetStringUTF8(IDS_MIXED_ACTIVE_CONTENT_DESCRIPTION),
-            false, blink::WebMixedContentContextType::kBlockable));
+            nullptr, blink::WebMixedContentContextType::kBlockable));
   }
 
   security_style_explanations->displayed_mixed_content =
@@ -289,7 +289,7 @@
         content::SecurityStyleExplanation(
             l10n_util::GetStringUTF8(IDS_MIXED_PASSIVE_CONTENT_SUMMARY),
             l10n_util::GetStringUTF8(IDS_MIXED_PASSIVE_CONTENT_DESCRIPTION),
-            false, blink::WebMixedContentContextType::kOptionallyBlockable));
+            nullptr, blink::WebMixedContentContextType::kOptionallyBlockable));
   }
 
   security_style_explanations->contained_mixed_form =
diff --git a/components/security_state/content/content_utils_unittest.cc b/components/security_state/content/content_utils_unittest.cc
index e2615b32..d4e30e83 100644
--- a/components/security_state/content/content_utils_unittest.cc
+++ b/components/security_state/content/content_utils_unittest.cc
@@ -580,7 +580,7 @@
 TEST(SecurityStateContentUtilsTest, DefaultSecurityStyleExplanation) {
   content::SecurityStyleExplanation explanation("summary", "description");
 
-  EXPECT_EQ(false, explanation.has_certificate);
+  EXPECT_EQ(false, !!explanation.certificate);
   EXPECT_EQ(blink::WebMixedContentContextType::kNotMixedContent,
             explanation.mixed_content_type);
 }
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 2a77332..9f4e4fb 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -32,8 +32,8 @@
 
 // Describes the overall security state of the page.
 //
-// If you reorder, add, or delete values from this enum, you must also
-// update the UI icons in ToolbarModelImpl::GetIconForSecurityLevel.
+// If you change this enum, you may need to update the UI icons in
+// ToolbarModelImpl::GetVectorIcon and GetIconForSecurityState.
 //
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.security_state
@@ -56,7 +56,7 @@
   // HTTPS (non-EV) with valid cert.
   SECURE,
 
-  // HTTPS, but with an outdated protocol version.
+  // Obsolete, do not use. TODO(lgarron): Remove via https://crbug.com/645698.
   SECURITY_WARNING,
 
   // HTTPS, but the certificate verification chain is anchored on a
diff --git a/components/viz/client/client_shared_bitmap_manager.cc b/components/viz/client/client_shared_bitmap_manager.cc
index 8c1d51a..63b30e7 100644
--- a/components/viz/client/client_shared_bitmap_manager.cc
+++ b/components/viz/client/client_shared_bitmap_manager.cc
@@ -24,7 +24,7 @@
 class ClientSharedBitmap : public SharedBitmap {
  public:
   ClientSharedBitmap(
-      scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+      scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
           shared_bitmap_allocation_notifier,
       base::SharedMemory* shared_memory,
       const SharedBitmapId& id,
@@ -36,7 +36,7 @@
             std::move(shared_bitmap_allocation_notifier)) {}
 
   ClientSharedBitmap(
-      scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+      scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
           shared_bitmap_allocation_notifier,
       std::unique_ptr<base::SharedMemory> shared_memory_holder,
       const SharedBitmapId& id,
@@ -60,7 +60,7 @@
   }
 
  private:
-  scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+  scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
       shared_bitmap_allocation_notifier_;
   std::unique_ptr<base::SharedMemory> shared_memory_holder_;
 };
@@ -112,7 +112,7 @@
 }  // namespace
 
 ClientSharedBitmapManager::ClientSharedBitmapManager(
-    scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+    scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
         shared_bitmap_allocation_notifier)
     : shared_bitmap_allocation_notifier_(
           std::move(shared_bitmap_allocation_notifier)) {}
diff --git a/components/viz/client/client_shared_bitmap_manager.h b/components/viz/client/client_shared_bitmap_manager.h
index 8bb479c..553bb57 100644
--- a/components/viz/client/client_shared_bitmap_manager.h
+++ b/components/viz/client/client_shared_bitmap_manager.h
@@ -13,9 +13,9 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/shared_memory.h"
 #include "base/synchronization/lock.h"
-#include "cc/ipc/shared_bitmap_allocation_notifier.mojom.h"
 #include "components/viz/common/resources/shared_bitmap_manager.h"
 #include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
+#include "services/viz/public/interfaces/compositing/shared_bitmap_allocation_notifier.mojom.h"
 
 namespace viz {
 
@@ -25,7 +25,7 @@
 class ClientSharedBitmapManager : public SharedBitmapManager {
  public:
   explicit ClientSharedBitmapManager(
-      scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+      scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
           shared_bitmap_allocation_notifier);
   ~ClientSharedBitmapManager() override;
 
@@ -43,7 +43,7 @@
   uint32_t NotifyAllocatedSharedBitmap(base::SharedMemory* memory,
                                        const SharedBitmapId& id);
 
-  scoped_refptr<cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
+  scoped_refptr<mojom::ThreadSafeSharedBitmapAllocationNotifierPtr>
       shared_bitmap_allocation_notifier_;
 
   base::Lock lock_;
diff --git a/components/viz/host/BUILD.gn b/components/viz/host/BUILD.gn
index 092768f..be3dc76 100644
--- a/components/viz/host/BUILD.gn
+++ b/components/viz/host/BUILD.gn
@@ -8,6 +8,8 @@
   defines = [ "VIZ_HOST_IMPLEMENTATION" ]
 
   sources = [
+    "hit_test/hit_test_query.cc",
+    "hit_test/hit_test_query.h",
     "host_frame_sink_client.h",
     "host_frame_sink_manager.cc",
     "host_frame_sink_manager.h",
@@ -21,6 +23,7 @@
   deps = [
     "//base",
     "//cc/ipc:interfaces",
+    "//components/viz/common",
     "//gpu/ipc/client",
     "//gpu/ipc/common",
     "//services/ui/gpu/interfaces",
@@ -36,6 +39,8 @@
     "//gpu/command_buffer/client",
     "//gpu/ipc/host",
     "//services/viz/compositing/privileged/interfaces",
+    "//services/viz/public/interfaces/hit_test",
+    "//ui/gfx/geometry",
   ]
 }
 
@@ -43,6 +48,7 @@
   testonly = true
 
   sources = [
+    "hit_test/hit_test_query_unittest.cc",
     "host_frame_sink_manager_unittests.cc",
     "server_gpu_memory_buffer_manager_unittest.cc",
   ]
@@ -52,7 +58,6 @@
     "//base",
     "//base/test:test_support",
     "//cc/ipc:interfaces",
-    "//components/viz/host/hit_test:unit_tests",
     "//gpu/ipc/host",
     "//mojo/public/cpp/bindings",
     "//services/ui/gpu/interfaces",
diff --git a/components/viz/host/hit_test/BUILD.gn b/components/viz/host/hit_test/BUILD.gn
deleted file mode 100644
index aa16214..0000000
--- a/components/viz/host/hit_test/BUILD.gn
+++ /dev/null
@@ -1,36 +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.
-
-import("//components/viz/viz.gni")
-import("//testing/test.gni")
-
-viz_source_set("hit_test") {
-  sources = [
-    "hit_test_query.cc",
-    "hit_test_query.h",
-  ]
-
-  public_deps = [
-    "//components/viz/common",
-    "//services/viz/public/interfaces/hit_test",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-  ]
-}
-
-viz_source_set("unit_tests") {
-  testonly = true
-
-  sources = [
-    "hit_test_query_unittest.cc",
-  ]
-
-  deps = [
-    ":hit_test",
-    "//base",
-    "//base/test:test_support",
-    "//cc:test_support",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/viz/host/hit_test/hit_test_query.h b/components/viz/host/hit_test/hit_test_query.h
index 31221bf..a99f745 100644
--- a/components/viz/host/hit_test/hit_test_query.h
+++ b/components/viz/host/hit_test/hit_test_query.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "components/viz/common/hit_test/aggregated_hit_test_region.h"
+#include "components/viz/host/viz_host_export.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "ui/gfx/geometry/point.h"
 
@@ -26,7 +27,7 @@
 // Finds the target for a given location based on the AggregatedHitTestRegion
 // list aggregated by HitTestAggregator.
 // TODO(riajiang): Handle 3d space cases correctly.
-class HitTestQuery {
+class VIZ_HOST_EXPORT HitTestQuery {
  public:
   HitTestQuery();
   ~HitTestQuery();
diff --git a/components/viz/service/DEPS b/components/viz/service/DEPS
index a29ec613..d840a11 100644
--- a/components/viz/service/DEPS
+++ b/components/viz/service/DEPS
@@ -2,6 +2,7 @@
   "+cc",
   "-components/viz/common/switches.h",
   "+components/viz/service",
+  "+services/viz/public/interfaces",
   "+third_party/skia",
   "+ui/gfx",
   "+ui/gfx/geometry",
@@ -12,6 +13,5 @@
   ".*_unittest\.cc": [
     "+components/viz/test",
     "+services/compositing/privileged/interfaces",
-    "+services/viz/public/interfaces/compositing",
   ]
 }
diff --git a/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc b/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc
index d320a3d..bc81f59 100644
--- a/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc
+++ b/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.cc
@@ -21,7 +21,7 @@
 }
 
 void SharedBitmapAllocationNotifierImpl::Bind(
-    cc::mojom::SharedBitmapAllocationNotifierRequest request) {
+    mojom::SharedBitmapAllocationNotifierRequest request) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (binding_.is_bound()) {
     DLOG(ERROR) << "Only one SharedBitmapAllocationNotifierRequest is "
diff --git a/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h b/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h
index 4e9330a..080fece 100644
--- a/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h
+++ b/components/viz/service/display_embedder/shared_bitmap_allocation_notifier_impl.h
@@ -9,10 +9,10 @@
 
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
-#include "cc/ipc/shared_bitmap_allocation_notifier.mojom.h"
 #include "components/viz/common/quads/shared_bitmap.h"
 #include "components/viz/service/viz_service_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "services/viz/public/interfaces/compositing/shared_bitmap_allocation_notifier.mojom.h"
 
 namespace viz {
 class ServerSharedBitmapManager;
@@ -23,7 +23,7 @@
 };
 
 class VIZ_SERVICE_EXPORT SharedBitmapAllocationNotifierImpl
-    : NON_EXPORTED_BASE(public cc::mojom::SharedBitmapAllocationNotifier) {
+    : NON_EXPORTED_BASE(public mojom::SharedBitmapAllocationNotifier) {
  public:
   explicit SharedBitmapAllocationNotifierImpl(
       ServerSharedBitmapManager* manager);
@@ -33,9 +33,9 @@
   void AddObserver(SharedBitmapAllocationObserver* observer);
   void RemoveObserver(SharedBitmapAllocationObserver* observer);
 
-  void Bind(cc::mojom::SharedBitmapAllocationNotifierRequest request);
+  void Bind(mojom::SharedBitmapAllocationNotifierRequest request);
 
-  // cc::mojom::SharedBitmapAllocationNotifier overrides:
+  // mojom::SharedBitmapAllocationNotifier overrides:
   void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
                                const SharedBitmapId& id) override;
   void DidDeleteSharedBitmap(const SharedBitmapId& id) override;
@@ -51,7 +51,7 @@
  private:
   THREAD_CHECKER(thread_checker_);
   ServerSharedBitmapManager* const manager_;
-  mojo::Binding<cc::mojom::SharedBitmapAllocationNotifier> binding_;
+  mojo::Binding<mojom::SharedBitmapAllocationNotifier> binding_;
   std::unordered_set<SharedBitmapId, SharedBitmapIdHash> owned_bitmaps_;
   base::ObserverList<SharedBitmapAllocationObserver> observers_;
   uint32_t last_sequence_number_ = 0;
diff --git a/components/viz/service/frame_sinks/DEPS b/components/viz/service/frame_sinks/DEPS
index 0216da50..d878155 100644
--- a/components/viz/service/frame_sinks/DEPS
+++ b/components/viz/service/frame_sinks/DEPS
@@ -6,5 +6,4 @@
   "+gpu/ipc/common",
   "+mojo/public/cpp/bindings",
   "+services/viz/compositing/privileged/interfaces",
-  "+services/viz/public/interfaces/compositing",
 ]
diff --git a/components/viz/service/hit_test/DEPS b/components/viz/service/hit_test/DEPS
index 37540ce..38ff3f7 100644
--- a/components/viz/service/hit_test/DEPS
+++ b/components/viz/service/hit_test/DEPS
@@ -1,6 +1,5 @@
 include_rules = [
   "+components/viz/service/surfaces",
-  "+services/viz/public/interfaces/hit_test",
 ]
 
 specific_include_rules = {
diff --git a/components/viz/service/hit_test/hit_test_aggregator.cc b/components/viz/service/hit_test/hit_test_aggregator.cc
index 301a38e..4b07ea15 100644
--- a/components/viz/service/hit_test/hit_test_aggregator.cc
+++ b/components/viz/service/hit_test/hit_test_aggregator.cc
@@ -47,15 +47,16 @@
 HitTestAggregator::~HitTestAggregator() = default;
 
 void HitTestAggregator::SubmitHitTestRegionList(
+    const SurfaceId& frame_sink_id,
     mojom::HitTestRegionListPtr hit_test_region_list) {
   DCHECK(ValidateHitTestRegionList(hit_test_region_list));
   // TODO(gklassen): Runtime validation that hit_test_region_list is valid.
   // TODO(gklassen): Inform FrameSink that the hit_test_region_list is invalid.
   // TODO(gklassen): FrameSink needs to inform the host of a difficult renderer.
-  pending_[hit_test_region_list->surface_id] = std::move(hit_test_region_list);
+  pending_[frame_sink_id] = std::move(hit_test_region_list);
 }
 
-void HitTestAggregator::PostTaskAggregate(SurfaceId display_surface_id) {
+void HitTestAggregator::PostTaskAggregate(const SurfaceId& display_surface_id) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&HitTestAggregator::Aggregate,
@@ -170,7 +171,7 @@
   AggregatedHitTestRegion* regions =
       static_cast<AggregatedHitTestRegion*>(write_buffer_.get());
 
-  regions[0].frame_sink_id = hit_test_region_list->surface_id.frame_sink_id();
+  regions[0].frame_sink_id = surface_id.frame_sink_id();
   regions[0].flags = hit_test_region_list->flags;
   regions[0].rect = hit_test_region_list->bounds;
   regions[0].transform = hit_test_region_list->transform;
diff --git a/components/viz/service/hit_test/hit_test_aggregator.h b/components/viz/service/hit_test/hit_test_aggregator.h
index 787d4a7..9d08d05 100644
--- a/components/viz/service/hit_test/hit_test_aggregator.h
+++ b/components/viz/service/hit_test/hit_test_aggregator.h
@@ -30,11 +30,12 @@
   // to SubmitCompositorFrame.  This is collected in pending_ until
   // surfaces are aggregated and put on the display.
   void SubmitHitTestRegionList(
+      const SurfaceId& surface_id,
       mojom::HitTestRegionListPtr hit_test_region_list);
 
   // Performs the work of Aggregate by creating a PostTask so that
   // the work is not directly on the call.
-  void PostTaskAggregate(SurfaceId display_surface_id);
+  void PostTaskAggregate(const SurfaceId& display_surface_id);
 
   // Called after surfaces have been aggregated into the DisplayFrame.
   // In this call HitTestRegionList structures received from active surfaces
diff --git a/components/viz/service/hit_test/hit_test_aggregator_unittest.cc b/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
index 002d50c7..c52abcb 100644
--- a/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
+++ b/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
@@ -232,7 +232,6 @@
     id++;
 
     auto hit_test_region_list = mojom::HitTestRegionList::New();
-    hit_test_region_list->surface_id = surface_id;
     hit_test_region_list->flags = mojom::kHitTestMine;
     hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
@@ -251,7 +250,7 @@
     }
 
     GetAggregator(kDisplayFrameSink)
-        ->SubmitHitTestRegionList(std::move(hit_test_region_list));
+        ->SubmitHitTestRegionList(surface_id, std::move(hit_test_region_list));
     return id;
   }
 
@@ -296,11 +295,11 @@
   SurfaceId display_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
 
   auto hit_test_region_list = mojom::HitTestRegionList::New();
-  hit_test_region_list->surface_id = display_surface_id;
   hit_test_region_list->flags = mojom::kHitTestMine;
   hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
-  aggregator->SubmitHitTestRegionList(std::move(hit_test_region_list));
+  aggregator->SubmitHitTestRegionList(display_surface_id,
+                                      std::move(hit_test_region_list));
   EXPECT_EQ(aggregator->Count(), 0);
 
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
@@ -345,10 +344,9 @@
 
   SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_r1 = mojom::HitTestRegion::New();
   e_hit_test_region_r1->surface_id = e_surface_id;
@@ -360,14 +358,15 @@
   e_hit_test_region_r2->flags = mojom::kHitTestMine;
   e_hit_test_region_r2->rect.SetRect(400, 100, 300, 400);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_r1));
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_r2));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_r1));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_r2));
 
   // Submit mojom::HitTestRegionList.
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
   // Add Surfaces to DisplayFrame in unexpected order.
@@ -427,10 +426,9 @@
   SurfaceId c1_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
   SurfaceId c2_surface_id = MakeSurfaceId(kDisplayFrameSink, 3);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_c1 = mojom::HitTestRegion::New();
   e_hit_test_region_c1->flags = mojom::kHitTestChildSurface;
@@ -442,26 +440,27 @@
   e_hit_test_region_c2->surface_id = c2_surface_id;
   e_hit_test_region_c2->rect.SetRect(400, 100, 400, 300);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c1));
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c2));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c1));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c2));
 
-  auto c1_hit_test_data = mojom::HitTestRegionList::New();
-  c1_hit_test_data->surface_id = c1_surface_id;
+  auto c1_hit_test_region_list = mojom::HitTestRegionList::New();
 
-  auto c2_hit_test_data = mojom::HitTestRegionList::New();
-  c2_hit_test_data->surface_id = c2_surface_id;
+  auto c2_hit_test_region_list = mojom::HitTestRegionList::New();
 
   // Submit in unexpected order.
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(c1_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c1_surface_id,
+                                      std::move(c1_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 2);
 
-  aggregator->SubmitHitTestRegionList(std::move(c2_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c2_surface_id,
+                                      std::move(c2_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 3);
 
   // Surfaces added to DisplayFrame in unexpected order.
@@ -531,10 +530,9 @@
   SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
   SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_div = mojom::HitTestRegion::New();
   e_hit_test_region_div->flags = mojom::kHitTestMine;
@@ -546,22 +544,23 @@
   e_hit_test_region_c->surface_id = c_surface_id;
   e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_div));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c));
 
-  auto c_hit_test_data = mojom::HitTestRegionList::New();
-  c_hit_test_data->surface_id = c_surface_id;
-  c_hit_test_data->flags = mojom::kHitTestMine;
-  c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+  auto c_hit_test_region_list = mojom::HitTestRegionList::New();
+  c_hit_test_region_list->flags = mojom::kHitTestMine;
+  c_hit_test_region_list->bounds.SetRect(0, 0, 200, 500);
 
   // Submit in unexpected order.
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(c_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c_surface_id,
+                                      std::move(c_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 2);
 
   // Surfaces added to DisplayFrame in unexpected order.
@@ -629,10 +628,9 @@
   SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
   SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_div = mojom::HitTestRegion::New();
   e_hit_test_region_div->flags = mojom::kHitTestMine;
@@ -644,22 +642,23 @@
   e_hit_test_region_c->surface_id = c_surface_id;
   e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_div));
 
-  auto c_hit_test_data = mojom::HitTestRegionList::New();
-  c_hit_test_data->surface_id = c_surface_id;
-  c_hit_test_data->flags = mojom::kHitTestMine;
-  c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+  auto c_hit_test_region_list = mojom::HitTestRegionList::New();
+  c_hit_test_region_list->flags = mojom::kHitTestMine;
+  c_hit_test_region_list->bounds.SetRect(0, 0, 200, 500);
 
   // Submit in unexpected order.
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(c_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c_surface_id,
+                                      std::move(c_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 2);
 
   // Surfaces added to DisplayFrame in unexpected order.
@@ -729,10 +728,9 @@
   SurfaceId a_surface_id = MakeSurfaceId(kDisplayFrameSink, 3);
   SurfaceId b_surface_id = MakeSurfaceId(kDisplayFrameSink, 4);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_c = mojom::HitTestRegion::New();
   e_hit_test_region_c->flags = mojom::kHitTestChildSurface;
@@ -740,12 +738,11 @@
   e_hit_test_region_c->rect.SetRect(300, 100, 1600, 800);
   e_hit_test_region_c->transform.Translate(200, 100);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c));
 
-  auto c_hit_test_data = mojom::HitTestRegionList::New();
-  c_hit_test_data->surface_id = c_surface_id;
-  c_hit_test_data->flags = mojom::kHitTestIgnore;
-  c_hit_test_data->bounds.SetRect(0, 0, 1600, 800);
+  auto c_hit_test_region_list = mojom::HitTestRegionList::New();
+  c_hit_test_region_list->flags = mojom::kHitTestIgnore;
+  c_hit_test_region_list->bounds.SetRect(0, 0, 1600, 800);
 
   auto c_hit_test_region_a = mojom::HitTestRegion::New();
   c_hit_test_region_a->flags = mojom::kHitTestChildSurface;
@@ -757,33 +754,35 @@
   c_hit_test_region_b->surface_id = b_surface_id;
   c_hit_test_region_b->rect.SetRect(0, 100, 800, 600);
 
-  c_hit_test_data->regions.push_back(std::move(c_hit_test_region_a));
-  c_hit_test_data->regions.push_back(std::move(c_hit_test_region_b));
+  c_hit_test_region_list->regions.push_back(std::move(c_hit_test_region_a));
+  c_hit_test_region_list->regions.push_back(std::move(c_hit_test_region_b));
 
-  auto a_hit_test_data = mojom::HitTestRegionList::New();
-  a_hit_test_data->surface_id = a_surface_id;
-  a_hit_test_data->flags = mojom::kHitTestMine;
-  a_hit_test_data->bounds.SetRect(0, 0, 200, 100);
+  auto a_hit_test_region_list = mojom::HitTestRegionList::New();
+  a_hit_test_region_list->flags = mojom::kHitTestMine;
+  a_hit_test_region_list->bounds.SetRect(0, 0, 200, 100);
 
-  auto b_hit_test_data = mojom::HitTestRegionList::New();
-  b_hit_test_data->surface_id = b_surface_id;
-  b_hit_test_data->flags = mojom::kHitTestMine;
-  b_hit_test_data->bounds.SetRect(0, 100, 800, 600);
+  auto b_hit_test_region_list = mojom::HitTestRegionList::New();
+  b_hit_test_region_list->flags = mojom::kHitTestMine;
+  b_hit_test_region_list->bounds.SetRect(0, 100, 800, 600);
 
   // Submit in unexpected order.
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(c_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c_surface_id,
+                                      std::move(c_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
-  aggregator->SubmitHitTestRegionList(std::move(a_hit_test_data));
+  aggregator->SubmitHitTestRegionList(a_surface_id,
+                                      std::move(a_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 2);
 
-  aggregator->SubmitHitTestRegionList(std::move(b_hit_test_data));
+  aggregator->SubmitHitTestRegionList(b_surface_id,
+                                      std::move(b_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 3);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 4);
 
   // Surfaces added to DisplayFrame in unexpected order.
@@ -871,61 +870,61 @@
   SurfaceId c2_surface_id = MakeSurfaceId(kDisplayFrameSink, 3);
   SurfaceId c3_surface_id = MakeSurfaceId(kDisplayFrameSink, 4);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_c1 = mojom::HitTestRegion::New();
   e_hit_test_region_c1->flags = mojom::kHitTestChildSurface;
   e_hit_test_region_c1->surface_id = c1_surface_id;
   e_hit_test_region_c1->rect.SetRect(100, 100, 700, 700);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c1));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c1));
 
-  auto c1_hit_test_data = mojom::HitTestRegionList::New();
-  c1_hit_test_data->surface_id = c1_surface_id;
-  c1_hit_test_data->flags = mojom::kHitTestMine;
-  c1_hit_test_data->bounds.SetRect(0, 0, 600, 600);
+  auto c1_hit_test_region_list = mojom::HitTestRegionList::New();
+  c1_hit_test_region_list->flags = mojom::kHitTestMine;
+  c1_hit_test_region_list->bounds.SetRect(0, 0, 600, 600);
 
   auto c1_hit_test_region_c2 = mojom::HitTestRegion::New();
   c1_hit_test_region_c2->flags = mojom::kHitTestChildSurface;
   c1_hit_test_region_c2->surface_id = c2_surface_id;
   c1_hit_test_region_c2->rect.SetRect(100, 100, 500, 500);
 
-  c1_hit_test_data->regions.push_back(std::move(c1_hit_test_region_c2));
+  c1_hit_test_region_list->regions.push_back(std::move(c1_hit_test_region_c2));
 
-  auto c2_hit_test_data = mojom::HitTestRegionList::New();
-  c2_hit_test_data->surface_id = c2_surface_id;
-  c2_hit_test_data->flags = mojom::kHitTestMine;
-  c2_hit_test_data->bounds.SetRect(0, 0, 400, 400);
+  auto c2_hit_test_region_list = mojom::HitTestRegionList::New();
+  c2_hit_test_region_list->flags = mojom::kHitTestMine;
+  c2_hit_test_region_list->bounds.SetRect(0, 0, 400, 400);
 
   auto c2_hit_test_region_c3 = mojom::HitTestRegion::New();
   c2_hit_test_region_c3->flags = mojom::kHitTestChildSurface;
   c2_hit_test_region_c3->surface_id = c3_surface_id;
   c2_hit_test_region_c3->rect.SetRect(100, 100, 300, 300);
 
-  c2_hit_test_data->regions.push_back(std::move(c2_hit_test_region_c3));
+  c2_hit_test_region_list->regions.push_back(std::move(c2_hit_test_region_c3));
 
-  auto c3_hit_test_data = mojom::HitTestRegionList::New();
-  c3_hit_test_data->surface_id = c3_surface_id;
-  c3_hit_test_data->flags = mojom::kHitTestMine;
-  c3_hit_test_data->bounds.SetRect(0, 0, 200, 200);
+  auto c3_hit_test_region_list = mojom::HitTestRegionList::New();
+  c3_hit_test_region_list->flags = mojom::kHitTestMine;
+  c3_hit_test_region_list->bounds.SetRect(0, 0, 200, 200);
 
   // Submit in unexpected order.
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(c1_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c1_surface_id,
+                                      std::move(c1_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
-  aggregator->SubmitHitTestRegionList(std::move(c3_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c3_surface_id,
+                                      std::move(c3_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 2);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 3);
 
-  aggregator->SubmitHitTestRegionList(std::move(c2_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c2_surface_id,
+                                      std::move(c2_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 4);
 
   // Surfaces added to DisplayFrame in unexpected order.
@@ -1004,10 +1003,9 @@
   SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
   SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_div = mojom::HitTestRegion::New();
   e_hit_test_region_div->flags = mojom::kHitTestMine;
@@ -1019,19 +1017,19 @@
   e_hit_test_region_c->surface_id = c_surface_id;
   e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_div));
 
-  auto c_hit_test_data = mojom::HitTestRegionList::New();
-  c_hit_test_data->surface_id = c_surface_id;
-  c_hit_test_data->flags = mojom::kHitTestMine;
-  c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+  auto c_hit_test_region_list = mojom::HitTestRegionList::New();
+  c_hit_test_region_list->flags = mojom::kHitTestMine;
+  c_hit_test_region_list->bounds.SetRect(0, 0, 200, 500);
 
   // Submit in unexpected order, but not the child.
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
   // Surfaces added to DisplayFrame in unexpected order.
@@ -1152,10 +1150,9 @@
   SurfaceId e_surface_id = MakeSurfaceId(kDisplayFrameSink, 1);
   SurfaceId c_surface_id = MakeSurfaceId(kDisplayFrameSink, 2);
 
-  auto e_hit_test_data = mojom::HitTestRegionList::New();
-  e_hit_test_data->surface_id = e_surface_id;
-  e_hit_test_data->flags = mojom::kHitTestMine;
-  e_hit_test_data->bounds.SetRect(0, 0, 1024, 768);
+  auto e_hit_test_region_list = mojom::HitTestRegionList::New();
+  e_hit_test_region_list->flags = mojom::kHitTestMine;
+  e_hit_test_region_list->bounds.SetRect(0, 0, 1024, 768);
 
   auto e_hit_test_region_div = mojom::HitTestRegion::New();
   e_hit_test_region_div->flags = mojom::kHitTestMine;
@@ -1167,13 +1164,12 @@
   e_hit_test_region_c->surface_id = c_surface_id;
   e_hit_test_region_c->rect.SetRect(100, 100, 200, 500);
 
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_c));
-  e_hit_test_data->regions.push_back(std::move(e_hit_test_region_div));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_c));
+  e_hit_test_region_list->regions.push_back(std::move(e_hit_test_region_div));
 
-  auto c_hit_test_data = mojom::HitTestRegionList::New();
-  c_hit_test_data->surface_id = c_surface_id;
-  c_hit_test_data->flags = mojom::kHitTestMine;
-  c_hit_test_data->bounds.SetRect(0, 0, 200, 500);
+  auto c_hit_test_region_list = mojom::HitTestRegionList::New();
+  c_hit_test_region_list->flags = mojom::kHitTestMine;
+  c_hit_test_region_list->bounds.SetRect(0, 0, 200, 500);
 
   EXPECT_EQ(aggregator->GetActiveRegionCount(), 0);
 
@@ -1181,10 +1177,12 @@
 
   EXPECT_EQ(aggregator->GetPendingCount(), 0);
 
-  aggregator->SubmitHitTestRegionList(std::move(c_hit_test_data));
+  aggregator->SubmitHitTestRegionList(c_surface_id,
+                                      std::move(c_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 1);
 
-  aggregator->SubmitHitTestRegionList(std::move(e_hit_test_data));
+  aggregator->SubmitHitTestRegionList(e_surface_id,
+                                      std::move(e_hit_test_region_list));
   EXPECT_EQ(aggregator->GetPendingCount(), 2);
 
   EXPECT_EQ(aggregator->GetActiveRegionCount(), 0);
diff --git a/components/viz/test/data/background_filter_rotated_sw.png b/components/viz/test/data/background_filter_rotated_sw.png
index 94bbb8e6..5b9395f 100644
--- a/components/viz/test/data/background_filter_rotated_sw.png
+++ b/components/viz/test/data/background_filter_rotated_sw.png
Binary files differ
diff --git a/components/viz/test/data/rotated_drop_shadow_filter_sw.png b/components/viz/test/data/rotated_drop_shadow_filter_sw.png
index d7c0cb4..0dd39e27 100644
--- a/components/viz/test/data/rotated_drop_shadow_filter_sw.png
+++ b/components/viz/test/data/rotated_drop_shadow_filter_sw.png
Binary files differ
diff --git a/components/viz/test/data/rotated_filter_sw.png b/components/viz/test/data/rotated_filter_sw.png
index 12b12f0..02bf842 100644
--- a/components/viz/test/data/rotated_filter_sw.png
+++ b/components/viz/test/data/rotated_filter_sw.png
Binary files differ
diff --git a/components/wallpaper/wallpaper_manager_base.cc b/components/wallpaper/wallpaper_manager_base.cc
index 225ffe1..4b33021 100644
--- a/components/wallpaper/wallpaper_manager_base.cc
+++ b/components/wallpaper/wallpaper_manager_base.cc
@@ -450,7 +450,11 @@
   GetUserWallpaperInfo(account_id, &info);
   info.type = DEFAULT;
   SetUserWallpaperInfo(account_id, info, true /* is_persistent */);
-  SetDefaultWallpaperNow(account_id);
+
+  // If we're at the login screen, do not change the wallpaper but defer it
+  // until the user logs in to the system.
+  if (user_manager::UserManager::Get()->IsUserLoggedIn())
+    SetDefaultWallpaperNow(account_id);
 }
 
 // static
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 22b9286..de15ce1 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -119,7 +119,8 @@
     "//services/file:lib",
     "//services/file/public/interfaces",
     "//services/metrics/public/cpp:metrics_cpp",
-    "//services/network/public/interfaces:interfaces",
+    "//services/network/public/cpp",
+    "//services/network/public/interfaces",
     "//services/resource_coordinator:lib",
     "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
     "//services/service_manager",
@@ -187,6 +188,8 @@
   ]
 
   sources = [
+    "$target_gen_dir/devtools/protocol/browser.cc",
+    "$target_gen_dir/devtools/protocol/browser.h",
     "$target_gen_dir/devtools/protocol/dom.cc",
     "$target_gen_dir/devtools/protocol/dom.h",
     "$target_gen_dir/devtools/protocol/emulation.cc",
@@ -546,6 +549,8 @@
     "devtools/devtools_url_request_interceptor.h",
     "devtools/forwarding_agent_host.cc",
     "devtools/forwarding_agent_host.h",
+    "devtools/protocol/browser_handler.cc",
+    "devtools/protocol/browser_handler.h",
     "devtools/protocol/devtools_domain_handler.cc",
     "devtools/protocol/devtools_domain_handler.h",
     "devtools/protocol/dom_handler.cc",
@@ -663,6 +668,8 @@
     "download/download_request_handle.h",
     "download/download_resource_handler.cc",
     "download/download_resource_handler.h",
+    "download/download_response_handler.cc",
+    "download/download_response_handler.h",
     "download/download_stats.cc",
     "download/download_stats.h",
     "download/download_task_runner.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 378c891..9ffada4 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -126,13 +126,13 @@
   "+third_party/WebKit/public/platform/reporting.mojom.h",
   "+third_party/WebKit/public/public_features.h",
   "+third_party/WebKit/public/web/WebAXEnums.h",
-  "+third_party/WebKit/public/web/WebCompositionUnderline.h",
   "+third_party/WebKit/public/web/WebConsoleMessage.h",
   "+third_party/WebKit/public/web/WebContextMenuData.h",
   "+third_party/WebKit/public/web/WebDeviceEmulationParams.h",
   "+third_party/WebKit/public/web/WebDragStatus.h",
   "+third_party/WebKit/public/web/WebFindOptions.h",
   "+third_party/WebKit/public/web/WebFrameSerializerCacheControlPolicy.h",
+  "+third_party/WebKit/public/web/WebImeTextSpan.h",
   "+third_party/WebKit/public/web/WebMediaPlayerAction.h",
   "+third_party/WebKit/public/web/WebPluginAction.h",
   "+third_party/WebKit/public/web/WebPopupType.h",
diff --git a/content/browser/android/ime_adapter_android.cc b/content/browser/android/ime_adapter_android.cc
index d23bbe9..275e2468 100644
--- a/content/browser/android/ime_adapter_android.cc
+++ b/content/browser/android/ime_adapter_android.cc
@@ -27,7 +27,7 @@
 #include "jni/ImeAdapter_jni.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 #include "third_party/WebKit/public/platform/WebTextInputType.h"
-#include "ui/base/ime/composition_underline.h"
+#include "ui/base/ime/ime_text_span.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertJavaStringToUTF16;
@@ -73,37 +73,37 @@
 }
 
 // Callback from Java to convert BackgroundColorSpan data to a
-// ui::CompositionUnderline instance, and append it to |underlines_ptr|.
+// ui::ImeTextSpan instance, and append it to |ime_text_spans_ptr|.
 void AppendBackgroundColorSpan(JNIEnv*,
                                const JavaParamRef<jclass>&,
-                               jlong underlines_ptr,
+                               jlong ime_text_spans_ptr,
                                jint start,
                                jint end,
                                jint background_color) {
   DCHECK_GE(start, 0);
   DCHECK_GE(end, 0);
   // Do not check |background_color|.
-  std::vector<ui::CompositionUnderline>* underlines =
-      reinterpret_cast<std::vector<ui::CompositionUnderline>*>(underlines_ptr);
-  underlines->push_back(ui::CompositionUnderline(
+  std::vector<ui::ImeTextSpan>* ime_text_spans =
+      reinterpret_cast<std::vector<ui::ImeTextSpan>*>(ime_text_spans_ptr);
+  ime_text_spans->push_back(ui::ImeTextSpan(
       static_cast<unsigned>(start), static_cast<unsigned>(end),
       SK_ColorTRANSPARENT, false, static_cast<unsigned>(background_color)));
 }
 
 // Callback from Java to convert UnderlineSpan data to a
-// ui::CompositionUnderline instance, and append it to |underlines_ptr|.
+// ui::ImeTextSpan instance, and append it to |ime_text_spans_ptr|.
 void AppendUnderlineSpan(JNIEnv*,
                          const JavaParamRef<jclass>&,
-                         jlong underlines_ptr,
+                         jlong ime_text_spans_ptr,
                          jint start,
                          jint end) {
   DCHECK_GE(start, 0);
   DCHECK_GE(end, 0);
-  std::vector<ui::CompositionUnderline>* underlines =
-      reinterpret_cast<std::vector<ui::CompositionUnderline>*>(underlines_ptr);
-  underlines->push_back(ui::CompositionUnderline(
-      static_cast<unsigned>(start), static_cast<unsigned>(end), SK_ColorBLACK,
-      false, SK_ColorTRANSPARENT));
+  std::vector<ui::ImeTextSpan>* ime_text_spans =
+      reinterpret_cast<std::vector<ui::ImeTextSpan>*>(ime_text_spans_ptr);
+  ime_text_spans->push_back(
+      ui::ImeTextSpan(static_cast<unsigned>(start), static_cast<unsigned>(end),
+                      SK_ColorBLACK, false, SK_ColorTRANSPARENT));
 }
 
 ImeAdapterAndroid::ImeAdapterAndroid(JNIEnv* env,
@@ -210,13 +210,13 @@
 
   base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
 
-  std::vector<ui::CompositionUnderline> underlines =
-      GetUnderlinesFromSpans(env, obj, text, text16);
+  std::vector<ui::ImeTextSpan> ime_text_spans =
+      GetImeTextSpansFromJava(env, obj, text, text16);
 
   // Default to plain underline if we didn't find any span that we care about.
-  if (underlines.empty()) {
-    underlines.push_back(ui::CompositionUnderline(
-        0, text16.length(), SK_ColorBLACK, false, SK_ColorTRANSPARENT));
+  if (ime_text_spans.empty()) {
+    ime_text_spans.push_back(ui::ImeTextSpan(0, text16.length(), SK_ColorBLACK,
+                                             false, SK_ColorTRANSPARENT));
   }
 
   // relative_cursor_pos is as described in the Android API for
@@ -225,7 +225,7 @@
   if (relative_cursor_pos > 0)
     relative_cursor_pos = text16.length() + relative_cursor_pos - 1;
 
-  rwhi->ImeSetComposition(text16, underlines, gfx::Range::InvalidRange(),
+  rwhi->ImeSetComposition(text16, ime_text_spans, gfx::Range::InvalidRange(),
                           relative_cursor_pos, relative_cursor_pos);
 }
 
@@ -240,8 +240,8 @@
 
   base::string16 text16 = ConvertJavaStringToUTF16(env, text_str);
 
-  std::vector<ui::CompositionUnderline> underlines =
-      GetUnderlinesFromSpans(env, obj, text, text16);
+  std::vector<ui::ImeTextSpan> ime_text_spans =
+      GetImeTextSpansFromJava(env, obj, text, text16);
 
   // relative_cursor_pos is as described in the Android API for
   // InputConnection#commitText, whereas the parameters for
@@ -251,7 +251,7 @@
   else
     relative_cursor_pos -= text16.length();
 
-  rwhi->ImeCommitText(text16, underlines, gfx::Range::InvalidRange(),
+  rwhi->ImeCommitText(text16, ime_text_spans, gfx::Range::InvalidRange(),
                       relative_cursor_pos);
 }
 
@@ -336,12 +336,12 @@
   if (!rfh)
     return;
 
-  std::vector<ui::CompositionUnderline> underlines;
-  underlines.push_back(ui::CompositionUnderline(0, end - start, SK_ColorBLACK,
-                                                false, SK_ColorTRANSPARENT));
+  std::vector<ui::ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ui::ImeTextSpan(0, end - start, SK_ColorBLACK, false,
+                                           SK_ColorTRANSPARENT));
 
   rfh->GetFrameInputHandler()->SetCompositionFromExistingText(start, end,
-                                                              underlines);
+                                                              ime_text_spans);
 }
 
 void ImeAdapterAndroid::DeleteSurroundingText(JNIEnv*,
@@ -412,22 +412,22 @@
   return nullptr;
 }
 
-std::vector<ui::CompositionUnderline> ImeAdapterAndroid::GetUnderlinesFromSpans(
+std::vector<ui::ImeTextSpan> ImeAdapterAndroid::GetImeTextSpansFromJava(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
     const base::android::JavaParamRef<jobject>& text,
     const base::string16& text16) {
-  std::vector<ui::CompositionUnderline> underlines;
+  std::vector<ui::ImeTextSpan> ime_text_spans;
   // Iterate over spans in |text|, dispatch those that we care about (e.g.,
   // BackgroundColorSpan) to a matching callback (e.g.,
-  // AppendBackgroundColorSpan()), and populate |underlines|.
-  Java_ImeAdapter_populateUnderlinesFromSpans(
-      env, obj, text, reinterpret_cast<jlong>(&underlines));
+  // AppendBackgroundColorSpan()), and populate |ime_text_spans|.
+  Java_ImeAdapter_populateImeTextSpansFromJava(
+      env, obj, text, reinterpret_cast<jlong>(&ime_text_spans));
 
   // Sort spans by |.startOffset|.
-  std::sort(underlines.begin(), underlines.end());
+  std::sort(ime_text_spans.begin(), ime_text_spans.end());
 
-  return underlines;
+  return ime_text_spans;
 }
 
 }  // namespace content
diff --git a/content/browser/android/ime_adapter_android.h b/content/browser/android/ime_adapter_android.h
index 06bd658..9e591fe 100644
--- a/content/browser/android/ime_adapter_android.h
+++ b/content/browser/android/ime_adapter_android.h
@@ -16,7 +16,7 @@
 
 namespace ui {
 
-struct CompositionUnderline;
+struct ImeTextSpan;
 
 }  // namespace ui
 
@@ -113,7 +113,7 @@
  private:
   RenderWidgetHostImpl* GetFocusedWidget();
   RenderFrameHost* GetFocusedFrame();
-  std::vector<ui::CompositionUnderline> GetUnderlinesFromSpans(
+  std::vector<ui::ImeTextSpan> GetImeTextSpansFromJava(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jobject>& text,
diff --git a/content/browser/appcache/appcache_update_url_loader_request.cc b/content/browser/appcache/appcache_update_url_loader_request.cc
index 590bd6f..3d7d8d2 100644
--- a/content/browser/appcache/appcache_update_url_loader_request.cc
+++ b/content/browser/appcache/appcache_update_url_loader_request.cc
@@ -40,7 +40,7 @@
 }
 
 std::string AppCacheUpdateJob::UpdateURLLoaderRequest::GetMimeType() const {
-  return response_->mime_type;
+  return response_.mime_type;
 }
 
 void AppCacheUpdateJob::UpdateURLLoaderRequest::SetSiteForCookies(
@@ -55,12 +55,12 @@
 
 net::HttpResponseHeaders*
 AppCacheUpdateJob::UpdateURLLoaderRequest::GetResponseHeaders() const {
-  return response_->headers.get();
+  return response_.headers.get();
 }
 
 int AppCacheUpdateJob::UpdateURLLoaderRequest::GetResponseCode() const {
-  if (response_->headers)
-    return response_->headers->response_code();
+  if (response_.headers)
+    return response_.headers->response_code();
   return 0;
 }
 
@@ -82,7 +82,7 @@
   url_loader_ = nullptr;
   handle_watcher_.Cancel();
   handle_.reset();
-  response_.reset(nullptr);
+  response_ = ResourceResponseHead();
   http_response_info_.reset(nullptr);
   read_requested_ = false;
   return 0;
@@ -92,8 +92,7 @@
     const ResourceResponseHead& response_head,
     const base::Optional<net::SSLInfo>& ssl_info,
     mojom::DownloadedTempFilePtr downloaded_file) {
-  response_.reset(new ResourceResponseHead());
-  *response_ = response_head;
+  response_ = response_head;
 
   // TODO(ananta/michaeln)
   // Populate other fields in the HttpResponseInfo class. It would be good to
@@ -116,7 +115,7 @@
 void AppCacheUpdateJob::UpdateURLLoaderRequest::OnReceiveRedirect(
     const net::RedirectInfo& redirect_info,
     const ResourceResponseHead& response_head) {
-  *response_ = response_head;
+  response_ = response_head;
   fetcher_->OnReceivedRedirect(redirect_info);
 }
 
@@ -191,8 +190,8 @@
   }
 
   uint32_t available = 0;
-  MojoResult result =
-      MojoToNetPendingBuffer::BeginRead(&handle_, &pending_read_, &available);
+  MojoResult result = network::MojoToNetPendingBuffer::BeginRead(
+      &handle_, &pending_read_, &available);
   DCHECK_NE(result, MOJO_RESULT_BUSY);
 
   if (result == MOJO_RESULT_SHOULD_WAIT) {
@@ -215,8 +214,8 @@
 
   int bytes_to_be_read = std::min<int>(buffer_size_, available);
 
-  scoped_refptr<MojoToNetIOBuffer> buffer =
-      new MojoToNetIOBuffer(pending_read_.get(), bytes_to_be_read);
+  auto buffer = base::MakeRefCounted<network::MojoToNetIOBuffer>(
+      pending_read_.get(), bytes_to_be_read);
 
   fetcher_->OnReadCompleted(buffer.get(), bytes_to_be_read);
 }
diff --git a/content/browser/appcache/appcache_update_url_loader_request.h b/content/browser/appcache/appcache_update_url_loader_request.h
index 185f0ff..ebe478c 100644
--- a/content/browser/appcache/appcache_update_url_loader_request.h
+++ b/content/browser/appcache/appcache_update_url_loader_request.h
@@ -11,12 +11,13 @@
 
 #include "base/macros.h"
 #include "content/browser/appcache/appcache_update_request_base.h"
-#include "content/common/net_adapters.h"
 #include "content/public/common/resource_request.h"
+#include "content/public/common/resource_response.h"
 #include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/base/io_buffer.h"
+#include "services/network/public/cpp/net_adapters.h"
 
 namespace net {
 class HttpResponseInfo;
@@ -26,7 +27,6 @@
 
 class URLLoaderFactoryGetter;
 struct ResourceRequest;
-struct ResourceResponseHead;
 
 // URLLoaderClient subclass for the UpdateRequestBase class. Provides
 // functionality to update the AppCache using functionality provided by the
@@ -91,7 +91,7 @@
   scoped_refptr<URLLoaderFactoryGetter> loader_factory_getter_;
 
   ResourceRequest request_;
-  std::unique_ptr<ResourceResponseHead> response_;
+  ResourceResponseHead response_;
   ResourceRequestCompletionStatus response_status_;
   // Response details.
   std::unique_ptr<net::HttpResponseInfo> http_response_info_;
@@ -114,7 +114,7 @@
   // Please look at the StartReading() function for details.
   bool read_requested_;
   // Adapter for transferring data from a mojo data pipe to net.
-  scoped_refptr<MojoToNetPendingBuffer> pending_read_;
+  scoped_refptr<network::MojoToNetPendingBuffer> pending_read_;
 
   DISALLOW_COPY_AND_ASSIGN(UpdateURLLoaderRequest);
 };
diff --git a/content/browser/appcache/appcache_url_loader_job.cc b/content/browser/appcache/appcache_url_loader_job.cc
index 5679b7a..0e578654 100644
--- a/content/browser/appcache/appcache_url_loader_job.cc
+++ b/content/browser/appcache/appcache_url_loader_job.cc
@@ -9,9 +9,9 @@
 #include "content/browser/appcache/appcache_subresource_url_factory.h"
 #include "content/browser/appcache/appcache_url_loader_request.h"
 #include "content/browser/url_loader_factory_getter.h"
-#include "content/common/net_adapters.h"
 #include "content/public/common/resource_type.h"
 #include "net/http/http_status_code.h"
+#include "services/network/public/cpp/net_adapters.h"
 
 namespace content {
 
@@ -393,7 +393,7 @@
 
   uint32_t num_bytes;
   // TODO: we should use the abstractions in MojoAsyncResourceHandler.
-  MojoResult result = NetToMojoPendingBuffer::BeginWrite(
+  MojoResult result = network::NetToMojoPendingBuffer::BeginWrite(
       &response_body_stream_, &pending_write_, &num_bytes);
   if (result == MOJO_RESULT_SHOULD_WAIT) {
     // The pipe is full. We need to wait for it to have more space.
@@ -410,8 +410,8 @@
   }
 
   CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
-  scoped_refptr<NetToMojoIOBuffer> buffer =
-      new NetToMojoIOBuffer(pending_write_.get());
+  auto buffer =
+      base::MakeRefCounted<network::NetToMojoIOBuffer>(pending_write_.get());
 
   reader_->ReadData(
       buffer.get(), info_->response_data_size(),
diff --git a/content/browser/appcache/appcache_url_loader_job.h b/content/browser/appcache/appcache_url_loader_job.h
index a1328c9..d696045 100644
--- a/content/browser/appcache/appcache_url_loader_job.h
+++ b/content/browser/appcache/appcache_url_loader_job.h
@@ -22,11 +22,14 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 
+namespace network {
+class NetToMojoPendingBuffer;
+}
+
 namespace content {
 
 class AppCacheRequest;
 class AppCacheURLLoaderRequest;
-class NetToMojoPendingBuffer;
 class URLLoaderFactoryGetter;
 
 // Holds information about the subresource load request like the routing id,
@@ -183,7 +186,7 @@
   // mojo data pipe entities.
   mojo::ScopedDataPipeProducerHandle response_body_stream_;
 
-  scoped_refptr<NetToMojoPendingBuffer> pending_write_;
+  scoped_refptr<network::NetToMojoPendingBuffer> pending_write_;
 
   mojo::SimpleWatcher writable_handle_watcher_;
 
diff --git a/content/browser/blob_storage/blob_url_loader_factory.cc b/content/browser/blob_storage/blob_url_loader_factory.cc
index a9406a33..85afc946 100644
--- a/content/browser/blob_storage/blob_url_loader_factory.cc
+++ b/content/browser/blob_storage/blob_url_loader_factory.cc
@@ -5,14 +5,15 @@
 #include "content/browser/blob_storage/blob_url_loader_factory.h"
 
 #include <stddef.h>
+#include <utility>
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/common/net_adapters.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/url_loader.mojom.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
@@ -26,6 +27,7 @@
 #include "storage/browser/blob/blob_reader.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/blob/blob_url_request_job.h"
+#include "storage/browser/blob/mojo_blob_reader.h"
 #include "storage/browser/fileapi/file_system_context.h"
 
 namespace content {
@@ -33,10 +35,12 @@
 namespace {
 constexpr size_t kDefaultAllocationSize = 512 * 1024;
 
-// This class handles a request for a blob:// url. It self-destructs (see
-// DeleteIfNeeded) when it has finished responding.
+// This class handles a request for a blob:// url. It self-destructs (directly,
+// or after passing ownership to storage::MojoBlobReader at the end of the Start
+// method) when it has finished responding.
 // Note: some of this code is duplicated from storage::BlobURLRequestJob.
-class BlobURLLoader : public mojom::URLLoader {
+class BlobURLLoader : public storage::MojoBlobReader::Delegate,
+                      public mojom::URLLoader {
  public:
   BlobURLLoader(mojom::URLLoaderRequest url_loader_request,
                 const ResourceRequest& request,
@@ -46,37 +50,29 @@
       : binding_(this, std::move(url_loader_request)),
         client_(std::move(client)),
         blob_handle_(std::move(blob_handle)),
-        writable_handle_watcher_(FROM_HERE,
-                                 mojo::SimpleWatcher::ArmingPolicy::MANUAL),
-        peer_closed_handle_watcher_(FROM_HERE,
-                                    mojo::SimpleWatcher::ArmingPolicy::MANUAL),
         weak_factory_(this) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
     // PostTask since it might destruct.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(&BlobURLLoader::Start, weak_factory_.GetWeakPtr(), request,
                    make_scoped_refptr(file_system_context)));
   }
 
+ private:
   void Start(const ResourceRequest& request,
              scoped_refptr<storage::FileSystemContext> file_system_context) {
     if (!blob_handle_) {
-      NotifyCompleted(net::ERR_FILE_NOT_FOUND);
+      OnComplete(net::ERR_FILE_NOT_FOUND, 0);
+      delete this;
       return;
     }
 
-    blob_reader_ = blob_handle_->CreateReader(file_system_context.get());
-
     // We only support GET request per the spec.
     if (request.method != "GET") {
-      NotifyCompleted(net::ERR_METHOD_NOT_SUPPORTED);
-      return;
-    }
-
-    if (blob_reader_->net_error()) {
-      NotifyCompleted(blob_reader_->net_error());
+      OnComplete(net::ERR_METHOD_NOT_SUPPORTED, 0);
+      delete this;
       return;
     }
 
@@ -95,112 +91,65 @@
           // We don't support multiple range requests in one single URL request,
           // because we need to do multipart encoding here.
           // TODO(jianli): Support multipart byte range requests.
-          NotifyCompleted(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+          OnComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, 0);
+          delete this;
+          return;
         }
       }
     }
 
-    storage::BlobReader::Status size_status =
-        blob_reader_->CalculateSize(base::Bind(&BlobURLLoader::DidCalculateSize,
-                                               weak_factory_.GetWeakPtr()));
-    switch (size_status) {
-      case storage::BlobReader::Status::NET_ERROR:
-        NotifyCompleted(blob_reader_->net_error());
-        return;
-      case storage::BlobReader::Status::IO_PENDING:
-        return;
-      case storage::BlobReader::Status::DONE:
-        DidCalculateSize(net::OK);
-        return;
-    }
-
-    NOTREACHED();
+    storage::MojoBlobReader::Create(file_system_context.get(),
+                                    blob_handle_.get(), byte_range_,
+                                    base::WrapUnique(this));
   }
 
-  ~BlobURLLoader() override {}
-
- private:
   // mojom::URLLoader implementation:
   void FollowRedirect() override { NOTREACHED(); }
 
   void SetPriority(net::RequestPriority priority,
                    int32_t intra_priority_value) override {}
 
-  // Notifies the client that the request completed. Takes care of deleting this
-  // object now if possible (i.e. no outstanding data pipe), otherwise this
-  // object will be deleted when the data pipe is closed.
-  void NotifyCompleted(int error_code) {
-    if (error_code != net::OK && !sent_headers_) {
-      net::HttpStatusCode status_code =
-          storage::BlobURLRequestJob::NetErrorToHttpStatusCode(error_code);
-      ResourceResponseHead response;
-      response.headers = storage::BlobURLRequestJob::GenerateHeaders(
-          status_code, nullptr, nullptr, nullptr, nullptr);
-      client_->OnReceiveResponse(response, base::nullopt, nullptr);
-    }
-    ResourceRequestCompletionStatus request_complete_data;
-    // TODO(kinuko): We should probably set the error_code here,
-    // while it makes existing tests fail. crbug.com/732750
-    request_complete_data.completion_time = base::TimeTicks::Now();
-    request_complete_data.encoded_body_length = total_written_bytes_;
-    request_complete_data.decoded_body_length = total_written_bytes_;
-    client_->OnComplete(request_complete_data);
-
-    DeleteIfNeeded();
+  // storage::MojoBlobReader::Delegate implementation:
+  mojo::ScopedDataPipeProducerHandle PassDataPipe() override {
+    mojo::DataPipe data_pipe(kDefaultAllocationSize);
+    response_body_consumer_handle_ = std::move(data_pipe.consumer_handle);
+    return std::move(data_pipe.producer_handle);
   }
 
-  void DidCalculateSize(int result) {
-    if (result != net::OK) {
-      NotifyCompleted(result);
-      return;
-    }
-
-    // Apply the range requirement.
-    if (!byte_range_.ComputeBounds(blob_reader_->total_size())) {
-      NotifyCompleted(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
-      return;
-    }
-
-    DCHECK_LE(byte_range_.first_byte_position(),
-              byte_range_.last_byte_position() + 1);
-    uint64_t length =
-        base::checked_cast<uint64_t>(byte_range_.last_byte_position() -
-                                     byte_range_.first_byte_position() + 1);
-
-    if (byte_range_set_)
-      blob_reader_->SetReadRange(byte_range_.first_byte_position(), length);
+  RequestSideData DidCalculateSize(uint64_t total_size,
+                                   uint64_t content_size) override {
+    total_size_ = total_size;
+    bool result = byte_range_.ComputeBounds(total_size);
+    DCHECK(result);
 
     net::HttpStatusCode status_code = net::HTTP_OK;
     if (byte_range_set_ && byte_range_.IsValid()) {
       status_code = net::HTTP_PARTIAL_CONTENT;
     } else {
+      DCHECK_EQ(total_size, content_size);
       // TODO(horo): When the requester doesn't need the side data
       // (ex:FileReader) we should skip reading the side data.
-      if (blob_reader_->has_side_data() &&
-          blob_reader_->ReadSideData(base::Bind(&BlobURLLoader::DidReadMetadata,
-                                                weak_factory_.GetWeakPtr())) ==
-              storage::BlobReader::Status::IO_PENDING) {
-        return;
-      }
+      return REQUEST_SIDE_DATA;
     }
 
-    HeadersCompleted(status_code);
+    HeadersCompleted(status_code, content_size, nullptr);
+    return DONT_REQUEST_SIDE_DATA;
   }
 
-  void DidReadMetadata(storage::BlobReader::Status result) {
-    if (result != storage::BlobReader::Status::DONE) {
-      NotifyCompleted(blob_reader_->net_error());
-      return;
-    }
-    HeadersCompleted(net::HTTP_OK);
+  void DidReadSideData(net::IOBufferWithSize* data) override {
+    HeadersCompleted(net::HTTP_OK, total_size_, data);
   }
 
-  void HeadersCompleted(net::HttpStatusCode status_code) {
+  void HeadersCompleted(net::HttpStatusCode status_code,
+                        uint64_t content_size,
+                        net::IOBufferWithSize* metadata) {
     ResourceResponseHead response;
     response.content_length = 0;
+    if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT)
+      response.content_length = content_size;
     response.headers = storage::BlobURLRequestJob::GenerateHeaders(
-        status_code, blob_handle_.get(), blob_reader_.get(), &byte_range_,
-        &response.content_length);
+        status_code, blob_handle_.get(), &byte_range_, total_size_,
+        content_size);
 
     std::string mime_type;
     response.headers->GetMimeType(&mime_type);
@@ -214,112 +163,38 @@
     client_->OnReceiveResponse(response, base::nullopt, nullptr);
     sent_headers_ = true;
 
-    net::IOBufferWithSize* metadata = blob_reader_->side_data();
     if (metadata) {
       const uint8_t* data = reinterpret_cast<const uint8_t*>(metadata->data());
       client_->OnReceiveCachedMetadata(
           std::vector<uint8_t>(data, data + metadata->size()));
     }
-
-    mojo::DataPipe data_pipe(kDefaultAllocationSize);
-    response_body_stream_ = std::move(data_pipe.producer_handle);
-    response_body_consumer_handle_ = std::move(data_pipe.consumer_handle);
-    peer_closed_handle_watcher_.Watch(
-        response_body_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
-        base::Bind(&BlobURLLoader::OnResponseBodyStreamClosed,
-                   base::Unretained(this)));
-    peer_closed_handle_watcher_.ArmOrNotify();
-
-    writable_handle_watcher_.Watch(
-        response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
-        base::Bind(&BlobURLLoader::OnResponseBodyStreamReady,
-                   base::Unretained(this)));
-
-    // Start reading...
-    ReadMore();
   }
 
-  void ReadMore() {
-    DCHECK(!pending_write_.get());
-
-    uint32_t num_bytes;
-    // TODO: we should use the abstractions in MojoAsyncResourceHandler.
-    MojoResult result = NetToMojoPendingBuffer::BeginWrite(
-        &response_body_stream_, &pending_write_, &num_bytes);
-    if (result == MOJO_RESULT_SHOULD_WAIT) {
-      // The pipe is full. We need to wait for it to have more space.
-      writable_handle_watcher_.ArmOrNotify();
-      return;
-    } else if (result != MOJO_RESULT_OK) {
-      // The response body stream is in a bad state. Bail.
-      writable_handle_watcher_.Cancel();
-      response_body_stream_.reset();
-      NotifyCompleted(net::ERR_UNEXPECTED);
-      return;
-    }
-
-    CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
-    scoped_refptr<net::IOBuffer> buf(
-        new NetToMojoIOBuffer(pending_write_.get()));
-    int bytes_read;
-    storage::BlobReader::Status read_status = blob_reader_->Read(
-        buf.get(), static_cast<int>(num_bytes), &bytes_read,
-        base::Bind(&BlobURLLoader::DidRead, weak_factory_.GetWeakPtr(), false));
-    switch (read_status) {
-      case storage::BlobReader::Status::NET_ERROR:
-        NotifyCompleted(blob_reader_->net_error());
-        return;
-      case storage::BlobReader::Status::IO_PENDING:
-        // Wait for DidRead.
-        return;
-      case storage::BlobReader::Status::DONE:
-        if (bytes_read > 0) {
-          DidRead(true, bytes_read);
-        } else {
-          writable_handle_watcher_.Cancel();
-          pending_write_->Complete(0);
-          pending_write_ = nullptr;  // This closes the data pipe.
-          NotifyCompleted(net::OK);
-          return;
-        }
-    }
-  }
-
-  void DidRead(bool completed_synchronously, int num_bytes) {
+  void DidRead(int num_bytes) override {
     if (response_body_consumer_handle_.is_valid()) {
       // Send the data pipe on the first OnReadCompleted call.
       client_->OnStartLoadingResponseBody(
           std::move(response_body_consumer_handle_));
     }
-    response_body_stream_ = pending_write_->Complete(num_bytes);
-    total_written_bytes_ += num_bytes;
-    pending_write_ = nullptr;
-    if (completed_synchronously) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::Bind(&BlobURLLoader::ReadMore, weak_factory_.GetWeakPtr()));
-    } else {
-      ReadMore();
+  }
+
+  void OnComplete(net::Error error_code,
+                  uint64_t total_written_bytes) override {
+    if (error_code != net::OK && !sent_headers_) {
+      net::HttpStatusCode status_code =
+          storage::BlobURLRequestJob::NetErrorToHttpStatusCode(error_code);
+      ResourceResponseHead response;
+      response.headers = storage::BlobURLRequestJob::GenerateHeaders(
+          status_code, nullptr, nullptr, 0, 0);
+      client_->OnReceiveResponse(response, base::nullopt, nullptr);
     }
-  }
-
-  void OnResponseBodyStreamClosed(MojoResult result) {
-    response_body_stream_.reset();
-    pending_write_ = nullptr;
-    DeleteIfNeeded();
-  }
-
-  void OnResponseBodyStreamReady(MojoResult result) {
-    // TODO: Handle a bad |result| value.
-    DCHECK_EQ(result, MOJO_RESULT_OK);
-    ReadMore();
-  }
-
-  void DeleteIfNeeded() {
-    bool has_data_pipe =
-        pending_write_.get() || response_body_stream_.is_valid();
-    if (!has_data_pipe)
-      delete this;
+    ResourceRequestCompletionStatus request_complete_data;
+    // TODO(kinuko): We should probably set the error_code here,
+    // while it makes existing tests fail. crbug.com/732750
+    request_complete_data.completion_time = base::TimeTicks::Now();
+    request_complete_data.encoded_body_length = total_written_bytes;
+    request_complete_data.decoded_body_length = total_written_bytes;
+    client_->OnComplete(request_complete_data);
   }
 
   mojo::Binding<mojom::URLLoader> binding_;
@@ -328,18 +203,11 @@
   bool byte_range_set_ = false;
   net::HttpByteRange byte_range_;
 
+  uint64_t total_size_ = 0;
   bool sent_headers_ = false;
 
   std::unique_ptr<storage::BlobDataHandle> blob_handle_;
-  std::unique_ptr<storage::BlobReader> blob_reader_;
-
-  // TODO(jam): share with URLLoaderImpl
-  mojo::ScopedDataPipeProducerHandle response_body_stream_;
   mojo::ScopedDataPipeConsumerHandle response_body_consumer_handle_;
-  scoped_refptr<NetToMojoPendingBuffer> pending_write_;
-  mojo::SimpleWatcher writable_handle_watcher_;
-  mojo::SimpleWatcher peer_closed_handle_watcher_;
-  int64_t total_written_bytes_ = 0;
 
   base::WeakPtrFactory<BlobURLLoader> weak_factory_;
 
diff --git a/content/browser/browser_associated_interface_unittest.cc b/content/browser/browser_associated_interface_unittest.cc
index c18a79d..8b315e3 100644
--- a/content/browser/browser_associated_interface_unittest.cc
+++ b/content/browser/browser_associated_interface_unittest.cc
@@ -95,7 +95,7 @@
   void OnFilterRemoved() override {
     // Check that the bindings are cleared by
     // BrowserAssociatedInterface::ClearBindings() callbacks.
-    EXPECT_FALSE(internal_state_->bindings_.get());
+    EXPECT_FALSE(internal_state_->bindings_.has_value());
   }
 
   // mojom::BrowserAssociatedInterfaceTestDriver:
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 0eed74e..e44dd5ab 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -62,15 +62,16 @@
 
 namespace {
 
-std::vector<ui::CompositionUnderline> ConvertToUiUnderline(
-    const std::vector<blink::WebCompositionUnderline>& underlines) {
-  std::vector<ui::CompositionUnderline> ui_underlines;
-  for (const auto& underline : underlines) {
-    ui_underlines.emplace_back(ui::CompositionUnderline(
-        underline.start_offset, underline.end_offset, underline.color,
-        underline.thick, underline.background_color));
+std::vector<ui::ImeTextSpan> ConvertToUiImeTextSpan(
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans) {
+  std::vector<ui::ImeTextSpan> ui_ime_text_spans;
+  for (const auto& ime_text_span : ime_text_spans) {
+    ui_ime_text_spans.emplace_back(
+        ui::ImeTextSpan(ime_text_span.start_offset, ime_text_span.end_offset,
+                        ime_text_span.color, ime_text_span.thick,
+                        ime_text_span.background_color));
   }
-  return ui_underlines;
+  return ui_ime_text_spans;
 }
 };  // namespace
 
@@ -929,29 +930,30 @@
 void BrowserPluginGuest::OnImeSetComposition(
     int browser_plugin_instance_id,
     const BrowserPluginHostMsg_SetComposition_Params& params) {
-  std::vector<ui::CompositionUnderline> ui_underlines =
-      ConvertToUiUnderline(params.underlines);
+  std::vector<ui::ImeTextSpan> ui_ime_text_spans =
+      ConvertToUiImeTextSpan(params.ime_text_spans);
   GetWebContents()
       ->GetRenderViewHost()
       ->GetWidget()
       ->GetWidgetInputHandler()
-      ->ImeSetComposition(params.text, ui_underlines, params.replacement_range,
-                          params.selection_start, params.selection_end);
+      ->ImeSetComposition(params.text, ui_ime_text_spans,
+                          params.replacement_range, params.selection_start,
+                          params.selection_end);
 }
 
 void BrowserPluginGuest::OnImeCommitText(
     int browser_plugin_instance_id,
     const base::string16& text,
-    const std::vector<blink::WebCompositionUnderline>& underlines,
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int relative_cursor_pos) {
-  std::vector<ui::CompositionUnderline> ui_underlines =
-      ConvertToUiUnderline(underlines);
+  std::vector<ui::ImeTextSpan> ui_ime_text_spans =
+      ConvertToUiImeTextSpan(ime_text_spans);
   GetWebContents()
       ->GetRenderViewHost()
       ->GetWidget()
       ->GetWidgetInputHandler()
-      ->ImeCommitText(text, ui_underlines, replacement_range,
+      ->ImeCommitText(text, ui_ime_text_spans, replacement_range,
                       relative_cursor_pos);
 }
 
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
index 725b77d..ac97e22 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -38,8 +38,8 @@
 #include "third_party/WebKit/public/platform/WebDragOperation.h"
 #include "third_party/WebKit/public/platform/WebFocusType.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "third_party/WebKit/public/web/WebDragStatus.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ime/text_input_type.h"
 #include "ui/gfx/geometry/rect.h"
@@ -336,12 +336,11 @@
   void OnImeSetComposition(
       int instance_id,
       const BrowserPluginHostMsg_SetComposition_Params& params);
-  void OnImeCommitText(
-      int instance_id,
-      const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
-      const gfx::Range& replacement_range,
-      int relative_cursor_pos);
+  void OnImeCommitText(int instance_id,
+                       const base::string16& text,
+                       const std::vector<blink::WebImeTextSpan>& ime_text_spans,
+                       const gfx::Range& replacement_range,
+                       int relative_cursor_pos);
   void OnImeFinishComposingText(int instance_id, bool keep_selection);
   void OnExtendSelectionAndDelete(int instance_id, int before, int after);
   void OnImeCancelComposition();
diff --git a/content/browser/devtools/BUILD.gn b/content/browser/devtools/BUILD.gn
index 1f3a739e..611d028 100644
--- a/content/browser/devtools/BUILD.gn
+++ b/content/browser/devtools/BUILD.gn
@@ -74,6 +74,8 @@
 
   # These are relative to $target_gen_dir.
   outputs = [
+    "protocol/browser.cc",
+    "protocol/browser.h",
     "protocol/dom.cc",
     "protocol/dom.h",
     "protocol/emulation.cc",
diff --git a/content/browser/devtools/browser_devtools_agent_host.cc b/content/browser/devtools/browser_devtools_agent_host.cc
index bb7a585..74d57201 100644
--- a/content/browser/devtools/browser_devtools_agent_host.cc
+++ b/content/browser/devtools/browser_devtools_agent_host.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
 #include "content/browser/devtools/devtools_session.h"
+#include "content/browser/devtools/protocol/browser_handler.h"
 #include "content/browser/devtools/protocol/io_handler.h"
 #include "content/browser/devtools/protocol/memory_handler.h"
 #include "content/browser/devtools/protocol/protocol.h"
@@ -51,6 +52,7 @@
   if (only_discovery_)
     return;
 
+  session->AddHandler(base::WrapUnique(new protocol::BrowserHandler()));
   session->AddHandler(base::WrapUnique(new protocol::IOHandler(
       GetIOContext())));
   session->AddHandler(base::WrapUnique(new protocol::MemoryHandler()));
diff --git a/content/browser/devtools/protocol/browser_handler.cc b/content/browser/devtools/protocol/browser_handler.cc
new file mode 100644
index 0000000..b5fd5f4
--- /dev/null
+++ b/content/browser/devtools/protocol/browser_handler.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/protocol/browser_handler.h"
+
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/user_agent.h"
+#include "v8/include/v8-version-string.h"
+
+namespace content {
+namespace protocol {
+
+BrowserHandler::BrowserHandler()
+    : DevToolsDomainHandler(Browser::Metainfo::domainName) {}
+
+BrowserHandler::~BrowserHandler() {}
+
+void BrowserHandler::Wire(UberDispatcher* dispatcher) {
+  Browser::Dispatcher::wire(dispatcher, this);
+}
+
+Response BrowserHandler::GetVersion(std::string* protocol_version,
+                                    std::string* product,
+                                    std::string* revision,
+                                    std::string* user_agent,
+                                    std::string* js_version) {
+  *protocol_version = DevToolsAgentHost::GetProtocolVersion();
+  *revision = GetWebKitRevision();
+  *product = GetContentClient()->GetProduct();
+  *user_agent = GetContentClient()->GetUserAgent();
+  *js_version = V8_VERSION_STRING;
+  return Response::OK();
+}
+
+}  // namespace protocol
+}  // namespace content
diff --git a/content/browser/devtools/protocol/browser_handler.h b/content/browser/devtools/protocol/browser_handler.h
new file mode 100644
index 0000000..fe109fe
--- /dev/null
+++ b/content/browser/devtools/protocol/browser_handler.h
@@ -0,0 +1,37 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_BROWSER_HANDLER_H_
+#define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_BROWSER_HANDLER_H_
+
+#include "base/macros.h"
+#include "content/browser/devtools/protocol/browser.h"
+#include "content/browser/devtools/protocol/devtools_domain_handler.h"
+
+namespace content {
+
+namespace protocol {
+
+class BrowserHandler : public DevToolsDomainHandler, public Browser::Backend {
+ public:
+  BrowserHandler();
+  ~BrowserHandler() override;
+
+  void Wire(UberDispatcher* dispatcher) override;
+
+  // Protocol methods.
+  Response GetVersion(std::string* protocol_version,
+                      std::string* product,
+                      std::string* revision,
+                      std::string* user_agent,
+                      std::string* js_version) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrowserHandler);
+};
+
+}  // namespace protocol
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_BROWSER_HANDLER_H_
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 9a526397..4d89cb2b 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -167,12 +167,6 @@
     return true;
   }
 
-  void ShowCertificateViewerInDevTools(
-      WebContents* web_contents,
-      scoped_refptr<net::X509Certificate> certificate) override {
-    last_shown_certificate_ = certificate;
-  }
-
   base::DictionaryValue* SendCommand(
       const std::string& method,
       std::unique_ptr<base::DictionaryValue> params) {
@@ -353,10 +347,6 @@
     return urls;
   }
 
-  const scoped_refptr<net::X509Certificate>& last_shown_certificate() {
-    return last_shown_certificate_;
-  }
-
   const scoped_refptr<net::X509Certificate>& ok_cert() { return ok_cert_; }
 
   const scoped_refptr<net::X509Certificate>& expired_cert() {
@@ -419,7 +409,6 @@
   std::unique_ptr<base::DictionaryValue> waiting_for_notification_params_;
   int waiting_for_command_result_id_;
   bool in_dispatch_;
-  scoped_refptr<net::X509Certificate> last_shown_certificate_;
   scoped_refptr<net::X509Certificate> ok_cert_;
   scoped_refptr<net::X509Certificate> expired_cert_;
   bool agent_host_can_close_;
@@ -586,17 +575,21 @@
 bool MatchesBitmap(const SkBitmap& expected_bmp,
                    const SkBitmap& actual_bmp,
                    const gfx::Rect& matching_mask,
+                   float device_scale_factor,
                    int error_limit) {
   // Number of pixels with an error
   int error_pixels_count = 0;
 
   gfx::Rect error_bounding_rect = gfx::Rect();
 
+  // Scale expectations along with the mask.
+  device_scale_factor = device_scale_factor ? device_scale_factor : 1;
+
   // Check that bitmaps have identical dimensions.
-  EXPECT_EQ(expected_bmp.width(), actual_bmp.width());
-  EXPECT_EQ(expected_bmp.height(), actual_bmp.height());
-  if (expected_bmp.width() != actual_bmp.width() ||
-      expected_bmp.height() != actual_bmp.height()) {
+  EXPECT_EQ(expected_bmp.width() * device_scale_factor, actual_bmp.width());
+  EXPECT_EQ(expected_bmp.height() * device_scale_factor, actual_bmp.height());
+  if (expected_bmp.width() * device_scale_factor != actual_bmp.width() ||
+      expected_bmp.height() * device_scale_factor != actual_bmp.height()) {
     return false;
   }
 
@@ -604,7 +597,8 @@
 
   for (int x = matching_mask.x(); x < matching_mask.right(); ++x) {
     for (int y = matching_mask.y(); y < matching_mask.bottom(); ++y) {
-      SkColor actual_color = actual_bmp.getColor(x, y);
+      SkColor actual_color =
+          actual_bmp.getColor(x * device_scale_factor, y * device_scale_factor);
       SkColor expected_color = expected_bmp.getColor(x, y);
       if (!ColorsMatchWithinLimit(actual_color, expected_color, error_limit)) {
         if (error_pixels_count < 10) {
@@ -632,13 +626,14 @@
   enum ScreenshotEncoding { ENCODING_PNG, ENCODING_JPEG };
   void CaptureScreenshotAndCompareTo(const SkBitmap& expected_bitmap,
                                      ScreenshotEncoding encoding,
-                                     bool fromSurface,
+                                     bool from_surface,
+                                     float device_scale_factor = 0,
                                      const gfx::RectF& clip = gfx::RectF(),
                                      float clip_scale = 0) {
     std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue());
     params->SetString("format", encoding == ENCODING_PNG ? "png" : "jpeg");
     params->SetInteger("quality", 100);
-    params->SetBoolean("fromSurface", fromSurface);
+    params->SetBoolean("fromSurface", from_surface);
     if (clip_scale) {
       std::unique_ptr<base::DictionaryValue> clip_value(
           new base::DictionaryValue());
@@ -674,13 +669,14 @@
     matching_mask.Inset(4, 4, 4, 4);
 #endif
     EXPECT_TRUE(MatchesBitmap(expected_bitmap, *result_bitmap, matching_mask,
-                              error_limit));
+                              device_scale_factor, error_limit));
   }
 
   // Takes a screenshot of a colored box that is positioned inside the frame.
   void PlaceAndCaptureBox(const gfx::Size& frame_size,
                           const gfx::Size& box_size,
-                          float screenshot_scale) {
+                          float screenshot_scale,
+                          float device_scale_factor) {
     static const int kBoxOffsetHeight = 100;
     const gfx::Size scaled_box_size =
         ScaleToFlooredSize(box_size, screenshot_scale);
@@ -711,7 +707,7 @@
     params.reset(new base::DictionaryValue());
     params->SetInteger("width", frame_size.width());
     params->SetInteger("height", frame_size.height());
-    params->SetDouble("deviceScaleFactor", 1);
+    params->SetDouble("deviceScaleFactor", device_scale_factor);
     params->SetBoolean("mobile", false);
     SendCommand("Emulation.setDeviceMetricsOverride", std::move(params));
 
@@ -727,8 +723,8 @@
     expected_bitmap.allocN32Pixels(scaled_box_size.width(),
                                    scaled_box_size.height());
     expected_bitmap.eraseColor(SkColorSetRGB(0x00, 0x00, 0xff));
-    CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_PNG, true, clip,
-                                  screenshot_scale);
+    CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_PNG, true,
+                                  device_scale_factor, clip, screenshot_scale);
 
     // Reset for next screenshot.
     SendCommand("Emulation.clearDeviceMetricsOverride", nullptr);
@@ -801,12 +797,17 @@
   Attach();
 
   // Test capturing a subarea inside the emulated frame at different scales.
-  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 1.0);
-  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 2.0);
-  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 0.5);
+  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 1.0, 1.);
+  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 2.0, 1.);
+  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 0.5, 1.);
 
   // Ensure that content outside the emulated frame is painted, too.
-  PlaceAndCaptureBox(kFrameSize, gfx::Size(10, 8192), 1.0);
+  PlaceAndCaptureBox(kFrameSize, gfx::Size(10, 8192), 1.0, 1.);
+
+  // Check non-1 device scale factor.
+  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 1.0, 2.);
+  // Ensure not emulating device scale factor works.
+  PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 1.0, 0.);
 }
 
 // Verifies that setDefaultBackgroundColor and captureScreenshot support a
@@ -1548,59 +1549,6 @@
   EXPECT_THAT(console_messages_, ElementsAre("before", "at", "done", "after"));
 }
 
-// Tests that the Security.showCertificateViewer command shows the
-// certificate corresponding to the visible navigation entry, even when
-// an interstitial is showing. Regression test for
-// https://crbug.com/647759.
-IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, ShowCertificateViewer) {
-  // First test that the correct certificate is shown for a normal
-  // (non-interstitial) page.
-  NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1);
-  Attach();
-
-  // Set a dummy certificate on the NavigationEntry.
-  shell()
-      ->web_contents()
-      ->GetController()
-      .GetVisibleEntry()
-      ->GetSSL()
-      .certificate = ok_cert();
-
-  std::unique_ptr<base::DictionaryValue> params1(new base::DictionaryValue());
-  SendCommand("Security.showCertificateViewer", std::move(params1), true);
-
-  scoped_refptr<net::X509Certificate> normal_page_cert = shell()
-                                                             ->web_contents()
-                                                             ->GetController()
-                                                             .GetVisibleEntry()
-                                                             ->GetSSL()
-                                                             .certificate;
-  ASSERT_TRUE(normal_page_cert);
-  EXPECT_EQ(normal_page_cert, last_shown_certificate());
-
-  // Now test that the correct certificate is shown on an interstitial.
-  TestInterstitialDelegate* delegate = new TestInterstitialDelegate;
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
-  GURL interstitial_url("https://example.test");
-  InterstitialPageImpl* interstitial = new InterstitialPageImpl(
-      web_contents, static_cast<RenderWidgetHostDelegate*>(web_contents), true,
-      interstitial_url, delegate);
-  interstitial->Show();
-  WaitForInterstitialAttach(web_contents);
-
-  // Set the transient navigation entry certificate.
-  NavigationEntry* transient_entry =
-      web_contents->GetController().GetTransientEntry();
-  ASSERT_TRUE(transient_entry);
-  transient_entry->GetSSL().certificate = expired_cert();
-  ASSERT_TRUE(transient_entry->GetSSL().certificate);
-
-  std::unique_ptr<base::DictionaryValue> params2(new base::DictionaryValue());
-  SendCommand("Security.showCertificateViewer", std::move(params2), true);
-  EXPECT_EQ(transient_entry->GetSSL().certificate, last_shown_certificate());
-}
-
 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, CertificateError) {
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
diff --git a/content/browser/devtools/protocol/io_handler.h b/content/browser/devtools/protocol/io_handler.h
index 3bff07c..066541c 100644
--- a/content/browser/devtools/protocol/io_handler.h
+++ b/content/browser/devtools/protocol/io_handler.h
@@ -49,4 +49,4 @@
 }  // namespace protocol
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_TRACING_HANDLER_H_
+#endif  // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_IO_HANDLER_H_
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 714cccf7..21c9d64 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -458,8 +458,10 @@
     if (!modified_params.view_size.height)
       emulated_view_size.set_height(original_view_size.height());
 
-    dpfactor =
-        modified_params.device_scale_factor / screen_info.device_scale_factor;
+    dpfactor = modified_params.device_scale_factor
+                   ? modified_params.device_scale_factor /
+                         screen_info.device_scale_factor
+                   : 1;
     // When clip is specified, we scale viewport via clip, otherwise we use
     // scale.
     modified_params.scale = clip.isJust() ? 1 : dpfactor;
diff --git a/content/browser/devtools/protocol/security_handler.cc b/content/browser/devtools/protocol/security_handler.cc
index 0ba37a9..803f58f 100644
--- a/content/browser/devtools/protocol/security_handler.cc
+++ b/content/browser/devtools/protocol/security_handler.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/base64.h"
 #include "content/browser/devtools/devtools_session.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/public/browser/navigation_controller.h"
@@ -14,6 +15,7 @@
 #include "content/public/browser/ssl_status.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "net/cert/x509_certificate.h"
 #include "third_party/WebKit/public/platform/WebMixedContentContextType.h"
 
 namespace content {
@@ -66,12 +68,32 @@
     const std::vector<SecurityStyleExplanation>& explanations_to_add,
     Explanations* explanations) {
   for (const auto& it : explanations_to_add) {
+    std::unique_ptr<protocol::Array<String>> certificate =
+        protocol::Array<String>::create();
+    if (it.certificate) {
+      std::string der;
+      std::string encoded;
+      bool rv = net::X509Certificate::GetDEREncoded(
+          it.certificate->os_cert_handle(), &der);
+      DCHECK(rv);
+      base::Base64Encode(der, &encoded);
+
+      certificate->addItem(encoded);
+
+      for (auto* cert : it.certificate->GetIntermediateCertificates()) {
+        rv = net::X509Certificate::GetDEREncoded(cert, &der);
+        DCHECK(rv);
+        base::Base64Encode(der, &encoded);
+        certificate->addItem(encoded);
+      }
+    }
+
     explanations->addItem(
         Security::SecurityStateExplanation::Create()
             .SetSecurityState(security_style)
             .SetSummary(it.summary)
             .SetDescription(it.description)
-            .SetHasCertificate(it.has_certificate)
+            .SetCertificate(std::move(certificate))
             .SetMixedContentType(MixedContentTypeToProtocolMixedContentType(
                 it.mixed_content_type))
             .Build());
@@ -210,19 +232,6 @@
   return Response::OK();
 }
 
-Response SecurityHandler::ShowCertificateViewer() {
-  if (!host_)
-    return Response::InternalError();
-  WebContents* web_contents = WebContents::FromRenderFrameHost(host_);
-  scoped_refptr<net::X509Certificate> certificate =
-      web_contents->GetController().GetVisibleEntry()->GetSSL().certificate;
-  if (!certificate)
-    return Response::Error("Could not find certificate");
-  web_contents->GetDelegate()->ShowCertificateViewerInDevTools(
-      web_contents, certificate);
-  return Response::OK();
-}
-
 Response SecurityHandler::HandleCertificateError(int event_id,
                                                  const String& action) {
   if (cert_error_callbacks_.find(event_id) == cert_error_callbacks_.end()) {
diff --git a/content/browser/devtools/protocol/security_handler.h b/content/browser/devtools/protocol/security_handler.h
index 96c24ff3..c70fdef 100644
--- a/content/browser/devtools/protocol/security_handler.h
+++ b/content/browser/devtools/protocol/security_handler.h
@@ -40,7 +40,6 @@
   // Security::Backend overrides.
   Response Enable() override;
   Response Disable() override;
-  Response ShowCertificateViewer() override;
   Response HandleCertificateError(int event_id, const String& action) override;
   Response SetOverrideCertificateErrors(bool override) override;
 
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index a43e4ef..057470ce 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -10,6 +10,10 @@
         "export_header": "content/common/content_export.h",
         "options": [
             {
+                "domain": "Browser",
+                "include": ["getVersion"]
+            },
+            {
                 "domain": "DOM",
                 "include": ["setFileInputFiles"],
                 "include_events": [],
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index b95c89a2..10503c7 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -18,6 +18,7 @@
 #include "content/browser/devtools/devtools_frame_trace_recorder.h"
 #include "content/browser/devtools/devtools_manager.h"
 #include "content/browser/devtools/devtools_session.h"
+#include "content/browser/devtools/protocol/browser_handler.h"
 #include "content/browser/devtools/protocol/dom_handler.h"
 #include "content/browser/devtools/protocol/emulation_handler.h"
 #include "content/browser/devtools/protocol/input_handler.h"
@@ -506,6 +507,7 @@
 
   protocol::EmulationHandler* emulation_handler =
       new protocol::EmulationHandler();
+  session->AddHandler(base::WrapUnique(new protocol::BrowserHandler()));
   session->AddHandler(base::WrapUnique(new protocol::DOMHandler()));
   session->AddHandler(base::WrapUnique(emulation_handler));
   session->AddHandler(base::WrapUnique(new protocol::InputHandler()));
diff --git a/content/browser/dom_storage/dom_storage_host.cc b/content/browser/dom_storage/dom_storage_host.cc
index 162cdc7..38655ef 100644
--- a/content/browser/dom_storage/dom_storage_host.cc
+++ b/content/browser/dom_storage/dom_storage_host.cc
@@ -32,8 +32,16 @@
   references.namespace_ = context_->GetStorageNamespace(namespace_id);
   if (!references.namespace_.get())
     return context_->DiagnoseSessionNamespaceId(namespace_id);
-  references.area_ = references.namespace_->OpenStorageArea(origin);
-  DCHECK(references.area_.get());
+
+  // namespace->OpenStorageArea() is called only once per process
+  // (areas_open_count[area] is 0).
+  references.area_ = references.namespace_->GetOpenStorageArea(origin);
+  if (!references.area_ || !areas_open_count_[references.area_.get()]) {
+    references.area_ = references.namespace_->OpenStorageArea(origin);
+    DCHECK(references.area_.get());
+    DCHECK_EQ(0, areas_open_count_[references.area_.get()]);
+  }
+  ++areas_open_count_[references.area_.get()];
   connections_[connection_id] = references;
   return base::nullopt;
 }
@@ -42,7 +50,15 @@
   const auto found = connections_.find(connection_id);
   if (found == connections_.end())
     return;
-  found->second.namespace_->CloseStorageArea(found->second.area_.get());
+  DOMStorageArea* area = found->second.area_.get();
+  DCHECK(areas_open_count_[area]);
+
+  // namespace->CloseStorageArea() is called only once per process
+  // (areas_open_count[area] becomes 0).
+  if (--areas_open_count_[area] == 0) {
+    found->second.namespace_->CloseStorageArea(area);
+    areas_open_count_.erase(area);
+  }
   connections_.erase(found);
 }
 
@@ -84,29 +100,34 @@
   return area->GetItem(key);
 }
 
-bool DOMStorageHost::SetAreaItem(
-    int connection_id, const base::string16& key,
-    const base::string16& value, const GURL& page_url,
-    base::NullableString16* old_value) {
+bool DOMStorageHost::SetAreaItem(int connection_id,
+                                 const base::string16& key,
+                                 const base::string16& value,
+                                 const base::NullableString16& client_old_value,
+                                 const GURL& page_url) {
   DOMStorageArea* area = GetOpenArea(connection_id);
   if (!area)
     return false;
-  if (!area->SetItem(key, value, old_value))
+  base::NullableString16 old_value;
+  if (!area->SetItem(key, value, &old_value))
     return false;
-  if (old_value->is_null() || old_value->string() != value)
-    context_->NotifyItemSet(area, key, value, *old_value, page_url);
+  if (old_value.is_null() || old_value.string() != value)
+    context_->NotifyItemSet(area, key, value, old_value, page_url);
   return true;
 }
 
 bool DOMStorageHost::RemoveAreaItem(
-    int connection_id, const base::string16& key, const GURL& page_url,
-    base::string16* old_value) {
+    int connection_id,
+    const base::string16& key,
+    const base::NullableString16& client_old_value,
+    const GURL& page_url) {
   DOMStorageArea* area = GetOpenArea(connection_id);
   if (!area)
     return false;
-  if (!area->RemoveItem(key, old_value))
+  base::string16 old_value;
+  if (!area->RemoveItem(key, &old_value))
     return false;
-  context_->NotifyItemRemoved(area, key, *old_value, page_url);
+  context_->NotifyItemRemoved(area, key, old_value, page_url);
   return true;
 }
 
diff --git a/content/browser/dom_storage/dom_storage_host.h b/content/browser/dom_storage/dom_storage_host.h
index 6b931d19..5804806 100644
--- a/content/browser/dom_storage/dom_storage_host.h
+++ b/content/browser/dom_storage/dom_storage_host.h
@@ -45,13 +45,15 @@
   base::NullableString16 GetAreaKey(int connection_id, unsigned index);
   base::NullableString16 GetAreaItem(int connection_id,
                                      const base::string16& key);
-  bool SetAreaItem(int connection_id, const base::string16& key,
-                   const base::string16& value, const GURL& page_url,
-                   base::NullableString16* old_value);
+  bool SetAreaItem(int connection_id,
+                   const base::string16& key,
+                   const base::string16& value,
+                   const base::NullableString16& client_old_value,
+                   const GURL& page_url);
   bool RemoveAreaItem(int connection_id,
                       const base::string16& key,
-                      const GURL& page_url,
-                      base::string16* old_value);
+                      const base::NullableString16& client_old_value,
+                      const GURL& page_url);
   bool ClearArea(int connection_id, const GURL& page_url);
   bool HasAreaOpen(int namespace_id, const GURL& origin) const;
   bool HasConnection(int connection_id) const {
@@ -75,6 +77,7 @@
 
   scoped_refptr<DOMStorageContextImpl> context_;
   AreaMap connections_;
+  std::map<DOMStorageArea*, int> areas_open_count_;
 
   DISALLOW_COPY_AND_ASSIGN(DOMStorageHost);
 };
diff --git a/content/browser/dom_storage/dom_storage_message_filter.cc b/content/browser/dom_storage/dom_storage_message_filter.cc
index ec5622f..bb64fcc 100644
--- a/content/browser/dom_storage/dom_storage_message_filter.cc
+++ b/content/browser/dom_storage/dom_storage_message_filter.cc
@@ -112,27 +112,30 @@
 }
 
 void DOMStorageMessageFilter::OnSetItem(
-    int connection_id, const base::string16& key,
-    const base::string16& value, const GURL& page_url) {
-  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DCHECK_EQ(0, connection_dispatching_message_for_);
-  base::AutoReset<int> auto_reset(&connection_dispatching_message_for_,
-                            connection_id);
-  base::NullableString16 not_used;
-  bool success = host_->SetAreaItem(connection_id, key, value,
-                                    page_url, &not_used);
-  Send(new DOMStorageMsg_AsyncOperationComplete(success));
-}
-
-void DOMStorageMessageFilter::OnRemoveItem(
-    int connection_id, const base::string16& key,
+    int connection_id,
+    const base::string16& key,
+    const base::string16& value,
+    const base::NullableString16& client_old_value,
     const GURL& page_url) {
   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK_EQ(0, connection_dispatching_message_for_);
   base::AutoReset<int> auto_reset(&connection_dispatching_message_for_,
                             connection_id);
-  base::string16 not_used;
-  host_->RemoveAreaItem(connection_id, key, page_url, &not_used);
+  bool success =
+      host_->SetAreaItem(connection_id, key, value, client_old_value, page_url);
+  Send(new DOMStorageMsg_AsyncOperationComplete(success));
+}
+
+void DOMStorageMessageFilter::OnRemoveItem(
+    int connection_id,
+    const base::string16& key,
+    const base::NullableString16& client_old_value,
+    const GURL& page_url) {
+  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_EQ(0, connection_dispatching_message_for_);
+  base::AutoReset<int> auto_reset(&connection_dispatching_message_for_,
+                            connection_id);
+  host_->RemoveAreaItem(connection_id, key, client_old_value, page_url);
   Send(new DOMStorageMsg_AsyncOperationComplete(true));
 }
 
diff --git a/content/browser/dom_storage/dom_storage_message_filter.h b/content/browser/dom_storage/dom_storage_message_filter.h
index c3394e7..ff199f8 100644
--- a/content/browser/dom_storage/dom_storage_message_filter.h
+++ b/content/browser/dom_storage/dom_storage_message_filter.h
@@ -55,9 +55,14 @@
                          const GURL& origin);
   void OnCloseStorageArea(int connection_id);
   void OnLoadStorageArea(int connection_id, DOMStorageValuesMap* map);
-  void OnSetItem(int connection_id, const base::string16& key,
-                 const base::string16& value, const GURL& page_url);
-  void OnRemoveItem(int connection_id, const base::string16& key,
+  void OnSetItem(int connection_id,
+                 const base::string16& key,
+                 const base::string16& value,
+                 const base::NullableString16& client_old_value,
+                 const GURL& page_url);
+  void OnRemoveItem(int connection_id,
+                    const base::string16& key,
+                    const base::NullableString16& client_old_value,
                     const GURL& page_url);
   void OnClear(int connection_id, const GURL& page_url);
   void OnFlushMessages();
diff --git a/content/browser/dom_storage/dom_storage_namespace.cc b/content/browser/dom_storage/dom_storage_namespace.cc
index 847b193..10568d3 100644
--- a/content/browser/dom_storage/dom_storage_namespace.cc
+++ b/content/browser/dom_storage/dom_storage_namespace.cc
@@ -196,6 +196,13 @@
     origins->push_back(entry.first);
 }
 
+int DOMStorageNamespace::GetAreaOpenCount(const GURL& origin) const {
+  const auto& found = areas_.find(origin);
+  if (found == areas_.end())
+    return 0;
+  return found->second.open_count_;
+}
+
 DOMStorageNamespace::AreaHolder*
 DOMStorageNamespace::GetAreaHolder(const GURL& origin) {
   AreaMap::iterator found = areas_.find(origin);
diff --git a/content/browser/dom_storage/dom_storage_namespace.h b/content/browser/dom_storage/dom_storage_namespace.h
index 8f75f2ab..2fdab5a 100644
--- a/content/browser/dom_storage/dom_storage_namespace.h
+++ b/content/browser/dom_storage/dom_storage_namespace.h
@@ -85,6 +85,8 @@
 
   void GetOriginsWithAreas(std::vector<GURL>* origins) const;
 
+  int GetAreaOpenCount(const GURL& origin) const;
+
  private:
   friend class base::RefCountedThreadSafe<DOMStorageNamespace>;
 
diff --git a/content/browser/download/download_request_core.cc b/content/browser/download/download_request_core.cc
index e37a480..2f3619b 100644
--- a/content/browser/download/download_request_core.cc
+++ b/content/browser/download/download_request_core.cc
@@ -461,106 +461,4 @@
       request() ? request()->url().spec().c_str() : "<NULL request>");
 }
 
-// static
-DownloadInterruptReason DownloadRequestCore::HandleSuccessfulServerResponse(
-    const net::HttpResponseHeaders& http_headers,
-    DownloadSaveInfo* save_info) {
-  switch (http_headers.response_code()) {
-    case -1:  // Non-HTTP request.
-    case net::HTTP_OK:
-    case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
-    case net::HTTP_PARTIAL_CONTENT:
-      // Expected successful codes.
-      break;
-
-    case net::HTTP_CREATED:
-    case net::HTTP_ACCEPTED:
-      // Per RFC 7231 the entity being transferred is metadata about the
-      // resource at the target URL and not the resource at that URL (or the
-      // resource that would be at the URL once processing is completed in the
-      // case of HTTP_ACCEPTED). However, we currently don't have special
-      // handling for these response and they are downloaded the same as a
-      // regular response.
-      break;
-
-    case net::HTTP_NO_CONTENT:
-    case net::HTTP_RESET_CONTENT:
-    // These two status codes don't have an entity (or rather RFC 7231
-    // requires that there be no entity). They are treated the same as the
-    // resource not being found since there is no entity to download.
-
-    case net::HTTP_NOT_FOUND:
-      return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
-      break;
-
-    case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
-      // Retry by downloading from the start automatically:
-      // If we haven't received data when we get this error, we won't.
-      return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
-      break;
-    case net::HTTP_UNAUTHORIZED:
-    case net::HTTP_PROXY_AUTHENTICATION_REQUIRED:
-      // Server didn't authorize this request.
-      return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
-      break;
-    case net::HTTP_FORBIDDEN:
-      // Server forbids access to this resource.
-      return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
-      break;
-    default:  // All other errors.
-      // Redirection and informational codes should have been handled earlier
-      // in the stack.
-      // TODO(xingliu): Handle HTTP_PRECONDITION_FAILED and resurrect
-      // DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION for range requests.
-      // This will change extensions::api::download::InterruptReason.
-      DCHECK_NE(3, http_headers.response_code() / 100);
-      DCHECK_NE(1, http_headers.response_code() / 100);
-      return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
-  }
-
-  // The caller is expecting a partial response.
-  if (save_info && (save_info->offset > 0 || save_info->length > 0)) {
-    if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) {
-      // Server should send partial content when "If-Match" or
-      // "If-Unmodified-Since" check passes, and the range request header has
-      // last byte position. e.g. "Range:bytes=50-99".
-      if (save_info->length != DownloadSaveInfo::kLengthFullContent)
-        return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
-
-      // Requested a partial range, but received the entire response, when
-      // the range request header is "Range:bytes={offset}-".
-      save_info->offset = 0;
-      save_info->hash_of_partial_file.clear();
-      save_info->hash_state.reset();
-      return DOWNLOAD_INTERRUPT_REASON_NONE;
-    }
-
-    int64_t first_byte = -1;
-    int64_t last_byte = -1;
-    int64_t length = -1;
-    if (!http_headers.GetContentRangeFor206(&first_byte, &last_byte, &length))
-      return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
-    DCHECK_GE(first_byte, 0);
-
-    if (first_byte != save_info->offset ||
-        (save_info->length > 0 &&
-         last_byte != save_info->offset + save_info->length - 1)) {
-      // The server returned a different range than the one we requested. Assume
-      // the response is bad.
-      //
-      // In the future we should consider allowing offsets that are less than
-      // the offset we've requested, since in theory we can truncate the partial
-      // file at the offset and continue.
-      return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
-    }
-
-    return DOWNLOAD_INTERRUPT_REASON_NONE;
-  }
-
-  if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT)
-    return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
-
-  return DOWNLOAD_INTERRUPT_REASON_NONE;
-}
-
 }  // namespace content
diff --git a/content/browser/download/download_request_core.h b/content/browser/download/download_request_core.h
index 93c4315..52fd227 100644
--- a/content/browser/download/download_request_core.h
+++ b/content/browser/download/download_request_core.h
@@ -115,10 +115,6 @@
   net::URLRequest* request() const { return request_; }
 
  private:
-  static DownloadInterruptReason HandleSuccessfulServerResponse(
-      const net::HttpResponseHeaders& http_headers,
-      DownloadSaveInfo* save_info);
-
   std::unique_ptr<DownloadCreateInfo> CreateDownloadCreateInfo(
       DownloadInterruptReason result);
 
diff --git a/content/browser/download/download_response_handler.cc b/content/browser/download/download_response_handler.cc
new file mode 100644
index 0000000..715df6d
--- /dev/null
+++ b/content/browser/download/download_response_handler.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/download/download_response_handler.h"
+
+#include "content/browser/download/download_stats.h"
+#include "content/browser/download/download_utils.h"
+
+namespace content {
+
+DownloadResponseHandler::DownloadResponseHandler(
+    ResourceRequest* resource_request,
+    Delegate* delegate,
+    bool is_parallel_request)
+    : delegate_(delegate) {
+  if (!is_parallel_request)
+    RecordDownloadCount(UNTHROTTLED_COUNT);
+
+  // TODO(qinmin): create the DownloadSaveInfo from |resource_request|
+}
+
+DownloadResponseHandler::~DownloadResponseHandler() = default;
+
+void DownloadResponseHandler::OnReceiveResponse(
+    const content::ResourceResponseHead& head,
+    const base::Optional<net::SSLInfo>& ssl_info,
+    mojom::DownloadedTempFilePtr downloaded_file) {
+  // TODO(qinmin): create the DownloadCreateInfo from |head|.
+
+  // TODO(qinmin): pass DownloadSaveInfo here.
+  DownloadInterruptReason result =
+      head.headers
+          ? HandleSuccessfulServerResponse(*(head.headers.get()), nullptr)
+          : DOWNLOAD_INTERRUPT_REASON_NONE;
+  if (result != DOWNLOAD_INTERRUPT_REASON_NONE) {
+    delegate_->OnResponseStarted(download_create_info_,
+                                 mojo::ScopedDataPipeConsumerHandle());
+  }
+}
+
+void DownloadResponseHandler::OnReceiveRedirect(
+    const net::RedirectInfo& redirect_info,
+    const content::ResourceResponseHead& head) {}
+
+void DownloadResponseHandler::OnDataDownloaded(int64_t data_length,
+                                               int64_t encoded_length) {}
+
+void DownloadResponseHandler::OnUploadProgress(
+    int64_t current_position,
+    int64_t total_size,
+    OnUploadProgressCallback callback) {}
+
+void DownloadResponseHandler::OnReceiveCachedMetadata(
+    const std::vector<uint8_t>& data) {}
+
+void DownloadResponseHandler::OnTransferSizeUpdated(
+    int32_t transfer_size_diff) {};
+
+void DownloadResponseHandler::OnStartLoadingResponseBody(
+    mojo::ScopedDataPipeConsumerHandle body) {
+  delegate_->OnResponseStarted(download_create_info_, std::move(body));
+}
+
+void DownloadResponseHandler::OnComplete(
+    const content::ResourceRequestCompletionStatus& completion_status) {
+  // TODO(qinmin): passing the |completion_status| to DownloadFile.
+}
+
+}  // namespace content
diff --git a/content/browser/download/download_response_handler.h b/content/browser/download/download_response_handler.h
new file mode 100644
index 0000000..f60158e
--- /dev/null
+++ b/content/browser/download/download_response_handler.h
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DOWNLOAD_RESPONSE_HANDLER_
+#define CONTENT_BROWSER_DOWNLOAD_RESPONSE_HANDLER_
+
+#include "content/browser/download/download_create_info.h"
+#include "content/public/common/resource_request.h"
+#include "content/public/common/url_loader.mojom.h"
+
+namespace content {
+
+// This class is responsible for handling the server response for a download.
+// It passes the DataPipeConsumerHandle and completion status to the download
+// sink. The class is common to both navigation triggered downloads and
+// context menu downloads
+class DownloadResponseHandler : public mojom::URLLoaderClient {
+ public:
+  class Delegate {
+   public:
+    virtual void OnResponseStarted(
+        const DownloadCreateInfo& download_create_info,
+        mojo::ScopedDataPipeConsumerHandle body) = 0;
+  };
+
+  DownloadResponseHandler(ResourceRequest* resource_request,
+                          Delegate* delegate,
+                          bool is_parallel_request);
+  ~DownloadResponseHandler() override;
+
+  // mojom::URLLoaderClient
+  void OnReceiveResponse(const content::ResourceResponseHead& head,
+                         const base::Optional<net::SSLInfo>& ssl_info,
+                         mojom::DownloadedTempFilePtr downloaded_file) override;
+  void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
+                         const content::ResourceResponseHead& head) override;
+  void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override;
+  void OnUploadProgress(int64_t current_position,
+                        int64_t total_size,
+                        OnUploadProgressCallback callback) override;
+  void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override;
+  void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
+  void OnStartLoadingResponseBody(
+      mojo::ScopedDataPipeConsumerHandle body) override;
+  void OnComplete(const content::ResourceRequestCompletionStatus&
+                  completion_status) override;
+
+ private:
+  Delegate* const delegate_;
+
+  DownloadCreateInfo download_create_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadResponseHandler);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DOWNLOAD_RESPONSE_HANDLER
diff --git a/content/browser/download/download_utils.cc b/content/browser/download/download_utils.cc
index 856a531..3e288d8 100644
--- a/content/browser/download/download_utils.cc
+++ b/content/browser/download/download_utils.cc
@@ -10,12 +10,14 @@
 #include "content/browser/download/download_interrupt_reasons_impl.h"
 #include "content/browser/download/download_stats.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_save_info.h"
 #include "content/public/browser/download_url_parameters.h"
 #include "content/public/common/resource_request.h"
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/load_flags.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
 #include "net/url_request/url_request_context.h"
 
 namespace content {
@@ -250,5 +252,105 @@
   return request;
 }
 
+DownloadInterruptReason HandleSuccessfulServerResponse(
+    const net::HttpResponseHeaders& http_headers,
+    DownloadSaveInfo* save_info) {
+  switch (http_headers.response_code()) {
+    case -1:  // Non-HTTP request.
+    case net::HTTP_OK:
+    case net::HTTP_NON_AUTHORITATIVE_INFORMATION:
+    case net::HTTP_PARTIAL_CONTENT:
+      // Expected successful codes.
+      break;
+
+    case net::HTTP_CREATED:
+    case net::HTTP_ACCEPTED:
+      // Per RFC 7231 the entity being transferred is metadata about the
+      // resource at the target URL and not the resource at that URL (or the
+      // resource that would be at the URL once processing is completed in the
+      // case of HTTP_ACCEPTED). However, we currently don't have special
+      // handling for these response and they are downloaded the same as a
+      // regular response.
+      break;
+
+    case net::HTTP_NO_CONTENT:
+    case net::HTTP_RESET_CONTENT:
+    // These two status codes don't have an entity (or rather RFC 7231
+    // requires that there be no entity). They are treated the same as the
+    // resource not being found since there is no entity to download.
+
+    case net::HTTP_NOT_FOUND:
+      return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+      break;
+
+    case net::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
+      // Retry by downloading from the start automatically:
+      // If we haven't received data when we get this error, we won't.
+      return DOWNLOAD_INTERRUPT_REASON_SERVER_NO_RANGE;
+      break;
+    case net::HTTP_UNAUTHORIZED:
+    case net::HTTP_PROXY_AUTHENTICATION_REQUIRED:
+      // Server didn't authorize this request.
+      return DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED;
+      break;
+    case net::HTTP_FORBIDDEN:
+      // Server forbids access to this resource.
+      return DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN;
+      break;
+    default:  // All other errors.
+      // Redirection and informational codes should have been handled earlier
+      // in the stack.
+      // TODO(xingliu): Handle HTTP_PRECONDITION_FAILED and resurrect
+      // DOWNLOAD_INTERRUPT_REASON_SERVER_PRECONDITION for range requests.
+      // This will change extensions::api::download::InterruptReason.
+      DCHECK_NE(3, http_headers.response_code() / 100);
+      DCHECK_NE(1, http_headers.response_code() / 100);
+      return DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED;
+  }
+
+  // The caller is expecting a partial response.
+  if (save_info && (save_info->offset > 0 || save_info->length > 0)) {
+    if (http_headers.response_code() != net::HTTP_PARTIAL_CONTENT) {
+      // Server should send partial content when "If-Match" or
+      // "If-Unmodified-Since" check passes, and the range request header has
+      // last byte position. e.g. "Range:bytes=50-99".
+      if (save_info->length != DownloadSaveInfo::kLengthFullContent)
+        return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+
+      // Requested a partial range, but received the entire response, when
+      // the range request header is "Range:bytes={offset}-".
+      save_info->offset = 0;
+      save_info->hash_of_partial_file.clear();
+      save_info->hash_state.reset();
+      return DOWNLOAD_INTERRUPT_REASON_NONE;
+    }
+
+    int64_t first_byte = -1;
+    int64_t last_byte = -1;
+    int64_t length = -1;
+    if (!http_headers.GetContentRangeFor206(&first_byte, &last_byte, &length))
+      return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+    DCHECK_GE(first_byte, 0);
+
+    if (first_byte != save_info->offset ||
+        (save_info->length > 0 &&
+         last_byte != save_info->offset + save_info->length - 1)) {
+      // The server returned a different range than the one we requested. Assume
+      // the response is bad.
+      //
+      // In the future we should consider allowing offsets that are less than
+      // the offset we've requested, since in theory we can truncate the partial
+      // file at the offset and continue.
+      return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+    }
+
+    return DOWNLOAD_INTERRUPT_REASON_NONE;
+  }
+
+  if (http_headers.response_code() == net::HTTP_PARTIAL_CONTENT)
+    return DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT;
+
+  return DOWNLOAD_INTERRUPT_REASON_NONE;
+}
 
 }  // namespace content
diff --git a/content/browser/download/download_utils.h b/content/browser/download/download_utils.h
index 741218a..4b8c2ef 100644
--- a/content/browser/download/download_utils.h
+++ b/content/browser/download/download_utils.h
@@ -8,6 +8,7 @@
 #include "content/public/browser/download_interrupt_reasons.h"
 #include "net/base/net_errors.h"
 #include "net/cert/cert_status_flags.h"
+#include "net/http/http_response_headers.h"
 
 namespace net {
 class URLRequest;
@@ -17,6 +18,7 @@
 
 class DownloadUrlParameters;
 struct ResourceRequest;
+struct DownloadSaveInfo;
 
 // Handle the url request completion status and return the interrupt reasons.
 // |cert_status| is ignored if error_code is not net::ERR_ABORTED.
@@ -29,9 +31,13 @@
     DownloadUrlParameters* params);
 
 // Create a URLRequest from |params|.
-std::unique_ptr<net::URLRequest> CreateURLRequestOnIOThread(
+std::unique_ptr<net::URLRequest> CONTENT_EXPORT CreateURLRequestOnIOThread(
     DownloadUrlParameters* params);
 
+DownloadInterruptReason CONTENT_EXPORT
+HandleSuccessfulServerResponse(const net::HttpResponseHeaders& http_headers,
+                               DownloadSaveInfo* save_info);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_UTILS_H_
diff --git a/content/browser/download/parallel_download_job.cc b/content/browser/download/parallel_download_job.cc
index 519711e..c800f09 100644
--- a/content/browser/download/parallel_download_job.cc
+++ b/content/browser/download/parallel_download_job.cc
@@ -134,8 +134,10 @@
 void ParallelDownloadJob::BuildParallelRequests() {
   DCHECK(!requests_sent_);
   DCHECK(!is_paused());
-  if (is_canceled_)
+  if (is_canceled_ ||
+      download_item_->GetLastReason() != DOWNLOAD_INTERRUPT_REASON_NONE) {
     return;
+  }
 
   // TODO(qinmin): The size of |slices_to_download| should be no larger than
   // |kParallelRequestCount| unless |kParallelRequestCount| is changed after
diff --git a/content/browser/download/parallel_download_job_unittest.cc b/content/browser/download/parallel_download_job_unittest.cc
index ae7d5b04c..223a3a1 100644
--- a/content/browser/download/parallel_download_job_unittest.cc
+++ b/content/browser/download/parallel_download_job_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
+#include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "content/browser/download/download_destination_observer.h"
 #include "content/browser/download/download_file_impl.h"
@@ -22,6 +23,7 @@
 
 using ::testing::_;
 using ::testing::NiceMock;
+using ::testing::Return;
 using ::testing::StrictMock;
 
 namespace content {
@@ -91,6 +93,11 @@
 
   ParallelDownloadJob::WorkerMap& workers() { return workers_; }
 
+  void MakeFileInitialized(const DownloadFile::InitializeCallback& callback,
+                           DownloadInterruptReason result) {
+    ParallelDownloadJob::OnDownloadFileInitialized(callback, result);
+  }
+
   int GetParallelRequestCount() const override { return request_count_; }
   int64_t GetMinSliceSize() const override { return min_slice_size_; }
   int GetMinRemainingTimeInSeconds() const override {
@@ -198,7 +205,7 @@
   // Task 1:  Range:50-, for 50 bytes.
   CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 2, 1, 10);
   BuildParallelRequests();
-  EXPECT_EQ(1, static_cast<int>(job_->workers().size()));
+  EXPECT_EQ(1u, job_->workers().size());
   VerifyWorker(50, 0);
   DestroyParallelJob();
 
@@ -208,7 +215,7 @@
   // Task 2:  Range:66-, for 34 bytes.
   CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 3, 1, 10);
   BuildParallelRequests();
-  EXPECT_EQ(2, static_cast<int>(job_->workers().size()));
+  EXPECT_EQ(2u, job_->workers().size());
   VerifyWorker(33, 33);
   VerifyWorker(66, 0);
   DestroyParallelJob();
@@ -242,7 +249,7 @@
   DownloadItem::ReceivedSlices slices = {DownloadItem::ReceivedSlice(0, 17)};
   CreateParallelJob(12, 88, slices, 3, 1, 10);
   BuildParallelRequests();
-  EXPECT_EQ(2, static_cast<int>(job_->workers().size()));
+  EXPECT_EQ(2u, job_->workers().size());
   VerifyWorker(44, 27);
   VerifyWorker(71, 0);
   DestroyParallelJob();
@@ -256,7 +263,7 @@
   slices = {DownloadItem::ReceivedSlice(0, 60)};
   CreateParallelJob(60, 40, slices, 4, 20, 10);
   BuildParallelRequests();
-  EXPECT_EQ(1, static_cast<int>(job_->workers().size()));
+  EXPECT_EQ(1u, job_->workers().size());
   VerifyWorker(80, 0);
   DestroyParallelJob();
 
@@ -277,7 +284,7 @@
       DownloadItem::ReceivedSlice(40, 10), DownloadItem::ReceivedSlice(90, 10)};
   CreateParallelJob(0, 12, slices, 2, 1, 10);
   BuildParallelRequests();
-  EXPECT_EQ(3, static_cast<int>(job_->workers().size()));
+  EXPECT_EQ(3u, job_->workers().size());
   VerifyWorker(30, 10);
   VerifyWorker(50, 40);
   VerifyWorker(100, 0);
@@ -359,7 +366,7 @@
   DownloadItem::ReceivedSlices slices = {DownloadItem::ReceivedSlice(0, 99)};
   CreateParallelJob(99, 1, slices, 3, 1, 10);
   BuildParallelRequests();
-  EXPECT_EQ(0, static_cast<int>(job_->workers().size()));
+  EXPECT_EQ(0u, job_->workers().size());
 
   DestroyParallelJob();
 }
@@ -398,4 +405,26 @@
   task_environment_.RunUntilIdle();
 }
 
+// Interruption from IO thread after the file initialized and before building
+// the parallel requests, should correctly stop the download.
+TEST_F(ParallelDownloadJobTest, InterruptOnStartup) {
+  DownloadItem::ReceivedSlices slices = {DownloadItem::ReceivedSlice(0, 99)};
+  CreateParallelJob(99, 1, slices, 3, 1, 10);
+
+  // Start to build the requests without any error.
+  base::MockCallback<DownloadFile::InitializeCallback> callback;
+  EXPECT_CALL(callback, Run(_)).Times(1);
+  job_->MakeFileInitialized(callback.Get(), DOWNLOAD_INTERRUPT_REASON_NONE);
+
+  // Simulate and inject an error from IO thread after file initializd.
+  EXPECT_CALL(*download_item_.get(), GetLastReason())
+      .WillOnce(Return(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED));
+
+  // Because of the error, no parallel requests are built.
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(0u, job_->workers().size());
+
+  DestroyParallelJob();
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc
index 849d9c1..b44c9e5 100644
--- a/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc
+++ b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc
@@ -20,17 +20,18 @@
 void LegacyIPCFrameInputHandler::SetCompositionFromExistingText(
     int32_t start,
     int32_t end,
-    const std::vector<ui::CompositionUnderline>& ui_underlines) {
-  std::vector<blink::WebCompositionUnderline> underlines;
-  for (const auto& underline : ui_underlines) {
-    blink::WebCompositionUnderline blink_underline(
-        underline.start_offset, underline.end_offset, underline.color,
-        underline.thick, underline.background_color);
-    underlines.push_back(blink_underline);
+    const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
+  std::vector<blink::WebImeTextSpan> ime_text_spans;
+  for (const auto& ime_text_span : ui_ime_text_spans) {
+    blink::WebImeTextSpan blink_ime_text_span(
+        ime_text_span.start_offset, ime_text_span.end_offset,
+        ime_text_span.color, ime_text_span.thick,
+        ime_text_span.background_color);
+    ime_text_spans.push_back(blink_ime_text_span);
   }
 
   SendInput(base::MakeUnique<InputMsg_SetCompositionFromExistingText>(
-      routing_id_, start, end, underlines));
+      routing_id_, start, end, ime_text_spans));
 }
 
 void LegacyIPCFrameInputHandler::ExtendSelectionAndDelete(int32_t before,
diff --git a/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h
index 7a10e51..c46e6940 100644
--- a/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h
+++ b/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h
@@ -24,7 +24,7 @@
   void SetCompositionFromExistingText(
       int32_t start,
       int32_t end,
-      const std::vector<ui::CompositionUnderline>& underlines) override;
+      const std::vector<ui::ImeTextSpan>& ime_text_spans) override;
   void ExtendSelectionAndDelete(int32_t before, int32_t after) override;
   void DeleteSurroundingText(int32_t before, int32_t after) override;
   void DeleteSurroundingTextInCodePoints(int32_t before,
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index c69cae8..e3310940 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1048,7 +1048,7 @@
   set_nav_entry_id(0);
 
   if (is_audible_)
-    GetProcess()->OnAudioStreamRemoved();
+    GetProcess()->OnMediaStreamRemoved();
 }
 
 void RenderFrameHostImpl::ReportContentSecurityPolicyViolation(
@@ -1240,9 +1240,9 @@
   if (is_audible_ == is_audible)
     return;
   if (is_audible)
-    GetProcess()->OnAudioStreamAdded();
+    GetProcess()->OnMediaStreamAdded();
   else
-    GetProcess()->OnAudioStreamRemoved();
+    GetProcess()->OnMediaStreamRemoved();
   is_audible_ = is_audible;
 
   GetFrameResourceCoordinator()->SetProperty(
diff --git a/content/browser/loader/redirect_to_file_resource_handler.cc b/content/browser/loader/redirect_to_file_resource_handler.cc
index 3bddfd7..d6acbbc 100644
--- a/content/browser/loader/redirect_to_file_resource_handler.cc
+++ b/content/browser/loader/redirect_to_file_resource_handler.cc
@@ -134,11 +134,6 @@
     net::URLRequest* request)
     : LayeredResourceHandler(request, std::move(next_handler)),
       buf_(new net::GrowableIOBuffer()),
-      buf_write_pending_(false),
-      write_cursor_(0),
-      writer_(NULL),
-      next_buffer_size_(kInitialReadBufSize),
-      completed_during_write_(false),
       weak_factory_(this) {}
 
 RedirectToFileResourceHandler::~RedirectToFileResourceHandler() {
@@ -158,7 +153,12 @@
 void RedirectToFileResourceHandler::OnResponseStarted(
     ResourceResponse* response,
     std::unique_ptr<ResourceController> controller) {
-  DCHECK(writer_);
+  if (!writer_) {
+    response_pending_file_creation_ = response;
+    HoldController(std::move(controller));
+    request()->LogBlockedBy("RedirectToFileResourceHandler");
+    return;
+  }
   response->head.download_file_path = writer_->path();
   next_handler_->OnResponseStarted(response, std::move(controller));
 }
@@ -168,12 +168,7 @@
     std::unique_ptr<ResourceController> controller) {
   DCHECK(!writer_);
 
-  // Defer starting the request until we have created the temporary file.
-  // TODO(darin): This is sub-optimal.  We should not delay starting the
-  // network request like this.
-  will_start_url_ = url;
-  HoldController(std::move(controller));
-  request()->LogBlockedBy("RedirectToFileResourceHandler");
+  // Create the file ASAP but don't block.
   if (create_temporary_file_stream_.is_null()) {
     CreateTemporaryFileStream(
         base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
@@ -183,6 +178,7 @@
         base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
                    weak_factory_.GetWeakPtr()));
   }
+  next_handler_->OnWillStart(url, std::move(controller));
 }
 
 void RedirectToFileResourceHandler::OnWillRead(
@@ -252,23 +248,30 @@
     std::unique_ptr<net::FileStream> file_stream,
     ShareableFileReference* deletable_file) {
   DCHECK(!writer_);
-  DCHECK(has_controller());
   if (error_code != base::File::FILE_OK) {
-    CancelWithError(net::FileErrorToNetError(error_code));
+    if (has_controller()) {
+      CancelWithError(net::FileErrorToNetError(error_code));
+    } else {
+      OutOfBandCancel(net::FileErrorToNetError(error_code),
+                      true /* tell_renderer */);
+    }
     return;
   }
 
   writer_ = new Writer(this, std::move(file_stream), deletable_file);
 
-  // Resume the request.
-  request()->LogUnblocked();
-  next_handler_->OnWillStart(std::move(will_start_url_), ReleaseController());
+  if (response_pending_file_creation_) {
+    scoped_refptr<ResourceResponse> response =
+        std::move(response_pending_file_creation_);
+    request()->LogUnblocked();
+    OnResponseStarted(response.get(), ReleaseController());
+  }
 }
 
 void RedirectToFileResourceHandler::DidWriteToFile(int result) {
   bool failed = false;
   if (result > 0) {
-    next_handler_->OnDataDownloaded(result);
+    OnDataDownloaded(result);
     write_cursor_ += result;
     // WriteMore will resume the request if the request hasn't completed and
     // there's more buffer space.
@@ -349,7 +352,7 @@
       break;
     if (rv <= 0)
       return false;
-    next_handler_->OnDataDownloaded(rv);
+    OnDataDownloaded(rv);
     write_cursor_ += rv;
   }
 
diff --git a/content/browser/loader/redirect_to_file_resource_handler.h b/content/browser/loader/redirect_to_file_resource_handler.h
index 9f642e7..079b3ae 100644
--- a/content/browser/loader/redirect_to_file_resource_handler.h
+++ b/content/browser/loader/redirect_to_file_resource_handler.h
@@ -97,6 +97,8 @@
 
   bool BufIsFull() const;
 
+  // If populated, OnResponseStarted completion is pending on file creation.
+  scoped_refptr<ResourceResponse> response_pending_file_creation_;
   CreateTemporaryFileStreamFunction create_temporary_file_stream_;
 
   // We allocate a single, fixed-size IO buffer (buf_) used to read from the
@@ -107,24 +109,23 @@
   // tracks the offset into buf_ that we are writing to disk.
 
   scoped_refptr<net::GrowableIOBuffer> buf_;
-  bool buf_write_pending_;
-  int write_cursor_;
+  bool buf_write_pending_ = false;
+  int write_cursor_ = 0;
 
   // Helper writer object which maintains references to the net::FileStream and
   // storage::ShareableFileReference. This is maintained separately so that,
   // on Windows, the temporary file isn't deleted until after it is closed.
   class Writer;
-  Writer* writer_;
+  Writer* writer_ = nullptr;
 
   // |next_buffer_size_| is the size of the buffer to be allocated on the next
   // OnWillRead() call.  We exponentially grow the size of the buffer allocated
   // when our owner fills our buffers. On the first OnWillRead() call, we
   // allocate a buffer of 32k and double it in OnReadCompleted() if the buffer
   // was filled, up to a maximum size of 512k.
-  int next_buffer_size_;
+  int next_buffer_size_ = kInitialReadBufSize;
 
-  bool completed_during_write_;
-  GURL will_start_url_;
+  bool completed_during_write_ = false;
   net::URLRequestStatus completed_status_;
 
   base::WeakPtrFactory<RedirectToFileResourceHandler> weak_factory_;
diff --git a/content/browser/loader/redirect_to_file_resource_handler_unittest.cc b/content/browser/loader/redirect_to_file_resource_handler_unittest.cc
index aaee16d..e7074444 100644
--- a/content/browser/loader/redirect_to_file_resource_handler_unittest.cc
+++ b/content/browser/loader/redirect_to_file_resource_handler_unittest.cc
@@ -275,19 +275,24 @@
     create_file_stream_callback_ = create_file_stream_callback;
   }
 
-  // Simulates starting the request, the response starting, and stream creation
-  // completing with the specified error code. Has |test_handler_| resume the
-  // request, if needed. Returns final status of |mock_loader_|.
-  MockResourceLoader::Status StartAndCreateStream(base::File::Error file_error)
-      WARN_UNUSED_RESULT {
-    DCHECK(file_stream_);
+  void PerformOnWillStart() {
+    MockResourceLoader::Status expected_status;
+    if (GetParam() == CompletionMode::ASYNC) {
+      expected_status = MockResourceLoader::Status::CALLBACK_PENDING;
+    } else {
+      expected_status = MockResourceLoader::Status::IDLE;
+    }
+    EXPECT_EQ(expected_status, mock_loader_->OnWillStart(url_request_->url()));
+  }
 
-    EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
-              mock_loader_->OnWillStart(url_request_->url()));
+  // Sets up the file stream or error, and performs the file callback.
+  void PerformCreateFile(base::File::Error file_error) {
+    DCHECK(file_stream_);
 
     file_stream_->set_expect_closed(file_error == base::File::FILE_OK);
     if (file_error != base::File::FILE_OK)
       file_stream_ = nullptr;
+
     base::ResetAndReturn(&create_file_stream_callback_)
         .Run(file_error, std::move(file_stream_),
              // Not really used by the test, but the ResourceHandler expects it
@@ -297,6 +302,18 @@
                  storage::ShareableFileReference::DELETE_ON_FINAL_RELEASE,
                  base::ThreadTaskRunnerHandle::Get().get())
                  .get());
+  }
+
+  // Simulates starting the request, the response starting, and stream creation
+  // completing with the specified error code. Has |test_handler_| resume the
+  // request, if needed. Returns final status of |mock_loader_|.
+  MockResourceLoader::Status StartAndCreateStream(base::File::Error file_error)
+      WARN_UNUSED_RESULT {
+    PerformOnWillStart();
+
+    // Create the file right away.
+    PerformCreateFile(file_error);
+
     // If this is an async test, |test_handler_| will defer the OnWillStart
     // event on success (On error, its OnWillStart method is not called).
     if (file_error == base::File::FILE_OK &&
@@ -389,6 +406,67 @@
   CompleteRequestSuccessfully(test_data.size());
 }
 
+TEST_P(RedirectToFileResourceHandlerTest, SingleBodyReadDelayedFileOnResponse) {
+  std::string test_data = CreateTestData(kMaxInitialSyncReadSize);
+
+  PerformOnWillStart();
+  if (GetParam() == CompletionMode::ASYNC) {
+    EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
+              mock_loader_->status());
+    test_handler_->Resume();
+    mock_loader_->WaitUntilIdleOrCanceled();
+  }
+  ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
+  mock_loader_->OnResponseStarted(make_scoped_refptr(new ResourceResponse()));
+  ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
+            mock_loader_->status());
+
+  PerformCreateFile(base::File::FILE_OK);
+
+  if (GetParam() == CompletionMode::ASYNC) {
+    EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
+              mock_loader_->status());
+    test_handler_->Resume();
+    mock_loader_->WaitUntilIdleOrCanceled();
+  }
+  EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
+
+  ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
+  ASSERT_EQ(MockResourceLoader::Status::IDLE,
+            mock_loader_->OnReadCompleted(test_data));
+  // Wait for the write to complete, in the async case.
+  base::RunLoop().RunUntilIdle();
+
+  CompleteRequestSuccessfully(test_data.size());
+}
+
+TEST_P(RedirectToFileResourceHandlerTest, SingleBodyReadDelayedFileError) {
+  std::string test_data = CreateTestData(0);
+
+  PerformOnWillStart();
+  if (GetParam() == CompletionMode::ASYNC) {
+    EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
+              mock_loader_->status());
+    test_handler_->Resume();
+    mock_loader_->WaitUntilIdleOrCanceled();
+  }
+  ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
+  mock_loader_->OnResponseStarted(make_scoped_refptr(new ResourceResponse()));
+  ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
+            mock_loader_->status());
+
+  PerformCreateFile(base::File::FILE_ERROR_FAILED);
+
+  EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
+  EXPECT_EQ(0, test_handler_->on_response_completed_called());
+  EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code());
+  ASSERT_EQ(MockResourceLoader::Status::IDLE,
+            mock_loader_->OnResponseCompleted(
+                net::URLRequestStatus::FromError(net::ERR_FAILED)));
+  EXPECT_FALSE(test_handler_->final_status().is_success());
+  EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error());
+}
+
 TEST_P(RedirectToFileResourceHandlerTest, ManySequentialBodyReads) {
   const size_t kBytesPerRead = 128;
   std::string test_data =
@@ -757,9 +835,17 @@
 
   EXPECT_EQ(0, test_handler_->on_response_completed_called());
   EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code());
-  ASSERT_EQ(MockResourceLoader::Status::IDLE,
-            mock_loader_->OnResponseCompleted(
-                net::URLRequestStatus::FromError(net::ERR_FAILED)));
+  if (GetParam() == CompletionMode::ASYNC) {
+    EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status());
+    ASSERT_EQ(MockResourceLoader::Status::IDLE,
+              mock_loader_->OnResponseCompletedFromExternalOutOfBandCancel(
+                  net::URLRequestStatus::FromError(net::ERR_FAILED)));
+  } else {
+    ASSERT_EQ(MockResourceLoader::Status::IDLE,
+              mock_loader_->OnResponseCompleted(
+                  net::URLRequestStatus::FromError(net::ERR_FAILED)));
+  }
+
   EXPECT_EQ(0, test_handler_->total_bytes_downloaded());
   EXPECT_FALSE(test_handler_->final_status().is_success());
   EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error());
diff --git a/content/browser/media/media_browsertest.cc b/content/browser/media/media_browsertest.cc
index 1345092..239bfb2 100644
--- a/content/browser/media/media_browsertest.cc
+++ b/content/browser/media/media_browsertest.cc
@@ -241,15 +241,6 @@
 IN_PROC_BROWSER_TEST_F(MediaTest, VideoBearRotated270) {
   RunVideoSizeTest("bear_rotate_270.mp4", 720, 1280);
 }
-
-// Android can't reliably load lots of videos on a page.
-// See http://crbug.com/749265
-#if !defined(OS_ANDROID)
-IN_PROC_BROWSER_TEST_F(MediaTest, LoadManyVideos) {
-  base::StringPairs query_params;
-  RunMediaTestPage("load_many_videos.html", query_params, kEnded, true);
-}
-#endif  // !defined(OS_ANDROID)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 #if defined(OS_CHROMEOS)
diff --git a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc
index 12f039c..282843f 100644
--- a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc
+++ b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc
@@ -13,15 +13,16 @@
 namespace content {
 
 namespace {
-std::vector<blink::WebCompositionUnderline> ConvertToBlinkUnderline(
-    const std::vector<ui::CompositionUnderline>& ui_underlines) {
-  std::vector<blink::WebCompositionUnderline> underlines;
-  for (const auto& underline : ui_underlines) {
-    underlines.emplace_back(blink::WebCompositionUnderline(
-        underline.start_offset, underline.end_offset, underline.color,
-        underline.thick, underline.background_color));
+std::vector<blink::WebImeTextSpan> ConvertToBlinkImeTextSpan(
+    const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
+  std::vector<blink::WebImeTextSpan> ime_text_spans;
+  for (const auto& ime_text_span : ui_ime_text_spans) {
+    ime_text_spans.emplace_back(blink::WebImeTextSpan(
+        ime_text_span.start_offset, ime_text_span.end_offset,
+        ime_text_span.color, ime_text_span.thick,
+        ime_text_span.background_color));
   }
-  return underlines;
+  return ime_text_spans;
 }
 
 }  // namespace
@@ -29,7 +30,6 @@
 LegacyIPCWidgetInputHandler::LegacyIPCWidgetInputHandler(
     LegacyInputRouterImpl* input_router)
     : input_router_(input_router) {}
-
 LegacyIPCWidgetInputHandler::~LegacyIPCWidgetInputHandler() {}
 
 void LegacyIPCWidgetInputHandler::SetFocus(bool focused) {
@@ -52,25 +52,25 @@
 
 void LegacyIPCWidgetInputHandler::ImeSetComposition(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& ui_underlines,
+    const std::vector<ui::ImeTextSpan>& ui_ime_text_spans,
     const gfx::Range& range,
     int32_t start,
     int32_t end) {
-  std::vector<blink::WebCompositionUnderline> underlines =
-      ConvertToBlinkUnderline(ui_underlines);
+  std::vector<blink::WebImeTextSpan> ime_text_spans =
+      ConvertToBlinkImeTextSpan(ui_ime_text_spans);
   SendInput(base::MakeUnique<InputMsg_ImeSetComposition>(
-      input_router_->routing_id(), text, underlines, range, start, end));
+      input_router_->routing_id(), text, ime_text_spans, range, start, end));
 }
 
 void LegacyIPCWidgetInputHandler::ImeCommitText(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& ui_underlines,
+    const std::vector<ui::ImeTextSpan>& ui_ime_text_spans,
     const gfx::Range& range,
     int32_t relative_cursor_position) {
-  std::vector<blink::WebCompositionUnderline> underlines =
-      ConvertToBlinkUnderline(ui_underlines);
+  std::vector<blink::WebImeTextSpan> ime_text_spans =
+      ConvertToBlinkImeTextSpan(ui_ime_text_spans);
   SendInput(base::MakeUnique<InputMsg_ImeCommitText>(
-      input_router_->routing_id(), text, underlines, range,
+      input_router_->routing_id(), text, ime_text_spans, range,
       relative_cursor_position));
 }
 
diff --git a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h
index 9bc8f0c..7bfd9e7 100644
--- a/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h
+++ b/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h
@@ -27,14 +27,13 @@
   void SetEditCommandsForNextKeyEvent(
       const std::vector<EditCommand>& commands) override;
   void CursorVisibilityChanged(bool visible) override;
-  void ImeSetComposition(
-      const base::string16& text,
-      const std::vector<ui::CompositionUnderline>& underlines,
-      const gfx::Range& range,
-      int32_t start,
-      int32_t end) override;
+  void ImeSetComposition(const base::string16& text,
+                         const std::vector<ui::ImeTextSpan>& ime_text_spans,
+                         const gfx::Range& range,
+                         int32_t start,
+                         int32_t end) override;
   void ImeCommitText(const base::string16& text,
-                     const std::vector<ui::CompositionUnderline>& underlines,
+                     const std::vector<ui::ImeTextSpan>& ime_text_spans,
                      const gfx::Range& range,
                      int32_t relative_cursor_position) override;
   void ImeFinishComposingText(bool keep_selection) override;
diff --git a/content/browser/renderer_host/input/mock_widget_input_handler.cc b/content/browser/renderer_host/input/mock_widget_input_handler.cc
index df93838..5e9c0f7 100644
--- a/content/browser/renderer_host/input/mock_widget_input_handler.cc
+++ b/content/browser/renderer_host/input/mock_widget_input_handler.cc
@@ -32,14 +32,14 @@
 
 void MockWidgetInputHandler::ImeSetComposition(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& range,
     int32_t start,
     int32_t end) {}
 
 void MockWidgetInputHandler::ImeCommitText(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& range,
     int32_t relative_cursor_position) {}
 
diff --git a/content/browser/renderer_host/input/mock_widget_input_handler.h b/content/browser/renderer_host/input/mock_widget_input_handler.h
index 8eb481b..adae4d3 100644
--- a/content/browser/renderer_host/input/mock_widget_input_handler.h
+++ b/content/browser/renderer_host/input/mock_widget_input_handler.h
@@ -36,14 +36,13 @@
   void SetEditCommandsForNextKeyEvent(
       const std::vector<content::EditCommand>& commands) override;
   void CursorVisibilityChanged(bool visible) override;
-  void ImeSetComposition(
-      const base::string16& text,
-      const std::vector<ui::CompositionUnderline>& underlines,
-      const gfx::Range& range,
-      int32_t start,
-      int32_t end) override;
+  void ImeSetComposition(const base::string16& text,
+                         const std::vector<ui::ImeTextSpan>& ime_text_spans,
+                         const gfx::Range& range,
+                         int32_t start,
+                         int32_t end) override;
   void ImeCommitText(const base::string16& text,
-                     const std::vector<ui::CompositionUnderline>& underlines,
+                     const std::vector<ui::ImeTextSpan>& ime_text_spans,
                      const gfx::Range& range,
                      int32_t relative_cursor_position) override;
   void ImeFinishComposingText(bool keep_selection) override;
diff --git a/content/browser/renderer_host/media/audio_input_device_manager.cc b/content/browser/renderer_host/media/audio_input_device_manager.cc
index 876e113..581e2b1 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager.cc
+++ b/content/browser/renderer_host/media/audio_input_device_manager.cc
@@ -168,25 +168,13 @@
   UMA_HISTOGRAM_TIMES("Media.AudioInputDeviceManager.OpenOnDeviceThreadTime",
                       base::TimeTicks::Now() - start_time);
 
-  media::AudioParameters valid_input_params =
-      input_params.IsValid()
-          ? input_params
-          : media::AudioParameters::UnavailableDeviceParams();
-
   StreamDeviceInfo info(device.type, device.name, device.id);
   info.session_id = session_id;
-  info.device.input.sample_rate = valid_input_params.sample_rate();
-  info.device.input.channel_layout = valid_input_params.channel_layout();
-  info.device.input.frames_per_buffer = valid_input_params.frames_per_buffer();
-  info.device.input.effects = valid_input_params.effects();
-  info.device.input.mic_positions = valid_input_params.mic_positions();
+  info.device.input = input_params.IsValid()
+                          ? input_params
+                          : media::AudioParameters::UnavailableDeviceParams();
   info.device.matched_output_device_id = matched_output_device_id;
-  info.device.matched_output.sample_rate = matched_output_params.sample_rate();
-  info.device.matched_output.channel_layout =
-      matched_output_params.channel_layout();
-  info.device.matched_output.frames_per_buffer =
-      matched_output_params.frames_per_buffer();
-  info.device.matched_output.effects = matched_output_params.effects();
+  info.device.matched_output = matched_output_params;
 
   devices_.push_back(info);
 
diff --git a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
index cfb976b..e0ce863 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
@@ -343,10 +343,9 @@
         manager_->GetOpenedDeviceInfoById(session_id);
     EXPECT_TRUE(
         media::AudioParameters(media::AudioParameters::AUDIO_FAKE,
-                               static_cast<media::ChannelLayout>(
-                                   device_info->device.input.channel_layout),
-                               device_info->device.input.sample_rate, 16,
-                               device_info->device.input.frames_per_buffer)
+                               device_info->device.input.channel_layout(),
+                               device_info->device.input.sample_rate(), 16,
+                               device_info->device.input.frames_per_buffer())
             .IsValid());
 
     manager_->Close(session_id);
diff --git a/content/browser/renderer_host/media/audio_output_authorization_handler.cc b/content/browser/renderer_host/media/audio_output_authorization_handler.cc
index 9bdfbc9..b19c9978 100644
--- a/content/browser/renderer_host/media/audio_output_authorization_handler.cc
+++ b/content/browser/renderer_host/media/audio_output_authorization_handler.cc
@@ -131,11 +131,10 @@
     if (info && !info->device.matched_output_device_id.empty()) {
       media::AudioParameters params(
           media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
-          static_cast<media::ChannelLayout>(
-              info->device.matched_output.channel_layout),
-          info->device.matched_output.sample_rate, 16,
-          info->device.matched_output.frames_per_buffer);
-      params.set_effects(info->device.matched_output.effects);
+          info->device.matched_output.channel_layout(),
+          info->device.matched_output.sample_rate(), 16,
+          info->device.matched_output.frames_per_buffer());
+      params.set_effects(info->device.matched_output.effects());
 
       // We don't need the origin for authorization in this case, but it's used
       // for hashing the device id before sending it back to the renderer.
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 661136c6..6f353fa3 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -1175,10 +1175,10 @@
           *existing_device_info = device_info;
           // Make sure that the audio |effects| reflect what the request
           // is set to and not what the capabilities are.
-          FilterAudioEffects(request->controls,
-                             &existing_device_info->device.input.effects);
-          EnableHotwordEffect(request->controls,
-                              &existing_device_info->device.input.effects);
+          int effects = existing_device_info->device.input.effects();
+          FilterAudioEffects(request->controls, &effects);
+          EnableHotwordEffect(request->controls, &effects);
+          existing_device_info->device.input.set_effects(effects);
           *existing_request_state = request->state(device_info.device.type);
           return true;
         }
@@ -1308,6 +1308,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DVLOG(1) << "Opened({stream_type = " << stream_type <<  "} "
            << "{capture_session_id = " << capture_session_id << "})";
+
   // Find the request(s) containing this device and mark it as used.
   // It can be used in several requests since the same device can be
   // requested from the same web page.
@@ -1336,10 +1337,10 @@
             // parameters to the default settings (including supported effects),
             // we need to adjust those settings here according to what the
             // request asks for.
-            FilterAudioEffects(request->controls,
-                               &device_info.device.input.effects);
-            EnableHotwordEffect(request->controls,
-                                &device_info.device.input.effects);
+            int effects = device_info.device.input.effects();
+            FilterAudioEffects(request->controls, &effects);
+            EnableHotwordEffect(request->controls, &effects);
+            device_info.device.input.set_effects(effects);
 
             device_info.device.matched_output = info->device.matched_output;
           }
@@ -1504,8 +1505,13 @@
       if (sample_rate <= 0 || sample_rate > 96000)
         sample_rate = 44100;
 
-      device_info.device.input.sample_rate = sample_rate;
-      device_info.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO;
+      media::AudioParameters params(
+          device_info.device.input.format(), media::CHANNEL_LAYOUT_STEREO,
+          sample_rate, device_info.device.input.bits_per_sample(),
+          device_info.device.input.frames_per_buffer());
+      params.set_effects(device_info.device.input.effects());
+      params.set_mic_positions(device_info.device.input.mic_positions());
+      device_info.device.input = params;
     }
 
     if (device_info.device.type == request->audio_type())
diff --git a/content/browser/renderer_host/media/video_capture_host.cc b/content/browser/renderer_host/media/video_capture_host.cc
index 9c1a673f..d9a2b4e 100644
--- a/content/browser/renderer_host/media/video_capture_host.cc
+++ b/content/browser/renderer_host/media/video_capture_host.cc
@@ -12,27 +12,78 @@
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
+#include "content/public/browser/render_process_host.h"
 #include "mojo/common/values_struct_traits.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace content {
 
-VideoCaptureHost::VideoCaptureHost(MediaStreamManager* media_stream_manager)
-    : media_stream_manager_(media_stream_manager),
+// Helper class that does all the work of notifying RenderProcessHost instance
+// about active video capture stream changes. It should be called and destroyed
+// on UI thread.
+class VideoCaptureHost::RenderProcessHostDelegate {
+ public:
+  explicit RenderProcessHostDelegate(int render_process_id)
+      : render_process_id_(render_process_id) {}
+
+  ~RenderProcessHostDelegate() { DCHECK_CURRENTLY_ON(BrowserThread::UI); }
+
+  // Helper functions that are used for notifying Browser-side RenderProcessHost
+  // if renderer is currently consuming video capture. This information is then
+  // used to determine if the renderer process should be backgrounded or not.
+  void NotifyStreamAdded() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    ++number_of_active_streams_;
+    RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_);
+    if (host)
+      host->OnMediaStreamAdded();
+  }
+
+  void NotifyStreamRemoved() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    // DoError() from camera side failure can be followed by Stop() from JS
+    // side, so we should check before going to negative.
+    // TODO(emircan): Investigate all edge cases and add more browsertests.
+    // https://crbug.com/754765
+    if (number_of_active_streams_ == 0)
+      return;
+    --number_of_active_streams_;
+    RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_);
+    if (host)
+      host->OnMediaStreamRemoved();
+  }
+
+  void NotifyAllStreamsRemoved() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    for (uint32_t i = 0; i < number_of_active_streams_; ++i)
+      NotifyStreamRemoved();
+  }
+
+ private:
+  const int render_process_id_;
+  uint32_t number_of_active_streams_ = 0;
+  DISALLOW_COPY_AND_ASSIGN(RenderProcessHostDelegate);
+};
+
+VideoCaptureHost::VideoCaptureHost(int render_process_id,
+                                   MediaStreamManager* media_stream_manager)
+    : render_process_host_delegate_(
+          new RenderProcessHostDelegate(render_process_id)),
+      media_stream_manager_(media_stream_manager),
       weak_factory_(this) {
   DVLOG(1) << __func__;
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 // static
-void VideoCaptureHost::Create(
-    MediaStreamManager* media_stream_manager,
-    mojom::VideoCaptureHostRequest request) {
+void VideoCaptureHost::Create(int render_process_id,
+                              MediaStreamManager* media_stream_manager,
+                              mojom::VideoCaptureHostRequest request) {
   DVLOG(1) << __func__;
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  mojo::MakeStrongBinding(
-      base::MakeUnique<VideoCaptureHost>(media_stream_manager),
-      std::move(request));
+  mojo::MakeStrongBinding(base::MakeUnique<VideoCaptureHost>(
+                              render_process_id, media_stream_manager),
+                          std::move(request));
 }
 
 VideoCaptureHost::~VideoCaptureHost() {
@@ -50,6 +101,15 @@
       controllers_.erase(it++);
     }
   }
+
+  // base::Unretained() usage is safe because |render_process_host_delegate_| is
+  // destroyed on UI thread.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&RenderProcessHostDelegate::NotifyAllStreamsRemoved,
+                 base::Unretained(render_process_host_delegate_.get())));
+  BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE,
+                            render_process_host_delegate_.release());
 }
 
 void VideoCaptureHost::OnError(VideoCaptureControllerID controller_id) {
@@ -118,6 +178,12 @@
   if (base::ContainsKey(device_id_to_observer_map_, controller_id)) {
     device_id_to_observer_map_[controller_id]->OnStateChanged(
         mojom::VideoCaptureState::STARTED);
+    // base::Unretained() usage is safe because |render_process_host_delegate_|
+    // is destroyed on UI thread.
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&RenderProcessHostDelegate::NotifyStreamAdded,
+                   base::Unretained(render_process_host_delegate_.get())));
   }
 }
 
@@ -139,6 +205,12 @@
   if (controllers_.find(controller_id) != controllers_.end()) {
     device_id_to_observer_map_[device_id]->OnStateChanged(
         mojom::VideoCaptureState::STARTED);
+    // base::Unretained() usage is safe because |render_process_host_delegate_|
+    // is destroyed on UI thread.
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&RenderProcessHostDelegate::NotifyStreamAdded,
+                   base::Unretained(render_process_host_delegate_.get())));
     return;
   }
 
@@ -162,6 +234,12 @@
   device_id_to_observer_map_.erase(controller_id);
 
   DeleteVideoCaptureController(controller_id, false);
+  // base::Unretained() usage is safe because |render_process_host_delegate_| is
+  // destroyed on UI thread.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&RenderProcessHostDelegate::NotifyStreamRemoved,
+                 base::Unretained(render_process_host_delegate_.get())));
 }
 
 void VideoCaptureHost::Pause(int32_t device_id) {
@@ -272,6 +350,12 @@
   }
 
   DeleteVideoCaptureController(controller_id, true);
+  // base::Unretained() usage is safe because |render_process_host_delegate_| is
+  // destroyed on UI thread.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&RenderProcessHostDelegate::NotifyStreamRemoved,
+                 base::Unretained(render_process_host_delegate_.get())));
 }
 
 void VideoCaptureHost::DoEnded(VideoCaptureControllerID controller_id) {
@@ -286,6 +370,12 @@
   }
 
   DeleteVideoCaptureController(controller_id, false);
+  // base::Unretained() usage is safe because |render_process_host_delegate_| is
+  // destroyed on UI thread.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&RenderProcessHostDelegate::NotifyStreamRemoved,
+                 base::Unretained(render_process_host_delegate_.get())));
 }
 
 void VideoCaptureHost::OnControllerAdded(
diff --git a/content/browser/renderer_host/media/video_capture_host.h b/content/browser/renderer_host/media/video_capture_host.h
index 9669c35..d9ad77c 100644
--- a/content/browser/renderer_host/media/video_capture_host.h
+++ b/content/browser/renderer_host/media/video_capture_host.h
@@ -28,9 +28,11 @@
     : public VideoCaptureControllerEventHandler,
       public mojom::VideoCaptureHost {
  public:
-  explicit VideoCaptureHost(MediaStreamManager* media_stream_manager);
+  VideoCaptureHost(int render_process_id,
+                   MediaStreamManager* media_stream_manager);
 
-  static void Create(MediaStreamManager* media_stream_manager,
+  static void Create(int render_process_id,
+                     MediaStreamManager* media_stream_manager,
                      mojom::VideoCaptureHostRequest request);
 
   ~VideoCaptureHost() override;
@@ -90,6 +92,9 @@
   void DeleteVideoCaptureController(VideoCaptureControllerID controller_id,
                                     bool on_error);
 
+  class RenderProcessHostDelegate;
+  std::unique_ptr<RenderProcessHostDelegate> render_process_host_delegate_;
+
   MediaStreamManager* const media_stream_manager_;
 
   // A map of VideoCaptureControllerID to the VideoCaptureController to which it
diff --git a/content/browser/renderer_host/media/video_capture_unittest.cc b/content/browser/renderer_host/media/video_capture_unittest.cc
index c2060af0..f87d522 100644
--- a/content/browser/renderer_host/media/video_capture_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_unittest.cc
@@ -134,7 +134,8 @@
         base::MakeUnique<MediaStreamManager>(audio_system_.get());
 
     // Create a Host and connect it to a simulated IPC channel.
-    host_.reset(new VideoCaptureHost(media_stream_manager_.get()));
+    host_.reset(new VideoCaptureHost(0 /* render_process_id */,
+                                     media_stream_manager_.get()));
 
     OpenSession();
   }
diff --git a/content/browser/renderer_host/render_process_host_browsertest.cc b/content/browser/renderer_host/render_process_host_browsertest.cc
index 4358501..019a08c 100644
--- a/content/browser/renderer_host/render_process_host_browsertest.cc
+++ b/content/browser/renderer_host/render_process_host_browsertest.cc
@@ -14,7 +14,9 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/test_service.mojom.h"
@@ -55,6 +57,10 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(switches::kIgnoreAutoplayRestrictionsForTests);
+    // These flags are necessary to emulate camera input for getUserMedia()
+    // tests.
+    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
+    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
   }
 
  protected:
@@ -474,7 +480,7 @@
     run_loop.Run();
 
     // No point in running the rest of the test if this is wrong.
-    ASSERT_EQ(1, rph->get_audio_stream_count_for_testing());
+    ASSERT_EQ(1, rph->get_media_stream_count_for_testing());
   }
 
   host_destructions_ = 0;
@@ -505,7 +511,85 @@
   }
 
   // Verify shutdown went as expected.
-  EXPECT_EQ(0, rph->get_audio_stream_count_for_testing());
+  EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
+  EXPECT_EQ(1, process_exits_);
+  EXPECT_EQ(0, host_destructions_);
+  if (!host_destructions_)
+    rph->RemoveObserver(this);
+}
+
+// Tests that video capture stream count increments when getUserMedia() is
+// called.
+IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
+                       GetUserMediaIncrementsVideoCaptureStreams) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  NavigateToURL(shell(),
+                embedded_test_server()->GetURL("/media/getusermedia.html"));
+  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
+      shell()->web_contents()->GetMainFrame()->GetProcess());
+  std::string result;
+  EXPECT_TRUE(ExecuteScriptAndExtractString(
+      shell(), "getUserMediaAndExpectSuccess({video: true});", &result))
+      << "Failed to execute javascript.";
+  EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
+}
+
+// Tests that video capture stream count resets when getUserMedia() is called
+// and stopped.
+IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, StopResetsVideoCaptureStreams) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  NavigateToURL(shell(),
+                embedded_test_server()->GetURL("/media/getusermedia.html"));
+  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
+      shell()->web_contents()->GetMainFrame()->GetProcess());
+  std::string result;
+  EXPECT_TRUE(ExecuteScriptAndExtractString(
+      shell(), "getUserMediaAndStop({video: true});", &result))
+      << "Failed to execute javascript.";
+  EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
+}
+
+// Tests that video capture stream counts (used for process priority
+// calculations) are properly set and cleared during media playback and renderer
+// terminations.
+IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
+                       KillProcessZerosVideoCaptureStreams) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  NavigateToURL(shell(),
+                embedded_test_server()->GetURL("/media/getusermedia.html"));
+  RenderProcessHostImpl* rph = static_cast<RenderProcessHostImpl*>(
+      shell()->web_contents()->GetMainFrame()->GetProcess());
+  std::string result;
+  EXPECT_TRUE(ExecuteScriptAndExtractString(
+      shell(), "getUserMediaAndExpectSuccess({video: true});", &result))
+      << "Failed to execute javascript.";
+  EXPECT_EQ(1, rph->get_media_stream_count_for_testing());
+
+  host_destructions_ = 0;
+  process_exits_ = 0;
+  rph->AddObserver(this);
+
+  mojom::TestServicePtr service;
+  BindInterface(rph, &service);
+
+  {
+    // Force a bad message event to occur which will terminate the renderer.
+    base::RunLoop run_loop;
+    set_process_exit_callback(media::BindToCurrentLoop(run_loop.QuitClosure()));
+    service->DoSomething(base::Bind(&base::DoNothing));
+    run_loop.Run();
+  }
+
+  {
+    // Cycle UI and IO loop once to ensure OnChannelClosing() has been delivered
+    // to audio stream owners and they get a chance to notify of stream closure.
+    base::RunLoop run_loop;
+    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                            media::BindToCurrentLoop(run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(0, rph->get_media_stream_count_for_testing());
   EXPECT_EQ(1, process_exits_);
   EXPECT_EQ(0, host_destructions_);
   if (!host_destructions_)
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 503f889..5622f6b 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -803,6 +803,12 @@
 // sites are hosted by a RenderProcessHost. This class is meant to help reusing
 // RenderProcessHosts among SiteInstances, not to perform security checks for a
 // RenderProcessHost.
+//
+// TODO(alexmos): Currently, the tracking in this class and in
+// UnmatchedServiceWorkerProcessTracker is associated with a BrowserContext,
+// but it needs to also consider StoragePartitions, so that process reuse is
+// allowed only within the same StoragePartition.  For now, the tracking is
+// done only for the default StoragePartition.  See https://crbug.com/752667.
 const void* const kCommittedSiteProcessCountTrackerKey =
     "CommittedSiteProcessCountTrackerKey";
 const void* const kPendingSiteProcessCountTrackerKey =
@@ -898,6 +904,37 @@
   CountPerProcessPerSiteMap map_;
 };
 
+bool ShouldUseSiteProcessTracking(BrowserContext* browser_context,
+                                  StoragePartition* dest_partition,
+                                  const GURL& site_url) {
+  if (site_url.is_empty())
+    return false;
+
+  // TODO(alexmos): Sites should be tracked separately for each
+  // StoragePartition.  For now, track them only in the default one.
+  StoragePartition* default_partition =
+      BrowserContext::GetDefaultStoragePartition(browser_context);
+  if (dest_partition != default_partition)
+    return false;
+
+  return true;
+}
+
+bool ShouldTrackProcessForSite(BrowserContext* browser_context,
+                               RenderProcessHost* render_process_host,
+                               const GURL& site_url) {
+  return ShouldUseSiteProcessTracking(
+      browser_context, render_process_host->GetStoragePartition(), site_url);
+}
+
+bool ShouldFindReusableProcessHostForSite(BrowserContext* browser_context,
+                                          const GURL& site_url) {
+  return ShouldUseSiteProcessTracking(
+      browser_context,
+      BrowserContext::GetStoragePartitionForSite(browser_context, site_url),
+      site_url);
+}
+
 const void* const kUnmatchedServiceWorkerProcessTrackerKey =
     "UnmatchedServiceWorkerProcessTrackerKey";
 
@@ -910,6 +947,12 @@
 // processes are reused for a navigation to a matching site. After a single
 // matching navigation is put into the process, all service workers for that
 // site in that process are considered 'matched.'
+//
+// TODO(alexmos): Currently, the tracking in this class and in
+// SiteProcessCountTracker is associated with a BrowserContext, but it needs to
+// also consider StoragePartitions, so that process reuse is allowed only
+// within the same StoragePartition.  For now, the tracking is done only for
+// the default StoragePartition.  See https://crbug.com/752667.
 class UnmatchedServiceWorkerProcessTracker
     : public base::SupportsUserData::Data,
       public RenderProcessHostObserver {
@@ -920,6 +963,10 @@
                        RenderProcessHost* render_process_host,
                        const GURL& site_url) {
     DCHECK(!site_url.is_empty());
+    if (!ShouldTrackProcessForSite(browser_context, render_process_host,
+                                   site_url))
+      return;
+
     UnmatchedServiceWorkerProcessTracker* tracker =
         static_cast<UnmatchedServiceWorkerProcessTracker*>(
             browser_context->GetUserData(
@@ -936,8 +983,9 @@
   // the process from the tracker if it exists.
   static RenderProcessHost* MatchWithSite(BrowserContext* browser_context,
                                           const GURL& site_url) {
-    if (site_url.is_empty())
+    if (!ShouldFindReusableProcessHostForSite(browser_context, site_url))
       return nullptr;
+
     UnmatchedServiceWorkerProcessTracker* tracker =
         static_cast<UnmatchedServiceWorkerProcessTracker*>(
             browser_context->GetUserData(
@@ -1866,7 +1914,7 @@
                                     base::Unretained(this)));
 
   registry->AddInterface(
-      base::Bind(&VideoCaptureHost::Create,
+      base::Bind(&VideoCaptureHost::Create, GetID(),
                  BrowserMainLoop::GetInstance()->media_stream_manager()));
 
   registry->AddInterface(
@@ -1986,7 +2034,7 @@
 }
 
 void RenderProcessHostImpl::BindSharedBitmapAllocationNotifier(
-    cc::mojom::SharedBitmapAllocationNotifierRequest request) {
+    viz::mojom::SharedBitmapAllocationNotifierRequest request) {
   shared_bitmap_allocation_notifier_impl_.Bind(std::move(request));
 }
 
@@ -2240,14 +2288,14 @@
   return audio_output_stream_factory_context_.get();
 }
 
-void RenderProcessHostImpl::OnAudioStreamAdded() {
-  ++audio_stream_count_;
+void RenderProcessHostImpl::OnMediaStreamAdded() {
+  ++media_stream_count_;
   UpdateProcessPriority();
 }
 
-void RenderProcessHostImpl::OnAudioStreamRemoved() {
-  DCHECK_GT(audio_stream_count_, 0);
-  --audio_stream_count_;
+void RenderProcessHostImpl::OnMediaStreamRemoved() {
+  DCHECK_GT(media_stream_count_, 0);
+  --media_stream_count_;
   UpdateProcessPriority();
 }
 
@@ -2261,7 +2309,8 @@
     BrowserContext* browser_context,
     RenderProcessHost* render_process_host,
     const GURL& site_url) {
-  if (site_url.is_empty())
+  if (!ShouldTrackProcessForSite(browser_context, render_process_host,
+                                 site_url))
     return;
 
   SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>(
@@ -2279,7 +2328,8 @@
     BrowserContext* browser_context,
     RenderProcessHost* render_process_host,
     const GURL& site_url) {
-  if (site_url.is_empty())
+  if (!ShouldTrackProcessForSite(browser_context, render_process_host,
+                                 site_url))
     return;
 
   SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>(
@@ -2297,7 +2347,8 @@
     BrowserContext* browser_context,
     RenderProcessHost* render_process_host,
     const GURL& site_url) {
-  if (site_url.is_empty())
+  if (!ShouldTrackProcessForSite(browser_context, render_process_host,
+                                 site_url))
     return;
 
   SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>(
@@ -2315,7 +2366,8 @@
     BrowserContext* browser_context,
     RenderProcessHost* render_process_host,
     const GURL& site_url) {
-  if (site_url.is_empty())
+  if (!ShouldTrackProcessForSite(browser_context, render_process_host,
+                                 site_url))
     return;
 
   SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>(
@@ -3707,11 +3759,11 @@
     return;
   }
 
-  // We background a process as soon as it hosts no active audio streams and no
-  // visible widgets -- the callers must call this function whenever we
+  // We background a process as soon as it hosts no active audio/video streams
+  // and no visible widgets -- the callers must call this function whenever we
   // transition in/out of those states.
   const bool should_background =
-      visible_widgets_ == 0 && audio_stream_count_ == 0 &&
+      visible_widgets_ == 0 && media_stream_count_ == 0 &&
       !base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableRendererBackgrounding);
   const bool should_background_changed =
@@ -3889,7 +3941,7 @@
 RenderProcessHost* RenderProcessHostImpl::FindReusableProcessHostForSite(
     BrowserContext* browser_context,
     const GURL& site_url) {
-  if (site_url.is_empty())
+  if (!ShouldFindReusableProcessHostForSite(browser_context, site_url))
     return nullptr;
 
   std::set<RenderProcessHost*> eligible_foreground_hosts;
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index ede5f06..ddadb7f 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -334,11 +334,11 @@
   RendererAudioOutputStreamFactoryContext*
   GetRendererAudioOutputStreamFactoryContext() override;
 
-  // Called when an audio stream is added or removed and used to determine if
-  // the process should be backgrounded or not.
-  void OnAudioStreamAdded() override;
-  void OnAudioStreamRemoved() override;
-  int get_audio_stream_count_for_testing() const { return audio_stream_count_; }
+  // Called when a video capture stream or an audio stream is added or removed
+  // and used to determine if the process should be backgrounded or not.
+  void OnMediaStreamAdded() override;
+  void OnMediaStreamRemoved() override;
+  int get_media_stream_count_for_testing() const { return media_stream_count_; }
 
   // Sets the global factory used to create new RenderProcessHosts.  It may be
   // nullptr, in which case the default RenderProcessHost will be created (this
@@ -439,7 +439,7 @@
       blink::mojom::OffscreenCanvasProviderRequest request);
   void BindFrameSinkProvider(mojom::FrameSinkProviderRequest request);
   void BindSharedBitmapAllocationNotifier(
-      cc::mojom::SharedBitmapAllocationNotifierRequest request);
+      viz::mojom::SharedBitmapAllocationNotifierRequest request);
   void CreateStoragePartitionService(
       mojom::StoragePartitionServiceRequest request);
   void CreateRendererHost(mojom::RendererHostRequest request);
@@ -738,9 +738,9 @@
   mojom::RendererAssociatedPtr renderer_interface_;
   mojo::Binding<mojom::RendererHost> renderer_host_binding_;
 
-  // Tracks active audio streams within the render process; used to determine if
-  // if a process should be backgrounded.
-  int audio_stream_count_ = 0;
+  // Tracks active audio and video streams within the render process; used to
+  // determine if if a process should be backgrounded.
+  int media_stream_count_ = 0;
 
   std::unique_ptr<resource_coordinator::ResourceCoordinatorInterface>
       process_resource_coordinator_;
diff --git a/content/browser/renderer_host/render_process_host_unittest.cc b/content/browser/renderer_host/render_process_host_unittest.cc
index c5db4da..7952100 100644
--- a/content/browser/renderer_host/render_process_host_unittest.cc
+++ b/content/browser/renderer_host/render_process_host_unittest.cc
@@ -754,6 +754,97 @@
   EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess());
 }
 
+// Helper test class to modify the StoragePartition returned for a particular
+// site URL.
+class StoragePartitionContentBrowserClient : public ContentBrowserClient {
+ public:
+  StoragePartitionContentBrowserClient(const GURL& site,
+                                       const std::string& partition_domain,
+                                       const std::string& partition_name)
+      : site_(site),
+        partition_domain_(partition_domain),
+        partition_name_(partition_name) {}
+  ~StoragePartitionContentBrowserClient() override {}
+
+ private:
+  void GetStoragePartitionConfigForSite(BrowserContext* browser_context,
+                                        const GURL& site,
+                                        bool can_be_default,
+                                        std::string* partition_domain,
+                                        std::string* partition_name,
+                                        bool* in_memory) override {
+    partition_domain->clear();
+    partition_name->clear();
+    *in_memory = false;
+
+    if (site == site_) {
+      *partition_domain = partition_domain_;
+      *partition_name = partition_name_;
+    }
+  }
+
+  GURL site_;
+  std::string partition_domain_;
+  std::string partition_name_;
+};
+
+// Check that a SiteInstance cannot reuse a RenderProcessHost in a different
+// StoragePartition.
+TEST_F(RenderProcessHostUnitTest,
+       DoNotReuseProcessInDifferentStoragePartition) {
+  const GURL kUrl("https://foo.com");
+  NavigateAndCommit(kUrl);
+
+  // Change foo.com SiteInstances to use a different StoragePartition.
+  StoragePartitionContentBrowserClient modified_client(kUrl, "foo_domain",
+                                                       "foo_name");
+  ContentBrowserClient* regular_client =
+      SetBrowserClientForTesting(&modified_client);
+
+  // Create a foo.com SiteInstance and check that its process does not
+  // reuse the foo process from the first navigation, since it's now in a
+  // different StoragePartiiton.
+  scoped_refptr<SiteInstanceImpl> site_instance =
+      SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
+  site_instance->set_process_reuse_policy(
+      SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE);
+  RenderProcessHost* process = site_instance->GetProcess();
+  EXPECT_NE(main_test_rfh()->GetProcess(), process);
+
+  SetBrowserClientForTesting(regular_client);
+}
+
+// Check that a SiteInstance cannot reuse a ServiceWorker process in a
+// different StoragePartition.
+TEST_F(RenderProcessHostUnitTest,
+       DoNotReuseServiceWorkerProcessInDifferentStoragePartition) {
+  const GURL kUrl("https://foo.com");
+
+  // Create a RenderProcessHost for a service worker.
+  scoped_refptr<SiteInstanceImpl> sw_site_instance =
+      SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
+  sw_site_instance->set_is_for_service_worker();
+  RenderProcessHost* sw_process = sw_site_instance->GetProcess();
+
+  // Change foo.com SiteInstances to use a different StoragePartition.
+  StoragePartitionContentBrowserClient modified_client(kUrl, "foo_domain",
+                                                       "foo_name");
+  ContentBrowserClient* regular_client =
+      SetBrowserClientForTesting(&modified_client);
+
+  // Create a foo.com SiteInstance and check that its process does not reuse
+  // the ServiceWorker foo.com process, since it's now in a different
+  // StoragePartition.
+  scoped_refptr<SiteInstanceImpl> site_instance =
+      SiteInstanceImpl::CreateForURL(browser_context(), kUrl);
+  site_instance->set_process_reuse_policy(
+      SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE);
+  RenderProcessHost* process = site_instance->GetProcess();
+  EXPECT_NE(sw_process, process);
+
+  SetBrowserClientForTesting(regular_client);
+}
+
 class SpareRenderProcessHostUnitTest : public RenderViewHostImplTestHarness {
  protected:
   void SetUp() override {
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 66146f0..4d07580 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -83,7 +83,7 @@
 #include "skia/ext/image_operations.h"
 #include "skia/ext/platform_canvas.h"
 #include "storage/browser/fileapi/isolated_context.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/display_switches.h"
@@ -1719,21 +1719,21 @@
 
 void RenderWidgetHostImpl::ImeSetComposition(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int selection_start,
     int selection_end) {
   GetWidgetInputHandler()->ImeSetComposition(
-      text, underlines, replacement_range, selection_start, selection_end);
+      text, ime_text_spans, replacement_range, selection_start, selection_end);
 }
 
 void RenderWidgetHostImpl::ImeCommitText(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int relative_cursor_pos) {
-  GetWidgetInputHandler()->ImeCommitText(text, underlines, replacement_range,
-                                         relative_cursor_pos);
+  GetWidgetInputHandler()->ImeCommitText(
+      text, ime_text_spans, replacement_range, relative_cursor_pos);
 }
 
 void RenderWidgetHostImpl::ImeFinishComposingText(bool keep_selection) {
@@ -1741,9 +1741,9 @@
 }
 
 void RenderWidgetHostImpl::ImeCancelComposition() {
-  GetWidgetInputHandler()->ImeSetComposition(
-      base::string16(), std::vector<ui::CompositionUnderline>(),
-      gfx::Range::InvalidRange(), 0, 0);
+  GetWidgetInputHandler()->ImeSetComposition(base::string16(),
+                                             std::vector<ui::ImeTextSpan>(),
+                                             gfx::Range::InvalidRange(), 0, 0);
 }
 
 void RenderWidgetHostImpl::RejectMouseLockOrUnlockIfNecessary() {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 8cc2749..52b5fa9 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -415,12 +415,11 @@
   //   (on Windows);
   // * when it receives a "preedit_changed" signal of GtkIMContext (on Linux);
   // * when markedText of NSTextInput is called (on Mac).
-  void ImeSetComposition(
-      const base::string16& text,
-      const std::vector<ui::CompositionUnderline>& underlines,
-      const gfx::Range& replacement_range,
-      int selection_start,
-      int selection_end);
+  void ImeSetComposition(const base::string16& text,
+                         const std::vector<ui::ImeTextSpan>& ime_text_spans,
+                         const gfx::Range& replacement_range,
+                         int selection_start,
+                         int selection_end);
 
   // Deletes the ongoing composition if any, inserts the specified text, and
   // moves the cursor.
@@ -430,7 +429,7 @@
   // * when it receives a "commit" signal of GtkIMContext (on Linux);
   // * when insertText of NSTextInput is called (on Mac).
   void ImeCommitText(const base::string16& text,
-                     const std::vector<ui::CompositionUnderline>& underlines,
+                     const std::vector<ui::ImeTextSpan>& ime_text_spans,
                      const gfx::Range& replacement_range,
                      int relative_cursor_pos);
 
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 ffd1e2ee..f693b0e3 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -61,7 +61,7 @@
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/ui/public/interfaces/window_manager_constants.mojom.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "ui/accessibility/platform/aura_window_properties.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/cursor_client.h"
@@ -1191,7 +1191,7 @@
   // TODO(suzhe): due to a bug of webkit, we can't use selection range with
   // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788
   text_input_manager_->GetActiveWidget()->ImeSetComposition(
-      composition.text, composition.underlines, gfx::Range::InvalidRange(),
+      composition.text, composition.ime_text_spans, gfx::Range::InvalidRange(),
       composition.selection.end(), composition.selection.end());
 
   has_composition_text_ = !composition.text.empty();
@@ -1218,8 +1218,7 @@
   if (text_input_manager_ && text_input_manager_->GetActiveWidget()) {
     if (text.length())
       text_input_manager_->GetActiveWidget()->ImeCommitText(
-          text, std::vector<ui::CompositionUnderline>(),
-          gfx::Range::InvalidRange(), 0);
+          text, std::vector<ui::ImeTextSpan>(), gfx::Range::InvalidRange(), 0);
     else if (has_composition_text_)
       text_input_manager_->GetActiveWidget()->ImeFinishComposingText(false);
   }
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 fa9609c..c111633 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
@@ -1648,14 +1648,14 @@
   composition_text.text = base::ASCIIToUTF16("|a|b");
 
   // Focused segment
-  composition_text.underlines.push_back(
-      ui::CompositionUnderline(0, 3, 0xff000000, true, 0x78563412));
+  composition_text.ime_text_spans.push_back(
+      ui::ImeTextSpan(0, 3, 0xff000000, true, 0x78563412));
 
   // Non-focused segment, with different background color.
-  composition_text.underlines.push_back(
-      ui::CompositionUnderline(3, 4, 0xff000000, false, 0xefcdab90));
+  composition_text.ime_text_spans.push_back(
+      ui::ImeTextSpan(3, 4, 0xff000000, false, 0xefcdab90));
 
-  const ui::CompositionUnderlines& underlines = composition_text.underlines;
+  const ui::ImeTextSpans& ime_text_spans = composition_text.ime_text_spans;
 
   // Caret is at the end. (This emulates Japanese MSIME 2007 and later)
   composition_text.selection = gfx::Range(4);
@@ -1672,15 +1672,16 @@
     InputMsg_ImeSetComposition::Read(msg, &params);
     // composition text
     EXPECT_EQ(composition_text.text, std::get<0>(params));
-    // underlines
-    ASSERT_EQ(underlines.size(), std::get<1>(params).size());
-    for (size_t i = 0; i < underlines.size(); ++i) {
-      EXPECT_EQ(underlines[i].start_offset,
+    // ime spans
+    ASSERT_EQ(ime_text_spans.size(), std::get<1>(params).size());
+    for (size_t i = 0; i < ime_text_spans.size(); ++i) {
+      EXPECT_EQ(ime_text_spans[i].start_offset,
                 std::get<1>(params)[i].start_offset);
-      EXPECT_EQ(underlines[i].end_offset, std::get<1>(params)[i].end_offset);
-      EXPECT_EQ(underlines[i].color, std::get<1>(params)[i].color);
-      EXPECT_EQ(underlines[i].thick, std::get<1>(params)[i].thick);
-      EXPECT_EQ(underlines[i].background_color,
+      EXPECT_EQ(ime_text_spans[i].end_offset,
+                std::get<1>(params)[i].end_offset);
+      EXPECT_EQ(ime_text_spans[i].color, std::get<1>(params)[i].color);
+      EXPECT_EQ(ime_text_spans[i].thick, std::get<1>(params)[i].thick);
+      EXPECT_EQ(ime_text_spans[i].background_color,
                 std::get<1>(params)[i].background_color);
     }
     EXPECT_EQ(gfx::Range::InvalidRange(), std::get<2>(params));
@@ -1704,12 +1705,12 @@
   composition_text.text = base::ASCIIToUTF16("|a|b");
 
   // Focused segment
-  composition_text.underlines.push_back(
-      ui::CompositionUnderline(0, 3, 0xff000000, true, 0x78563412));
+  composition_text.ime_text_spans.push_back(
+      ui::ImeTextSpan(0, 3, 0xff000000, true, 0x78563412));
 
   // Non-focused segment, with different background color.
-  composition_text.underlines.push_back(
-      ui::CompositionUnderline(3, 4, 0xff000000, false, 0xefcdab90));
+  composition_text.ime_text_spans.push_back(
+      ui::ImeTextSpan(3, 4, 0xff000000, false, 0xefcdab90));
 
   // Caret is at the end. (This emulates Japanese MSIME 2007 and later)
   composition_text.selection = gfx::Range(4);
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 9374bf3..72664cd 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -37,7 +37,7 @@
 #import "ui/base/cocoa/command_dispatcher.h"
 #include "ui/base/cocoa/remote_layer_api.h"
 #import "ui/base/cocoa/tool_tip_base_view.h"
-#include "ui/base/ime/composition_underline.h"
+#include "ui/base/ime/ime_text_span.h"
 #include "ui/display/display_observer.h"
 
 namespace content {
@@ -127,7 +127,7 @@
   NSRange markedTextSelectedRange_;
 
   // Underline information of the |markedText_|.
-  std::vector<ui::CompositionUnderline> underlines_;
+  std::vector<ui::ImeTextSpan> ime_text_spans_;
 
   // Replacement range information received from |setMarkedText:|.
   gfx::Range setMarkedTextReplacementRange_;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index db0fb3681..d61f81f 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -280,7 +280,7 @@
 // Extract underline information from an attributed string. Mostly copied from
 // third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
 void ExtractUnderlines(NSAttributedString* string,
-                       std::vector<ui::CompositionUnderline>* underlines) {
+                       std::vector<ui::ImeTextSpan>* ime_text_spans) {
   int length = [[string string] length];
   int i = 0;
   while (i < length) {
@@ -295,9 +295,9 @@
         color = WebColorFromNSColor(
             [colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
       }
-      underlines->push_back(
-          ui::CompositionUnderline(range.location, NSMaxRange(range), color,
-                                   [style intValue] > 1, SK_ColorTRANSPARENT));
+      ime_text_spans->push_back(
+          ui::ImeTextSpan(range.location, NSMaxRange(range), color,
+                          [style intValue] > 1, SK_ColorTRANSPARENT));
     }
     i = range.location + range.length;
   }
@@ -2197,7 +2197,7 @@
   textToBeInserted_.clear();
   markedText_.clear();
   markedTextSelectedRange_ = NSMakeRange(NSNotFound, 0);
-  underlines_.clear();
+  ime_text_spans_.clear();
   setMarkedTextReplacementRange_ = gfx::Range::InvalidRange();
   unmarkTextCalled_ = NO;
   hasEditCommands_ = NO;
@@ -2259,8 +2259,7 @@
   BOOL textInserted = NO;
   if (textToBeInserted_.length() >
       ((hasMarkedText_ || oldHasMarkedText) ? 0u : 1u)) {
-    widgetHost->ImeCommitText(textToBeInserted_,
-                              std::vector<ui::CompositionUnderline>(),
+    widgetHost->ImeCommitText(textToBeInserted_, std::vector<ui::ImeTextSpan>(),
                               gfx::Range::InvalidRange(), 0);
     textInserted = YES;
   }
@@ -2272,7 +2271,7 @@
     // composition node in WebKit.
     // When marked text is available, |markedTextSelectedRange_| will be the
     // range being selected inside the marked text.
-    widgetHost->ImeSetComposition(markedText_, underlines_,
+    widgetHost->ImeSetComposition(markedText_, ime_text_spans_,
                                   setMarkedTextReplacementRange_,
                                   markedTextSelectedRange_.location,
                                   NSMaxRange(markedTextSelectedRange_));
@@ -3306,7 +3305,7 @@
   hasMarkedText_ = NO;
   markedText_.clear();
   markedTextSelectedRange_ = NSMakeRange(NSNotFound, 0);
-  underlines_.clear();
+  ime_text_spans_.clear();
 
   // If we are handling a key down event, then FinishComposingText() will be
   // called in keyEvent: method.
@@ -3335,13 +3334,13 @@
   markedText_ = base::SysNSStringToUTF16(im_text);
   hasMarkedText_ = (length > 0);
 
-  underlines_.clear();
+  ime_text_spans_.clear();
   if (isAttributedString) {
-    ExtractUnderlines(string, &underlines_);
+    ExtractUnderlines(string, &ime_text_spans_);
   } else {
     // Use a thin black underline by default.
-    underlines_.push_back(ui::CompositionUnderline(0, length, SK_ColorBLACK,
-                                                   false, SK_ColorTRANSPARENT));
+    ime_text_spans_.push_back(
+        ui::ImeTextSpan(0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
   }
 
   // If we are handling a key down event, then SetComposition() will be
@@ -3356,7 +3355,7 @@
   } else {
     if (renderWidgetHostView_->GetActiveWidget()) {
       renderWidgetHostView_->GetActiveWidget()->ImeSetComposition(
-          markedText_, underlines_, gfx::Range(replacementRange),
+          markedText_, ime_text_spans_, gfx::Range(replacementRange),
           newSelRange.location, NSMaxRange(newSelRange));
     }
   }
@@ -3415,8 +3414,8 @@
     gfx::Range replacement_range(replacementRange);
     if (renderWidgetHostView_->GetActiveWidget()) {
       renderWidgetHostView_->GetActiveWidget()->ImeCommitText(
-          base::SysNSStringToUTF16(im_text),
-          std::vector<ui::CompositionUnderline>(), replacement_range, 0);
+          base::SysNSStringToUTF16(im_text), std::vector<ui::ImeTextSpan>(),
+          replacement_range, 0);
     }
   }
 
diff --git a/content/browser/service_worker/service_worker_installed_scripts_sender.cc b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
index bf4cbfe..51c683e 100644
--- a/content/browser/service_worker/service_worker_installed_scripts_sender.cc
+++ b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
@@ -10,8 +10,8 @@
 #include "content/browser/service_worker/service_worker_disk_cache.h"
 #include "content/browser/service_worker/service_worker_script_cache_map.h"
 #include "content/browser/service_worker/service_worker_storage.h"
-#include "content/common/net_adapters.h"
 #include "net/http/http_response_headers.h"
+#include "services/network/public/cpp/net_adapters.h"
 
 namespace content {
 
@@ -176,7 +176,7 @@
     // an equivalent error.
     DCHECK(!pending_write_);
     uint32_t num_bytes = 0;
-    MojoResult rv = NetToMojoPendingBuffer::BeginWrite(
+    MojoResult rv = network::NetToMojoPendingBuffer::BeginWrite(
         &body_handle_, &pending_write_, &num_bytes);
     switch (rv) {
       case MOJO_RESULT_INVALID_ARGUMENT:
@@ -193,8 +193,8 @@
         break;
     }
 
-    scoped_refptr<NetToMojoIOBuffer> buffer =
-        base::MakeRefCounted<NetToMojoIOBuffer>(pending_write_.get());
+    scoped_refptr<network::NetToMojoIOBuffer> buffer =
+        base::MakeRefCounted<network::NetToMojoIOBuffer>(pending_write_.get());
     reader_->ReadData(buffer.get(), num_bytes,
                       base::Bind(&Sender::OnResponseDataRead, AsWeakPtr()));
   }
@@ -253,7 +253,7 @@
   std::unique_ptr<MetaDataSender> meta_data_sender_;
 
   // For body.
-  scoped_refptr<NetToMojoPendingBuffer> pending_write_;
+  scoped_refptr<network::NetToMojoPendingBuffer> pending_write_;
   mojo::SimpleWatcher watcher_;
 
   // Pipes.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 4442c8f7..bec99bd 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3403,6 +3403,7 @@
 
 void WebContentsImpl::DidGetResourceResponseStart(
   const ResourceRequestDetails& details) {
+  SetNotWaitingForResponse();
   controller_.ssl_manager()->DidStartResourceResponse(
       details.url, details.has_certificate, details.ssl_cert_status);
 
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index d588e66..6c74fc6 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -244,6 +244,7 @@
   EXPECT_EQ(&shell()->web_contents()->GetController(),
             load_observer.controller_);
 }
+
 // Test that a renderer-initiated navigation to an invalid URL does not leave
 // around a pending entry that could be used in a URL spoof.  We test this in
 // a browser test because our unit test framework incorrectly calls
@@ -484,13 +485,15 @@
   base::string16 title = title_watcher.WaitAndGetTitle();
   ASSERT_EQ(title, base::ASCIIToUTF16("pushState"));
 
-  // LoadingStateChanged should be called 4 times: start and stop for the
-  // initial load of push_state.html, and start and stop for the "navigation"
-  // triggered by history.pushState(). However, the start notification for the
-  // history.pushState() navigation should set to_different_document to false.
+  // LoadingStateChanged should be called 5 times: start and stop for the
+  // initial load of push_state.html, once for the switch from
+  // IsWaitingForResponse() to !IsWaitingForResponse(), and start and stop for
+  // the "navigation" triggered by history.pushState(). However, the start
+  // notification for the history.pushState() navigation should set
+  // to_different_document to false.
   EXPECT_EQ("pushState", shell()->web_contents()->GetLastCommittedURL().ref());
-  EXPECT_EQ(4, delegate->loadingStateChangedCount());
-  EXPECT_EQ(3, delegate->loadingStateToDifferentDocumentCount());
+  EXPECT_EQ(5, delegate->loadingStateChangedCount());
+  EXPECT_EQ(4, delegate->loadingStateToDifferentDocumentCount());
 }
 
 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
@@ -512,6 +515,55 @@
   EXPECT_TRUE(new_web_contents_observer.RenderViewCreatedCalled());
 }
 
+namespace {
+
+class DidGetResourceResponseStartObserver : public WebContentsObserver {
+ public:
+  DidGetResourceResponseStartObserver(Shell* shell)
+      : WebContentsObserver(shell->web_contents()), shell_(shell) {
+    shell->web_contents()->SetDelegate(&delegate_);
+    EXPECT_FALSE(shell->web_contents()->IsWaitingForResponse());
+    EXPECT_FALSE(shell->web_contents()->IsLoading());
+  }
+
+  ~DidGetResourceResponseStartObserver() override {}
+
+  void DidGetResourceResponseStart(
+      const ResourceRequestDetails& details) override {
+    EXPECT_FALSE(shell_->web_contents()->IsWaitingForResponse());
+    EXPECT_TRUE(shell_->web_contents()->IsLoading());
+    EXPECT_GT(delegate_.loadingStateChangedCount(), 0);
+    ++resource_response_start_count_;
+  }
+
+  int resource_response_start_count() const {
+    return resource_response_start_count_;
+  }
+
+ private:
+  Shell* shell_;
+  LoadingStateChangedDelegate delegate_;
+  int resource_response_start_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(DidGetResourceResponseStartObserver);
+};
+
+}  // namespace
+
+// Makes sure that the WebContents is no longer marked as waiting for a response
+// after DidGetResourceResponseStart() is called.
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+                       DidGetResourceResponseStartUpdatesWaitingState) {
+  DidGetResourceResponseStartObserver observer(shell());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  LoadStopNotificationObserver load_observer(
+      &shell()->web_contents()->GetController());
+  NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
+  load_observer.Wait();
+  EXPECT_GT(observer.resource_response_start_count(), 0);
+}
+
 struct LoadProgressDelegateAndObserver : public WebContentsDelegate,
                                          public WebContentsObserver {
   LoadProgressDelegateAndObserver(Shell* shell)
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index ecb25838..0f2c3b3 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -245,8 +245,6 @@
     "net/url_request_service_worker_data.h",
     "net/url_request_user_data.cc",
     "net/url_request_user_data.h",
-    "net_adapters.cc",
-    "net_adapters.h",
     "origin_trials/trial_token.cc",
     "origin_trials/trial_token.h",
     "origin_trials/trial_token_validator.cc",
diff --git a/content/common/DEPS b/content/common/DEPS
index 736590dd..c0da6de2 100644
--- a/content/common/DEPS
+++ b/content/common/DEPS
@@ -65,12 +65,12 @@
   "+third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerState.h",
   "+third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom.h",
   "+third_party/WebKit/public/web/WebAXEnums.h",
-  "+third_party/WebKit/public/web/WebCompositionUnderline.h",
   "+third_party/WebKit/public/web/WebDeviceEmulationParams.h",
   "+third_party/WebKit/public/web/WebDragStatus.h",
   "+third_party/WebKit/public/web/WebFindOptions.h",
   "+third_party/WebKit/public/web/WebFrameOwnerProperties.h",
   "+third_party/WebKit/public/web/WebFrameSerializerCacheControlPolicy.h",
+  "+third_party/WebKit/public/web/WebImeTextSpan.h",
   "+third_party/WebKit/public/web/WebMediaPlayerAction.h",
   "+third_party/WebKit/public/web/WebPluginAction.h",
   "+third_party/WebKit/public/web/WebPopupType.h",
diff --git a/content/common/browser_plugin/browser_plugin_messages.h b/content/common/browser_plugin/browser_plugin_messages.h
index 57ac7ad..1258423 100644
--- a/content/common/browser_plugin/browser_plugin_messages.h
+++ b/content/common/browser_plugin/browser_plugin_messages.h
@@ -18,8 +18,8 @@
 #include "ipc/ipc_message_utils.h"
 #include "third_party/WebKit/public/platform/WebDragOperation.h"
 #include "third_party/WebKit/public/platform/WebFocusType.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "third_party/WebKit/public/web/WebDragStatus.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -45,7 +45,7 @@
 
 IPC_STRUCT_BEGIN(BrowserPluginHostMsg_SetComposition_Params)
   IPC_STRUCT_MEMBER(base::string16, text)
-  IPC_STRUCT_MEMBER(std::vector<blink::WebCompositionUnderline>, underlines)
+  IPC_STRUCT_MEMBER(std::vector<blink::WebImeTextSpan>, ime_text_spans)
   IPC_STRUCT_MEMBER(gfx::Range, replacement_range)
   IPC_STRUCT_MEMBER(int, selection_start)
   IPC_STRUCT_MEMBER(int, selection_end)
@@ -81,13 +81,12 @@
 
 // This message is sent from BrowserPlugin to BrowserPluginGuest to notify that
 // deleting the current composition and inserting specified text is requested.
-IPC_MESSAGE_CONTROL5(
-    BrowserPluginHostMsg_ImeCommitText,
-    int /* browser_plugin_instance_id */,
-    base::string16 /* text */,
-    std::vector<blink::WebCompositionUnderline> /* underlines */,
-    gfx::Range /* replacement_range */,
-    int /* relative_cursor_pos */)
+IPC_MESSAGE_CONTROL5(BrowserPluginHostMsg_ImeCommitText,
+                     int /* browser_plugin_instance_id */,
+                     base::string16 /* text */,
+                     std::vector<blink::WebImeTextSpan> /* ime_text_spans */,
+                     gfx::Range /* replacement_range */,
+                     int /* relative_cursor_pos */)
 
 // This message is sent from BrowserPlugin to BrowserPluginGuest to notify that
 // inserting the current composition is requested.
diff --git a/content/common/common.sb b/content/common/common.sb
index fbe0ff5..f8fe48b 100644
--- a/content/common/common.sb
+++ b/content/common/common.sb
@@ -19,6 +19,7 @@
 (define permitted-dir "PERMITTED_DIR")
 (define homedir-as-literal "USER_HOMEDIR_AS_LITERAL")
 (define elcap-or-later "ELCAP_OR_LATER")
+(define macos-1013 "MACOS_1013")
 
 ; Consumes a subpath and appends it to the user's homedir path.
 (define (user-homedir-path subpath)
diff --git a/content/common/content_param_traits_macros.h b/content/common/content_param_traits_macros.h
index a1187c78..9c5638e 100644
--- a/content/common/content_param_traits_macros.h
+++ b/content/common/content_param_traits_macros.h
@@ -21,7 +21,7 @@
 #include "third_party/WebKit/public/platform/WebContentSecurityPolicy.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 #include "third_party/WebKit/public/platform/WebPageVisibilityState.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "third_party/WebKit/public/web/WebSharedWorkerCreationContextType.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gfx/ipc/geometry/gfx_param_traits.h"
@@ -52,7 +52,7 @@
 IPC_ENUM_TRAITS_MAX_VALUE(blink::WebPageVisibilityState,
                           blink::kWebPageVisibilityStateLast)
 
-IPC_STRUCT_TRAITS_BEGIN(blink::WebCompositionUnderline)
+IPC_STRUCT_TRAITS_BEGIN(blink::WebImeTextSpan)
   IPC_STRUCT_TRAITS_MEMBER(start_offset)
   IPC_STRUCT_TRAITS_MEMBER(end_offset)
   IPC_STRUCT_TRAITS_MEMBER(color)
diff --git a/content/common/dom_storage/dom_storage_messages.h b/content/common/dom_storage/dom_storage_messages.h
index 3325bf9b..dc55283 100644
--- a/content/common/dom_storage/dom_storage_messages.h
+++ b/content/common/dom_storage/dom_storage_messages.h
@@ -77,17 +77,19 @@
 
 // Set a value that's associated with a key in a storage area.
 // A completion notification is sent in response.
-IPC_MESSAGE_CONTROL4(DOMStorageHostMsg_SetItem,
+IPC_MESSAGE_CONTROL5(DOMStorageHostMsg_SetItem,
                      int /* connection_id */,
                      base::string16 /* key */,
                      base::string16 /* value */,
+                     base::NullableString16 /* client_old_value */,
                      GURL /* page_url */)
 
 // Remove the value associated with a key in a storage area.
 // A completion notification is sent in response.
-IPC_MESSAGE_CONTROL3(DOMStorageHostMsg_RemoveItem,
+IPC_MESSAGE_CONTROL4(DOMStorageHostMsg_RemoveItem,
                      int /* connection_id */,
                      base::string16 /* key */,
+                     base::NullableString16 /* client_old_value */,
                      GURL /* page_url */)
 
 // Clear the storage area. A completion notification is sent in response.
diff --git a/content/common/input/input_handler.mojom b/content/common/input/input_handler.mojom
index 39cc748..2613a00 100644
--- a/content/common/input/input_handler.mojom
+++ b/content/common/input/input_handler.mojom
@@ -169,13 +169,13 @@
 
   // This message sends a string being composed with an input method.
   ImeSetComposition(mojo.common.mojom.String16 text,
-                    array<ui.mojom.CompositionUnderline> underlines,
+                    array<ui.mojom.ImeTextSpan> ime_text_spans,
                     gfx.mojom.Range range, int32 start, int32 end);
 
   // This message deletes the current composition, inserts specified text, and
   // moves the cursor.
   ImeCommitText(mojo.common.mojom.String16 text,
-                array<ui.mojom.CompositionUnderline> underlines,
+                array<ui.mojom.ImeTextSpan> ime_text_spans,
                 gfx.mojom.Range range, int32 relative_cursor_position);
 
   // This message inserts the ongoing composition.
@@ -216,7 +216,7 @@
   // Sets the text composition to be between the given start and end offsets in
   // the currently focused editable field.
   SetCompositionFromExistingText(
-      int32 start, int32 end, array<ui.mojom.CompositionUnderline> underlines);
+      int32 start, int32 end, array<ui.mojom.ImeTextSpan> ime_text_spans);
 
   // Deletes the current selection plus the specified number of characters
   // before and after the selection or caret.
diff --git a/content/common/input_messages.h b/content/common/input_messages.h
index 142f8db4..73dedb03 100644
--- a/content/common/input_messages.h
+++ b/content/common/input_messages.h
@@ -169,9 +169,9 @@
 // Sets the text composition to be between the given start and end offsets in
 // the currently focused editable field.
 IPC_MESSAGE_ROUTED3(InputMsg_SetCompositionFromExistingText,
-    int /* start */,
-    int /* end */,
-    std::vector<blink::WebCompositionUnderline> /* underlines */)
+                    int /* start */,
+                    int /* end */,
+                    std::vector<blink::WebImeTextSpan> /* ime_text_spans */)
 
 // Deletes the current selection plus the specified number of characters before
 // and after the selection or caret.
@@ -201,22 +201,20 @@
                     int /* end */)
 
 // This message sends a string being composed with an input method.
-IPC_MESSAGE_ROUTED5(
-    InputMsg_ImeSetComposition,
-    base::string16, /* text */
-    std::vector<blink::WebCompositionUnderline>, /* underlines */
-    gfx::Range /* replacement_range */,
-    int, /* selectiont_start */
-    int /* selection_end */)
+IPC_MESSAGE_ROUTED5(InputMsg_ImeSetComposition,
+                    base::string16,                     /* text */
+                    std::vector<blink::WebImeTextSpan>, /* ime_text_spans */
+                    gfx::Range /* replacement_range */,
+                    int, /* selectiont_start */
+                    int /* selection_end */)
 
 // This message deletes the current composition, inserts specified text, and
 // moves the cursor.
-IPC_MESSAGE_ROUTED4(
-    InputMsg_ImeCommitText,
-    base::string16 /* text */,
-    std::vector<blink::WebCompositionUnderline>, /* underlines */
-    gfx::Range /* replacement_range */,
-    int /* relative_cursor_pos */)
+IPC_MESSAGE_ROUTED4(InputMsg_ImeCommitText,
+                    base::string16 /* text */,
+                    std::vector<blink::WebImeTextSpan>, /* ime_text_spans */
+                    gfx::Range /* replacement_range */,
+                    int /* relative_cursor_pos */)
 
 // This message inserts the ongoing composition.
 IPC_MESSAGE_ROUTED1(InputMsg_ImeFinishComposingText, bool /* keep_selection */)
diff --git a/content/common/media/media_stream_messages.h b/content/common/media/media_stream_messages.h
index ea288d3..e44672f 100644
--- a/content/common/media/media_stream_messages.h
+++ b/content/common/media/media_stream_messages.h
@@ -25,18 +25,12 @@
 
 IPC_STRUCT_TRAITS_BEGIN(content::StreamDeviceInfo)
   IPC_STRUCT_TRAITS_MEMBER(device.type)
-  IPC_STRUCT_TRAITS_MEMBER(device.name)
   IPC_STRUCT_TRAITS_MEMBER(device.id)
   IPC_STRUCT_TRAITS_MEMBER(device.video_facing)
   IPC_STRUCT_TRAITS_MEMBER(device.matched_output_device_id)
-  IPC_STRUCT_TRAITS_MEMBER(device.input.sample_rate)
-  IPC_STRUCT_TRAITS_MEMBER(device.input.channel_layout)
-  IPC_STRUCT_TRAITS_MEMBER(device.input.frames_per_buffer)
-  IPC_STRUCT_TRAITS_MEMBER(device.input.effects)
-  IPC_STRUCT_TRAITS_MEMBER(device.input.mic_positions)
-  IPC_STRUCT_TRAITS_MEMBER(device.matched_output.sample_rate)
-  IPC_STRUCT_TRAITS_MEMBER(device.matched_output.channel_layout)
-  IPC_STRUCT_TRAITS_MEMBER(device.matched_output.frames_per_buffer)
+  IPC_STRUCT_TRAITS_MEMBER(device.name)
+  IPC_STRUCT_TRAITS_MEMBER(device.input)
+  IPC_STRUCT_TRAITS_MEMBER(device.matched_output)
   IPC_STRUCT_TRAITS_MEMBER(device.camera_calibration)
   IPC_STRUCT_TRAITS_MEMBER(session_id)
 IPC_STRUCT_TRAITS_END()
diff --git a/content/common/render_message_filter.mojom b/content/common/render_message_filter.mojom
index e51d3dc..b61002b7 100644
--- a/content/common/render_message_filter.mojom
+++ b/content/common/render_message_filter.mojom
@@ -4,7 +4,6 @@
 
 module content.mojom;
 
-import "cc/ipc/shared_bitmap_allocation_notifier.mojom";
 import "content/common/input/input_handler.mojom";
 import "content/common/native_types.mojom";
 import "content/common/widget.mojom";
diff --git a/content/common/sandbox_mac.h b/content/common/sandbox_mac.h
index 8496104..93765ed 100644
--- a/content/common/sandbox_mac.h
+++ b/content/common/sandbox_mac.h
@@ -61,6 +61,7 @@
 
   // TODO(kerrnel): this is only for the legacy sandbox.
   static const char* kSandboxElCapOrLater;
+  static const char* kSandboxMacOS1013;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(MacDirAccessSandboxTest, StringEscape);
diff --git a/content/common/sandbox_mac.mm b/content/common/sandbox_mac.mm
index 9156287..e9789fe 100644
--- a/content/common/sandbox_mac.mm
+++ b/content/common/sandbox_mac.mm
@@ -84,6 +84,7 @@
 const char* Sandbox::kSandboxPermittedDir = "PERMITTED_DIR";
 
 const char* Sandbox::kSandboxElCapOrLater = "ELCAP_OR_LATER";
+const char* Sandbox::kSandboxMacOS1013 = "MACOS_1013";
 
 // Warm up System APIs that empirically need to be accessed before the Sandbox
 // is turned on.
@@ -282,6 +283,10 @@
   if (!compiler.InsertBooleanParam(kSandboxElCapOrLater, elcap_or_later))
     return false;
 
+  bool macos_1013 = base::mac::IsOS10_13();
+  if (!compiler.InsertBooleanParam(kSandboxMacOS1013, macos_1013))
+    return false;
+
   // Initialize sandbox.
   std::string error_str;
   bool success = compiler.CompileAndApplyProfile(&error_str);
diff --git a/content/network/BUILD.gn b/content/network/BUILD.gn
index fe29a2cf..cc11dea 100644
--- a/content/network/BUILD.gn
+++ b/content/network/BUILD.gn
@@ -59,6 +59,7 @@
     "//content/public/common:common_sources",
     "//mojo/public/cpp/bindings",
     "//net",
+    "//services/network/public/cpp",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/interfaces",
   ]
diff --git a/content/network/DEPS b/content/network/DEPS
index 2f038c7..b24d403 100644
--- a/content/network/DEPS
+++ b/content/network/DEPS
@@ -4,7 +4,6 @@
   "-content",
   "+content/common/content_export.h",
   "+content/common/loader_util.h",
-  "+content/common/net_adapters.h",
   "+content/network",
   "+content/public/common/appcache_info.h",
   "+content/public/common/content_client.h",
@@ -18,6 +17,7 @@
   "+content/public/common/url_loader.mojom.h",
   "+content/public/common/url_loader_factory.mojom.h",
   "+content/public/network",
+  "+services/network",
   "+services/service_manager/public",
 ]
 
diff --git a/content/network/url_loader_impl.cc b/content/network/url_loader_impl.cc
index b244f79..140cacd 100644
--- a/content/network/url_loader_impl.cc
+++ b/content/network/url_loader_impl.cc
@@ -8,7 +8,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "content/common/loader_util.h"
-#include "content/common/net_adapters.h"
 #include "content/network/network_context.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/resource_response.h"
@@ -19,6 +18,7 @@
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_file_element_reader.h"
 #include "net/url_request/url_request_context.h"
+#include "services/network/public/cpp/net_adapters.h"
 
 namespace content {
 
@@ -289,7 +289,7 @@
   if (!pending_write_.get()) {
     // TODO: we should use the abstractions in MojoAsyncResourceHandler.
     pending_write_buffer_offset_ = 0;
-    MojoResult result = NetToMojoPendingBuffer::BeginWrite(
+    MojoResult result = network::NetToMojoPendingBuffer::BeginWrite(
         &response_body_stream_, &pending_write_, &pending_write_buffer_size_);
     if (result != MOJO_RESULT_OK && result != MOJO_RESULT_SHOULD_WAIT) {
       // The response body stream is in a bad state. Bail.
@@ -313,8 +313,8 @@
     }
   }
 
-  scoped_refptr<net::IOBuffer> buf(new NetToMojoIOBuffer(
-      pending_write_.get(), pending_write_buffer_offset_));
+  auto buf = base::MakeRefCounted<network::NetToMojoIOBuffer>(
+      pending_write_.get(), pending_write_buffer_offset_);
   int bytes_read;
   url_request_->Read(buf.get(),
                      static_cast<int>(pending_write_buffer_size_ -
diff --git a/content/network/url_loader_impl.h b/content/network/url_loader_impl.h
index a349be2..d0c057a9 100644
--- a/content/network/url_loader_impl.h
+++ b/content/network/url_loader_impl.h
@@ -18,10 +18,13 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request.h"
 
+namespace network {
+class NetToMojoPendingBuffer;
+}
+
 namespace content {
 
 class NetworkContext;
-class NetToMojoPendingBuffer;
 struct ResourceResponse;
 
 class CONTENT_EXPORT URLLoaderImpl : public mojom::URLLoader,
@@ -73,7 +76,7 @@
   int64_t total_written_bytes_ = 0;
 
   mojo::ScopedDataPipeProducerHandle response_body_stream_;
-  scoped_refptr<NetToMojoPendingBuffer> pending_write_;
+  scoped_refptr<network::NetToMojoPendingBuffer> pending_write_;
   uint32_t pending_write_buffer_size_ = 0;
   uint32_t pending_write_buffer_offset_ = 0;
   mojo::SimpleWatcher writable_handle_watcher_;
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
index 713b236..5370d225 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java
@@ -872,9 +872,10 @@
     }
 
     @CalledByNative
-    private void populateUnderlinesFromSpans(CharSequence text, long underlines) {
+    private void populateImeTextSpansFromJava(CharSequence text, long imeTextSpans) {
         if (DEBUG_LOGS) {
-            Log.i(TAG, "populateUnderlinesFromSpans: text [%s], underlines [%d]", text, underlines);
+            Log.i(TAG, "populateImeTextSpansFromJava: text [%s], ime_text_spans [%d]", text,
+                    imeTextSpans);
         }
         if (!(text instanceof SpannableString)) return;
 
@@ -883,11 +884,11 @@
                 spannableString.getSpans(0, text.length(), CharacterStyle.class);
         for (CharacterStyle span : spans) {
             if (span instanceof BackgroundColorSpan) {
-                nativeAppendBackgroundColorSpan(underlines, spannableString.getSpanStart(span),
+                nativeAppendBackgroundColorSpan(imeTextSpans, spannableString.getSpanStart(span),
                         spannableString.getSpanEnd(span),
                         ((BackgroundColorSpan) span).getBackgroundColor());
             } else if (span instanceof UnderlineSpan) {
-                nativeAppendUnderlineSpan(underlines, spannableString.getSpanStart(span),
+                nativeAppendUnderlineSpan(imeTextSpans, spannableString.getSpanStart(span),
                         spannableString.getSpanEnd(span));
             }
         }
@@ -917,9 +918,9 @@
     private native boolean nativeSendKeyEvent(long nativeImeAdapterAndroid, KeyEvent event,
             int type, int modifiers, long timestampMs, int keyCode, int scanCode,
             boolean isSystemKey, int unicodeChar);
-    private static native void nativeAppendUnderlineSpan(long underlinePtr, int start, int end);
-    private static native void nativeAppendBackgroundColorSpan(long underlinePtr, int start,
-            int end, int backgroundColor);
+    private static native void nativeAppendUnderlineSpan(long spanPtr, int start, int end);
+    private static native void nativeAppendBackgroundColorSpan(
+            long spanPtr, int start, int end, int backgroundColor);
     private native void nativeSetComposingText(long nativeImeAdapterAndroid, CharSequence text,
             String textStr, int newCursorPosition);
     private native void nativeCommitText(
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeUtils.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeUtils.java
index 8ef905d6..b667c45b 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ImeUtils.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeUtils.java
@@ -44,7 +44,6 @@
             outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
         }
 
-        int imeAction = 0;
         if (inputMode == WebTextInputMode.DEFAULT) {
             if (inputType == TextInputType.TEXT) {
                 // Normal text field
@@ -112,21 +111,8 @@
             }
         }
 
-        if (inputMode == WebTextInputMode.DEFAULT && inputType == TextInputType.SEARCH) {
-            imeAction |= EditorInfo.IME_ACTION_SEARCH;
-        } else if ((outAttrs.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0) {
-            // For textarea that sends you to another webpage on enter key press using
-            // JavaScript, we will only show ENTER.
-            imeAction |= EditorInfo.IME_ACTION_NONE;
-        } else if ((inputFlags & WebTextInputFlags.HAVE_NEXT_FOCUSABLE_ELEMENT) != 0) {
-            imeAction |= EditorInfo.IME_ACTION_NEXT;
-        } else {
-            // For last element inside form, we should give preference to GO key as PREVIOUS
-            // has less importance in those cases.
-            imeAction |= EditorInfo.IME_ACTION_GO;
-        }
-
-        outAttrs.imeOptions |= imeAction;
+        outAttrs.imeOptions |= getImeAction(inputType, inputFlags, inputMode,
+                (outAttrs.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0);
 
         // Handling of autocapitalize. Blink will send the flag taking into account the element's
         // type. This is not using AutocapitalizeNone because Android does not autocapitalize by
@@ -148,6 +134,25 @@
         outAttrs.initialSelEnd = initialSelEnd;
     }
 
+    private static int getImeAction(
+            int inputType, int inputFlags, int inputMode, boolean isMultiLineInput) {
+        int imeAction = 0;
+        if (inputMode == WebTextInputMode.DEFAULT && inputType == TextInputType.SEARCH) {
+            imeAction |= EditorInfo.IME_ACTION_SEARCH;
+        } else if (isMultiLineInput) {
+            // For textarea that sends you to another webpage on enter key press using
+            // JavaScript, we will only show ENTER.
+            imeAction |= EditorInfo.IME_ACTION_NONE;
+        } else if ((inputFlags & WebTextInputFlags.HAVE_NEXT_FOCUSABLE_ELEMENT) != 0) {
+            imeAction |= EditorInfo.IME_ACTION_NEXT;
+        } else {
+            // For last element inside form, we should give preference to GO key as PREVIOUS
+            // has less importance in those cases.
+            imeAction |= EditorInfo.IME_ACTION_GO;
+        }
+        return imeAction;
+    }
+
     /**
      * @param editorInfo The EditorInfo
      * @return Debug string for the given {@EditorInfo}.
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 54aadee7..360cc64 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -25,7 +25,6 @@
           "blink::mojom::OffscreenCanvasProvider",
           "blink::mojom::PermissionService",
           "blink::mojom::WebSocket",
-          "cc::mojom::SharedBitmapAllocationNotifier",
           "content::mojom::FieldTrialRecorder",
           "content::mojom::FrameSinkProvider",
           "content::mojom::MediaStreamDispatcherHost",
@@ -47,7 +46,8 @@
           "resource_coordinator::mojom::CoordinationUnit",
           "shape_detection::mojom::TextDetection",
           "storage::mojom::BlobRegistry",
-          "ui::mojom::Gpu"
+          "ui::mojom::Gpu",
+          "viz::mojom::SharedBitmapAllocationNotifier"
         ],
         "geolocation_config": [
           "device::mojom::GeolocationConfig"
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 8349727..aa409f7 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -231,6 +231,7 @@
     "restore_type.h",
     "save_page_type.h",
     "screen_orientation_delegate.h",
+    "security_style_explanation.cc",
     "security_style_explanation.h",
     "security_style_explanations.cc",
     "security_style_explanations.h",
diff --git a/content/public/browser/browser_associated_interface.h b/content/public/browser/browser_associated_interface.h
index 2b86e21..5fedb75 100644
--- a/content/public/browser/browser_associated_interface.h
+++ b/content/public/browser/browser_associated_interface.h
@@ -5,11 +5,11 @@
 #ifndef CONTENT_BROWSER_BROWSER_ASSOCIATED_INTERFACE_H_
 #define CONTENT_BROWSER_BROWSER_ASSOCIATED_INTERFACE_H_
 
-#include <memory>
 #include <string>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_thread.h"
@@ -55,7 +55,6 @@
   // |filter| and |impl| must live at least as long as this object.
   BrowserAssociatedInterface(BrowserMessageFilter* filter, Interface* impl)
       : internal_state_(new InternalState(impl)) {
-    internal_state_->Initialize();
     filter->AddAssociatedInterface(
         Interface::Name_,
         base::Bind(&InternalState::BindRequest, internal_state_),
@@ -69,16 +68,8 @@
 
   class InternalState : public base::RefCountedThreadSafe<InternalState> {
    public:
-    explicit InternalState(Interface* impl) : impl_(impl) {}
-
-    void Initialize() {
-      if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
-        BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                                base::Bind(&InternalState::Initialize, this));
-        return;
-      }
-      bindings_.reset(new mojo::AssociatedBindingSet<Interface>);
-    }
+    explicit InternalState(Interface* impl)
+        : impl_(impl), bindings_(base::in_place) {}
 
     void ClearBindings() {
       if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
@@ -106,7 +97,7 @@
     ~InternalState() {}
 
     Interface* impl_;
-    std::unique_ptr<mojo::AssociatedBindingSet<Interface>> bindings_;
+    base::Optional<mojo::AssociatedBindingSet<Interface>> bindings_;
 
     DISALLOW_COPY_AND_ASSIGN(InternalState);
   };
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index 238a1a6..da30dc5 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -130,10 +130,10 @@
   virtual RendererAudioOutputStreamFactoryContext*
   GetRendererAudioOutputStreamFactoryContext() = 0;
 
-  // Called when an audio stream is added or removed and used to determine if
-  // the process should be backgrounded or not.
-  virtual void OnAudioStreamAdded() = 0;
-  virtual void OnAudioStreamRemoved() = 0;
+  // Called when a video capture stream or an audio stream is added or removed
+  // and used to determine if the process should be backgrounded or not.
+  virtual void OnMediaStreamAdded() = 0;
+  virtual void OnMediaStreamRemoved() = 0;
 
   // Indicates whether the current RenderProcessHost is exclusively hosting
   // guest RenderFrames. Not all guest RenderFrames are created equal.  A guest,
diff --git a/content/public/browser/security_style_explanation.cc b/content/public/browser/security_style_explanation.cc
new file mode 100644
index 0000000..9d497471
--- /dev/null
+++ b/content/public/browser/security_style_explanation.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 "content/public/browser/security_style_explanation.h"
+
+namespace content {
+
+SecurityStyleExplanation::SecurityStyleExplanation() {}
+
+SecurityStyleExplanation::SecurityStyleExplanation(
+    const std::string& summary,
+    const std::string& description)
+    : summary(summary),
+      description(description),
+      mixed_content_type(blink::WebMixedContentContextType::kNotMixedContent) {}
+
+SecurityStyleExplanation::SecurityStyleExplanation(
+    const std::string& summary,
+    const std::string& description,
+    scoped_refptr<net::X509Certificate> certificate,
+    blink::WebMixedContentContextType mixed_content_type)
+    : summary(summary),
+      description(description),
+      certificate(certificate),
+      mixed_content_type(mixed_content_type) {}
+
+SecurityStyleExplanation::SecurityStyleExplanation(
+    const SecurityStyleExplanation& other) = default;
+
+SecurityStyleExplanation& SecurityStyleExplanation::operator=(
+    const SecurityStyleExplanation& other) = default;
+
+SecurityStyleExplanation::~SecurityStyleExplanation() {}
+
+}  // namespace content
diff --git a/content/public/browser/security_style_explanation.h b/content/public/browser/security_style_explanation.h
index e2d51b9..9f0b12b 100644
--- a/content/public/browser/security_style_explanation.h
+++ b/content/public/browser/security_style_explanation.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "content/common/content_export.h"
+#include "net/cert/x509_certificate.h"
 #include "third_party/WebKit/public/platform/WebMixedContentContextType.h"
 
 namespace content {
@@ -17,32 +18,24 @@
 // resource. An example summary phrase would be "Expired Certificate",
 // with a description along the lines of "This site's certificate chain
 // contains errors (net::CERT_DATE_INVALID)".
-struct SecurityStyleExplanation {
-  CONTENT_EXPORT SecurityStyleExplanation(){};
-  CONTENT_EXPORT SecurityStyleExplanation(const std::string& summary,
-                                          const std::string& description)
-      : summary(summary),
-        description(description),
-        has_certificate(false),
-        mixed_content_type(
-            blink::WebMixedContentContextType::kNotMixedContent) {}
-  CONTENT_EXPORT SecurityStyleExplanation(
+struct CONTENT_EXPORT SecurityStyleExplanation {
+  SecurityStyleExplanation();
+  SecurityStyleExplanation(const std::string& summary,
+                           const std::string& description);
+  SecurityStyleExplanation(
       const std::string& summary,
       const std::string& description,
-      bool has_certificate,
-      blink::WebMixedContentContextType mixed_content_type)
-      : summary(summary),
-        description(description),
-        has_certificate(has_certificate),
-        mixed_content_type(mixed_content_type) {}
-  CONTENT_EXPORT ~SecurityStyleExplanation() {}
+      scoped_refptr<net::X509Certificate> certificate,
+      blink::WebMixedContentContextType mixed_content_type);
+  SecurityStyleExplanation(const SecurityStyleExplanation& other);
+  SecurityStyleExplanation& operator=(const SecurityStyleExplanation& other);
+  ~SecurityStyleExplanation();
 
   std::string summary;
   std::string description;
-  // |has_certificate| indicates that this explanation has an associated
-  // certificate. UI surfaces can use this to add a button/link for viewing the
-  // certificate of the current page.
-  bool has_certificate;
+  // |certificate| indicates that this explanation has an associated
+  // certificate.
+  scoped_refptr<net::X509Certificate> certificate;
   // |mixed_content_type| indicates that the explanation describes a particular
   // type of mixed content. A value of kNotMixedContent means that the
   // explanation does not relate to mixed content. UI surfaces can use this to
diff --git a/content/public/browser/security_style_explanations.h b/content/public/browser/security_style_explanations.h
index 041236d..981970b 100644
--- a/content/public/browser/security_style_explanations.h
+++ b/content/public/browser/security_style_explanations.h
@@ -25,11 +25,10 @@
 // SecurityStyleExplanation that contains a human-readable explanation of the
 // factor. A single page may contain multiple explanations, each of which may
 // have a different severity level ("secure", "warning", "insecure" and "info").
-struct SecurityStyleExplanations {
-  CONTENT_EXPORT SecurityStyleExplanations();
-  CONTENT_EXPORT SecurityStyleExplanations(
-      const SecurityStyleExplanations& other);
-  CONTENT_EXPORT ~SecurityStyleExplanations();
+struct CONTENT_EXPORT SecurityStyleExplanations {
+  SecurityStyleExplanations();
+  SecurityStyleExplanations(const SecurityStyleExplanations& other);
+  ~SecurityStyleExplanations();
 
   // True if the page was loaded over HTTPS and ran mixed (HTTP) content
   // such as scripts.
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 30c545b..959fe72 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -14,7 +14,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/bindings_policy.h"
 #include "content/public/common/url_constants.h"
-#include "net/cert/x509_certificate.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace content {
@@ -266,11 +265,6 @@
   return blink::kWebSecurityStyleUnknown;
 }
 
-void WebContentsDelegate::ShowCertificateViewerInDevTools(
-    WebContents* web_contents,
-    scoped_refptr<net::X509Certificate> certificate) {
-}
-
 void WebContentsDelegate::RequestAppBannerFromDevTools(
     content::WebContents* web_contents) {
 }
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 3ffbea6..8b548ba 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -63,10 +63,6 @@
 class Size;
 }
 
-namespace net {
-class X509Certificate;
-}
-
 namespace url {
 class Origin;
 }
@@ -546,11 +542,6 @@
       WebContents* web_contents,
       SecurityStyleExplanations* security_style_explanations);
 
-  // Displays platform-specific (OS) dialog with the certificate details.
-  virtual void ShowCertificateViewerInDevTools(
-      WebContents* web_contents,
-      scoped_refptr<net::X509Certificate> certificate);
-
   // Requests the app banner. This method is called from the DevTools.
   virtual void RequestAppBannerFromDevTools(content::WebContents* web_contents);
 
diff --git a/content/public/common/media_stream_request.cc b/content/public/common/media_stream_request.cc
index 8512aad5..b042198 100644
--- a/content/public/common/media_stream_request.cc
+++ b/content/public/common/media_stream_request.cc
@@ -63,19 +63,21 @@
       id(id),
       video_facing(media::MEDIA_VIDEO_FACING_NONE),
       name(name),
-      input(sample_rate, channel_layout, frames_per_buffer) {}
+      input(media::AudioParameters::AUDIO_FAKE,
+            static_cast<media::ChannelLayout>(channel_layout),
+            sample_rate,
+            0,
+            frames_per_buffer) {}
 
 MediaStreamDevice::MediaStreamDevice(const MediaStreamDevice& other) = default;
 
 MediaStreamDevice::~MediaStreamDevice() {}
 
 bool MediaStreamDevice::IsEqual(const MediaStreamDevice& second) const {
-  const AudioDeviceParameters& input_second = second.input;
-  return type == second.type &&
-      name == second.name &&
-      id == second.id &&
-      input.sample_rate == input_second.sample_rate &&
-      input.channel_layout == input_second.channel_layout;
+  const media::AudioParameters& input_second = second.input;
+  return type == second.type && name == second.name && id == second.id &&
+         input.sample_rate() == input_second.sample_rate() &&
+         input.channel_layout() == input_second.channel_layout();
 }
 
 MediaStreamDevices::MediaStreamDevices() {}
@@ -94,23 +96,6 @@
   return NULL;
 }
 
-MediaStreamDevice::AudioDeviceParameters::AudioDeviceParameters()
-    : sample_rate(), channel_layout(), frames_per_buffer(), effects() {}
-
-MediaStreamDevice::AudioDeviceParameters::AudioDeviceParameters(
-    int sample_rate,
-    int channel_layout,
-    int frames_per_buffer)
-    : sample_rate(sample_rate),
-      channel_layout(channel_layout),
-      frames_per_buffer(frames_per_buffer),
-      effects() {}
-
-MediaStreamDevice::AudioDeviceParameters::AudioDeviceParameters(
-    const AudioDeviceParameters& other) = default;
-
-MediaStreamDevice::AudioDeviceParameters::~AudioDeviceParameters() {}
-
 MediaStreamRequest::MediaStreamRequest(
     int render_process_id,
     int render_frame_id,
diff --git a/content/public/common/media_stream_request.h b/content/public/common/media_stream_request.h
index f3b09c0..36821c4 100644
--- a/content/public/common/media_stream_request.h
+++ b/content/public/common/media_stream_request.h
@@ -122,49 +122,19 @@
   // The device's "friendly" name. Not guaranteed to be unique.
   std::string name;
 
-  // Contains properties that match directly with those with the same name
-  // in media::AudioParameters.
-  // TODO(ajm): Remove this type and use media::AudioParameters directly.
-  struct CONTENT_EXPORT AudioDeviceParameters {
-    AudioDeviceParameters();
-    AudioDeviceParameters(int sample_rate,
-                          int channel_layout,
-                          int frames_per_buffer);
-    AudioDeviceParameters(const AudioDeviceParameters& other);
-
-    ~AudioDeviceParameters();
-
-    // Preferred sample rate in samples per second for the device.
-    int sample_rate;
-
-    // Preferred channel configuration for the device.
-    // TODO(henrika): ideally, we would like to use media::ChannelLayout here
-    // but including media/base/channel_layout.h violates checkdeps rules.
-    int channel_layout;
-
-    // Preferred number of frames per buffer for the device.  This is filled
-    // in on the browser side and can be used by the renderer to match the
-    // expected browser side settings and avoid unnecessary buffering.
-    // See media::AudioParameters for more.
-    int frames_per_buffer;
-
-    // See media::AudioParameters::PlatformEffectsMask.
-    int effects;
-
-    std::vector<media::Point> mic_positions;
-  };
-
   // These below two member variables are valid only when the type of device is
   // audio (i.e. IsAudioInputMediaType returns true).
 
   // Contains the device properties of the capture device.
-  AudioDeviceParameters input;
+  media::AudioParameters input =
+      media::AudioParameters::UnavailableDeviceParams();
 
   // If the capture device has an associated output device (e.g. headphones),
   // this will contain the properties for the output device.  If no such device
   // exists (e.g. webcam w/mic), then the value of this member will be all
   // zeros.
-  AudioDeviceParameters matched_output;
+  media::AudioParameters matched_output =
+      media::AudioParameters::UnavailableDeviceParams();
 
   // This field is optional and available only for some camera models.
   base::Optional<CameraCalibration> camera_calibration;
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 0f95e02..dcc3163 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -159,9 +159,9 @@
   return nullptr;
 }
 
-void MockRenderProcessHost::OnAudioStreamAdded() {}
+void MockRenderProcessHost::OnMediaStreamAdded() {}
 
-void MockRenderProcessHost::OnAudioStreamRemoved() {}
+void MockRenderProcessHost::OnMediaStreamRemoved() {}
 
 StoragePartition* MockRenderProcessHost::GetStoragePartition() const {
   return BrowserContext::GetDefaultStoragePartition(browser_context_);
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 9dd2787..d67bd9d 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -66,8 +66,8 @@
   bool IsForGuestsOnly() const override;
   RendererAudioOutputStreamFactoryContext*
   GetRendererAudioOutputStreamFactoryContext() override;
-  void OnAudioStreamAdded() override;
-  void OnAudioStreamRemoved() override;
+  void OnMediaStreamAdded() override;
+  void OnMediaStreamRemoved() override;
   StoragePartition* GetStoragePartition() const override;
   virtual void AddWord(const base::string16& word);
   bool Shutdown(int exit_code, bool wait) override;
diff --git a/content/public/test/text_input_test_utils.cc b/content/public/test/text_input_test_utils.cc
index 65ec8d2..1e7153a 100644
--- a/content/public/test/text_input_test_utils.cc
+++ b/content/public/test/text_input_test_utils.cc
@@ -22,8 +22,8 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/test_utils.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
-#include "ui/base/ime/composition_underline.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
+#include "ui/base/ime/ime_text_span.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/input_method_observer.h"
 
@@ -268,22 +268,22 @@
 void SendImeCommitTextToWidget(
     RenderWidgetHost* rwh,
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int relative_cursor_pos) {
   RenderWidgetHostImpl::From(rwh)->ImeCommitText(
-      text, underlines, replacement_range, relative_cursor_pos);
+      text, ime_text_spans, replacement_range, relative_cursor_pos);
 }
 
 void SendImeSetCompositionTextToWidget(
     RenderWidgetHost* rwh,
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int selection_start,
     int selection_end) {
   RenderWidgetHostImpl::From(rwh)->ImeSetComposition(
-      text, underlines, replacement_range, selection_start, selection_end);
+      text, ime_text_spans, replacement_range, selection_start, selection_end);
 }
 
 bool DestroyRenderWidgetHost(int32_t process_id,
diff --git a/content/public/test/text_input_test_utils.h b/content/public/test/text_input_test_utils.h
index c8d9775..80c055c 100644
--- a/content/public/test/text_input_test_utils.h
+++ b/content/public/test/text_input_test_utils.h
@@ -26,7 +26,7 @@
 }
 
 namespace ui {
-struct CompositionUnderline;
+struct ImeTextSpan;
 }
 
 namespace content {
@@ -74,7 +74,7 @@
 void SendImeCommitTextToWidget(
     RenderWidgetHost* rwh,
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int relative_cursor_pos);
 
@@ -83,7 +83,7 @@
 void SendImeSetCompositionTextToWidget(
     RenderWidgetHost* rwh,
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int selection_start,
     int selection_end);
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 88ef96b5..9e281e4 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -551,7 +551,7 @@
 
 bool BrowserPlugin::SetComposition(
     const blink::WebString& text,
-    const blink::WebVector<blink::WebCompositionUnderline>& underlines,
+    const blink::WebVector<blink::WebImeTextSpan>& ime_text_spans,
     const blink::WebRange& replacementRange,
     int selectionStart,
     int selectionEnd) {
@@ -560,8 +560,8 @@
 
   BrowserPluginHostMsg_SetComposition_Params params;
   params.text = text.Utf16();
-  for (size_t i = 0; i < underlines.size(); ++i) {
-    params.underlines.push_back(underlines[i]);
+  for (size_t i = 0; i < ime_text_spans.size(); ++i) {
+    params.ime_text_spans.push_back(ime_text_spans[i]);
   }
 
   params.replacement_range =
@@ -580,15 +580,15 @@
 
 bool BrowserPlugin::CommitText(
     const blink::WebString& text,
-    const blink::WebVector<blink::WebCompositionUnderline>& underlines,
+    const blink::WebVector<blink::WebImeTextSpan>& ime_text_spans,
     const blink::WebRange& replacementRange,
     int relative_cursor_pos) {
   if (!attached())
     return false;
 
-  std::vector<blink::WebCompositionUnderline> std_underlines;
-  for (size_t i = 0; i < underlines.size(); ++i) {
-    std_underlines.push_back(underlines[i]);
+  std::vector<blink::WebImeTextSpan> std_ime_text_spans;
+  for (size_t i = 0; i < ime_text_spans.size(); ++i) {
+    std_ime_text_spans.push_back(ime_text_spans[i]);
   }
   gfx::Range replacement_range =
       replacementRange.IsNull()
@@ -597,7 +597,7 @@
                        static_cast<uint32_t>(replacementRange.EndOffset()));
 
   BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_ImeCommitText(
-      browser_plugin_instance_id_, text.Utf16(), std_underlines,
+      browser_plugin_instance_id_, text.Utf16(), std_ime_text_spans,
       replacement_range, relative_cursor_pos));
   // TODO(kochi): This assumes the IPC handling always succeeds.
   return true;
diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h
index 637002e8..74abac0 100644
--- a/content/renderer/browser_plugin/browser_plugin.h
+++ b/content/renderer/browser_plugin/browser_plugin.h
@@ -14,8 +14,8 @@
 #include "base/sequenced_task_runner_helpers.h"
 #include "content/renderer/mouse_lock_dispatcher.h"
 #include "content/renderer/render_view_impl.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "third_party/WebKit/public/web/WebDragStatus.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "third_party/WebKit/public/web/WebInputMethodController.h"
 #include "third_party/WebKit/public/web/WebNode.h"
 
@@ -111,15 +111,14 @@
                           const blink::WebString& value) override;
   bool SetComposition(
       const blink::WebString& text,
-      const blink::WebVector<blink::WebCompositionUnderline>& underlines,
+      const blink::WebVector<blink::WebImeTextSpan>& ime_text_spans,
       const blink::WebRange& replacementRange,
       int selectionStart,
       int selectionEnd) override;
-  bool CommitText(
-      const blink::WebString& text,
-      const blink::WebVector<blink::WebCompositionUnderline>& underlines,
-      const blink::WebRange& replacementRange,
-      int relative_cursor_pos) override;
+  bool CommitText(const blink::WebString& text,
+                  const blink::WebVector<blink::WebImeTextSpan>& ime_text_spans,
+                  const blink::WebRange& replacementRange,
+                  int relative_cursor_pos) override;
   bool FinishComposingText(
       blink::WebInputMethodController::ConfirmCompositionBehavior
           selection_behavior) override;
diff --git a/content/renderer/dom_storage/dom_storage_cached_area.cc b/content/renderer/dom_storage/dom_storage_cached_area.cc
index b2686d9..12480ea8c 100644
--- a/content/renderer/dom_storage/dom_storage_cached_area.cc
+++ b/content/renderer/dom_storage/dom_storage_cached_area.cc
@@ -53,16 +53,15 @@
     return false;
 
   PrimeIfNeeded(connection_id);
-  base::NullableString16 unused;
-  if (!map_->SetItem(key, value, &unused))
+  base::NullableString16 old_value;
+  if (!map_->SetItem(key, value, &old_value))
     return false;
 
   // Ignore mutations to 'key' until OnSetItemComplete.
   ignore_key_mutations_[key]++;
-  proxy_->SetItem(
-      connection_id, key, value, page_url,
-      base::Bind(&DOMStorageCachedArea::OnSetItemComplete,
-                 weak_factory_.GetWeakPtr(), key));
+  proxy_->SetItem(connection_id, key, value, old_value, page_url,
+                  base::Bind(&DOMStorageCachedArea::OnSetItemComplete,
+                             weak_factory_.GetWeakPtr(), key));
   return true;
 }
 
@@ -70,16 +69,16 @@
                                       const base::string16& key,
                                       const GURL& page_url) {
   PrimeIfNeeded(connection_id);
-  base::string16 unused;
-  if (!map_->RemoveItem(key, &unused))
+  base::string16 old_value;
+  if (!map_->RemoveItem(key, &old_value))
     return;
 
   // Ignore mutations to 'key' until OnRemoveItemComplete.
   ignore_key_mutations_[key]++;
-  proxy_->RemoveItem(
-      connection_id, key, page_url,
-      base::Bind(&DOMStorageCachedArea::OnRemoveItemComplete,
-                 weak_factory_.GetWeakPtr(), key));
+  proxy_->RemoveItem(connection_id, key,
+                     base::NullableString16(old_value, false), page_url,
+                     base::Bind(&DOMStorageCachedArea::OnRemoveItemComplete,
+                                weak_factory_.GetWeakPtr(), key));
 }
 
 void DOMStorageCachedArea::Clear(int connection_id, const GURL& page_url) {
diff --git a/content/renderer/dom_storage/dom_storage_cached_area_unittest.cc b/content/renderer/dom_storage/dom_storage_cached_area_unittest.cc
index 4c433271..46b3815e 100644
--- a/content/renderer/dom_storage/dom_storage_cached_area_unittest.cc
+++ b/content/renderer/dom_storage/dom_storage_cached_area_unittest.cc
@@ -37,6 +37,7 @@
   void SetItem(int connection_id,
                const base::string16& key,
                const base::string16& value,
+               const base::NullableString16& old_value,
                const GURL& page_url,
                const CompletionCallback& callback) override {
     pending_callbacks_.push_back(callback);
@@ -49,6 +50,7 @@
 
   void RemoveItem(int connection_id,
                   const base::string16& key,
+                  const base::NullableString16& old_value,
                   const GURL& page_url,
                   const CompletionCallback& callback) override {
     pending_callbacks_.push_back(callback);
diff --git a/content/renderer/dom_storage/dom_storage_dispatcher.cc b/content/renderer/dom_storage/dom_storage_dispatcher.cc
index b52367a..9d2ec453 100644
--- a/content/renderer/dom_storage/dom_storage_dispatcher.cc
+++ b/content/renderer/dom_storage/dom_storage_dispatcher.cc
@@ -114,10 +114,12 @@
   void SetItem(int connection_id,
                const base::string16& key,
                const base::string16& value,
+               const base::NullableString16& old_value,
                const GURL& page_url,
                const CompletionCallback& callback) override;
   void RemoveItem(int connection_id,
                   const base::string16& key,
+                  const base::NullableString16& old_value,
                   const GURL& page_url,
                   const CompletionCallback& callback) override;
   void ClearArea(int connection_id,
@@ -243,20 +245,26 @@
 }
 
 void DomStorageDispatcher::ProxyImpl::SetItem(
-    int connection_id, const base::string16& key,
-    const base::string16& value, const GURL& page_url,
+    int connection_id,
+    const base::string16& key,
+    const base::string16& value,
+    const base::NullableString16& old_value,
+    const GURL& page_url,
     const CompletionCallback& callback) {
   PushPendingCallback(callback);
   throttling_filter_->SendThrottled(new DOMStorageHostMsg_SetItem(
-      connection_id, key, value, page_url));
+      connection_id, key, value, old_value, page_url));
 }
 
 void DomStorageDispatcher::ProxyImpl::RemoveItem(
-    int connection_id, const base::string16& key,  const GURL& page_url,
+    int connection_id,
+    const base::string16& key,
+    const base::NullableString16& old_value,
+    const GURL& page_url,
     const CompletionCallback& callback) {
   PushPendingCallback(callback);
   throttling_filter_->SendThrottled(new DOMStorageHostMsg_RemoveItem(
-      connection_id, key, page_url));
+      connection_id, key, old_value, page_url));
 }
 
 void DomStorageDispatcher::ProxyImpl::ClearArea(int connection_id,
diff --git a/content/renderer/dom_storage/dom_storage_proxy.h b/content/renderer/dom_storage/dom_storage_proxy.h
index 80cd87cdc..8fbd361 100644
--- a/content/renderer/dom_storage/dom_storage_proxy.h
+++ b/content/renderer/dom_storage/dom_storage_proxy.h
@@ -26,11 +26,13 @@
   virtual void SetItem(int connection_id,
                        const base::string16& key,
                        const base::string16& value,
+                       const base::NullableString16& old_value,
                        const GURL& page_url,
                        const CompletionCallback& callback) = 0;
 
   virtual void RemoveItem(int connection_id,
                           const base::string16& key,
+                          const base::NullableString16& old_value,
                           const GURL& page_url,
                           const CompletionCallback& callback) = 0;
 
diff --git a/content/renderer/input/frame_input_handler_impl.cc b/content/renderer/input/frame_input_handler_impl.cc
index ba35f657..637ebdcb 100644
--- a/content/renderer/input/frame_input_handler_impl.cc
+++ b/content/renderer/input/frame_input_handler_impl.cc
@@ -66,11 +66,11 @@
 void FrameInputHandlerImpl::SetCompositionFromExistingText(
     int32_t start,
     int32_t end,
-    const std::vector<ui::CompositionUnderline>& ui_underlines) {
+    const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
   if (!main_thread_task_runner_->BelongsToCurrentThread()) {
     RunOnMainThread(
         base::Bind(&FrameInputHandlerImpl::SetCompositionFromExistingText,
-                   weak_this_, start, end, ui_underlines));
+                   weak_this_, start, end, ui_ime_text_spans));
     return;
   }
 
@@ -78,16 +78,17 @@
     return;
 
   ImeEventGuard guard(render_frame_->GetRenderWidget());
-  std::vector<blink::WebCompositionUnderline> underlines;
-  for (const auto& underline : ui_underlines) {
-    blink::WebCompositionUnderline blink_underline(
-        underline.start_offset, underline.end_offset, underline.color,
-        underline.thick, underline.background_color);
-    underlines.push_back(blink_underline);
+  std::vector<blink::WebImeTextSpan> ime_text_spans;
+  for (const auto& ime_text_span : ui_ime_text_spans) {
+    blink::WebImeTextSpan blink_ime_text_span(
+        ime_text_span.start_offset, ime_text_span.end_offset,
+        ime_text_span.color, ime_text_span.thick,
+        ime_text_span.background_color);
+    ime_text_spans.push_back(blink_ime_text_span);
   }
 
   render_frame_->GetWebFrame()->SetCompositionFromExistingText(start, end,
-                                                               underlines);
+                                                               ime_text_spans);
 }
 
 void FrameInputHandlerImpl::ExtendSelectionAndDelete(int32_t before,
diff --git a/content/renderer/input/frame_input_handler_impl.h b/content/renderer/input/frame_input_handler_impl.h
index 705f816..6519950 100644
--- a/content/renderer/input/frame_input_handler_impl.h
+++ b/content/renderer/input/frame_input_handler_impl.h
@@ -44,7 +44,7 @@
   void SetCompositionFromExistingText(
       int32_t start,
       int32_t end,
-      const std::vector<ui::CompositionUnderline>& underlines) override;
+      const std::vector<ui::ImeTextSpan>& ime_text_spans) override;
   void ExtendSelectionAndDelete(int32_t before, int32_t after) override;
   void DeleteSurroundingText(int32_t before, int32_t after) override;
   void DeleteSurroundingTextInCodePoints(int32_t before,
diff --git a/content/renderer/input/widget_input_handler_impl.cc b/content/renderer/input/widget_input_handler_impl.cc
index a042d773..d82da0f 100644
--- a/content/renderer/input/widget_input_handler_impl.cc
+++ b/content/renderer/input/widget_input_handler_impl.cc
@@ -24,17 +24,17 @@
 
 namespace {
 
-std::vector<blink::WebCompositionUnderline>
-ConvertUIUnderlinesToBlinkUnderlines(
-    const std::vector<ui::CompositionUnderline>& ui_underlines) {
-  std::vector<blink::WebCompositionUnderline> underlines;
-  for (const auto& underline : ui_underlines) {
-    blink::WebCompositionUnderline blink_underline(
-        underline.start_offset, underline.end_offset, underline.color,
-        underline.thick, underline.background_color);
-    underlines.push_back(blink_underline);
+std::vector<blink::WebImeTextSpan> ConvertUIImeTextSpansToBlinkImeTextSpans(
+    const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
+  std::vector<blink::WebImeTextSpan> ime_text_spans;
+  for (const auto& ime_text_span : ui_ime_text_spans) {
+    blink::WebImeTextSpan blink_ime_text_span(
+        ime_text_span.start_offset, ime_text_span.end_offset,
+        ime_text_span.color, ime_text_span.thick,
+        ime_text_span.background_color);
+    ime_text_spans.push_back(blink_ime_text_span);
   }
-  return underlines;
+  return ime_text_spans;
 }
 }  // namespace
 
@@ -89,24 +89,25 @@
 
 void WidgetInputHandlerImpl::ImeSetComposition(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& range,
     int32_t start,
     int32_t end) {
-  RunOnMainThread(base::Bind(
-      &RenderWidget::OnImeSetComposition, render_widget_, text,
-      ConvertUIUnderlinesToBlinkUnderlines(underlines), range, start, end));
+  RunOnMainThread(
+      base::Bind(&RenderWidget::OnImeSetComposition, render_widget_, text,
+                 ConvertUIImeTextSpansToBlinkImeTextSpans(ime_text_spans),
+                 range, start, end));
 }
 
 void WidgetInputHandlerImpl::ImeCommitText(
     const base::string16& text,
-    const std::vector<ui::CompositionUnderline>& underlines,
+    const std::vector<ui::ImeTextSpan>& ime_text_spans,
     const gfx::Range& range,
     int32_t relative_cursor_position) {
-  RunOnMainThread(base::Bind(&RenderWidget::OnImeCommitText, render_widget_,
-                             text,
-                             ConvertUIUnderlinesToBlinkUnderlines(underlines),
-                             range, relative_cursor_position));
+  RunOnMainThread(
+      base::Bind(&RenderWidget::OnImeCommitText, render_widget_, text,
+                 ConvertUIImeTextSpansToBlinkImeTextSpans(ime_text_spans),
+                 range, relative_cursor_position));
 }
 
 void WidgetInputHandlerImpl::ImeFinishComposingText(bool keep_selection) {
diff --git a/content/renderer/input/widget_input_handler_impl.h b/content/renderer/input/widget_input_handler_impl.h
index c05ac43..5c5cddc2 100644
--- a/content/renderer/input/widget_input_handler_impl.h
+++ b/content/renderer/input/widget_input_handler_impl.h
@@ -36,14 +36,13 @@
   void SetEditCommandsForNextKeyEvent(
       const std::vector<EditCommand>& commands) override;
   void CursorVisibilityChanged(bool visible) override;
-  void ImeSetComposition(
-      const base::string16& text,
-      const std::vector<ui::CompositionUnderline>& underlines,
-      const gfx::Range& range,
-      int32_t start,
-      int32_t end) override;
+  void ImeSetComposition(const base::string16& text,
+                         const std::vector<ui::ImeTextSpan>& ime_text_spans,
+                         const gfx::Range& range,
+                         int32_t start,
+                         int32_t end) override;
   void ImeCommitText(const base::string16& text,
-                     const std::vector<ui::CompositionUnderline>& underlines,
+                     const std::vector<ui::ImeTextSpan>& ime_text_spans,
                      const gfx::Range& range,
                      int32_t relative_cursor_position) override;
   void ImeFinishComposingText(bool keep_selection) override;
diff --git a/content/renderer/media/local_media_stream_audio_source.cc b/content/renderer/media/local_media_stream_audio_source.cc
index 2c7f1aa8..4d2e913 100644
--- a/content/renderer/media/local_media_stream_audio_source.cc
+++ b/content/renderer/media/local_media_stream_audio_source.cc
@@ -22,24 +22,23 @@
   MediaStreamSource::SetDeviceInfo(device_info);
 
   // If the device buffer size was not provided, use a default.
-  int frames_per_buffer = device_info.device.input.frames_per_buffer;
+  int frames_per_buffer = device_info.device.input.frames_per_buffer();
   if (frames_per_buffer <= 0) {
     // TODO(miu): Like in ProcessedLocalAudioSource::GetBufferSize(), we should
     // re-evaluate whether Android needs special treatment here. Or, perhaps we
     // should just DCHECK_GT(device_info...frames_per_buffer, 0)?
     // http://crbug.com/638081
 #if defined(OS_ANDROID)
-    frames_per_buffer = device_info.device.input.sample_rate / 50;  // 20 ms
+    frames_per_buffer = device_info.device.input.sample_rate() / 50;  // 20 ms
 #else
-    frames_per_buffer = device_info.device.input.sample_rate / 100;  // 10 ms
+    frames_per_buffer = device_info.device.input.sample_rate() / 100;  // 10 ms
 #endif
   }
 
   MediaStreamAudioSource::SetFormat(media::AudioParameters(
       media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
-      static_cast<media::ChannelLayout>(
-          device_info.device.input.channel_layout),
-      device_info.device.input.sample_rate,
+      device_info.device.input.channel_layout(),
+      device_info.device.input.sample_rate(),
       16,  // Legacy parameter (data is always in 32-bit float format).
       frames_per_buffer));
 }
diff --git a/content/renderer/media/media_stream_audio_processor_options.cc b/content/renderer/media/media_stream_audio_processor_options.cc
index 5fdd272..70ce8ad6 100644
--- a/content/renderer/media/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/media_stream_audio_processor_options.cc
@@ -292,9 +292,9 @@
 // static
 AudioProcessingProperties AudioProcessingProperties::FromConstraints(
     const blink::WebMediaConstraints& constraints,
-    const MediaStreamDevice::AudioDeviceParameters& input_params) {
+    const media::AudioParameters& input_params) {
   DCHECK(IsOldAudioConstraints());
-  MediaAudioConstraints audio_constraints(constraints, input_params.effects);
+  MediaAudioConstraints audio_constraints(constraints, input_params.effects());
   AudioProcessingProperties properties;
   properties.enable_sw_echo_cancellation =
       audio_constraints.GetEchoCancellationProperty();
@@ -524,14 +524,14 @@
 
 std::vector<media::Point> GetArrayGeometryPreferringConstraints(
     const MediaAudioConstraints& audio_constraints,
-    const MediaStreamDevice::AudioDeviceParameters& input_params) {
+    const media::AudioParameters& input_params) {
   const std::string constraints_geometry =
       audio_constraints.GetGoogArrayGeometry();
 
   // Give preference to the audio constraint over the device-supplied mic
   // positions. This is mainly for testing purposes.
   return constraints_geometry.empty()
-             ? input_params.mic_positions
+             ? input_params.mic_positions()
              : media::ParsePointsFromString(constraints_geometry);
 }
 
diff --git a/content/renderer/media/media_stream_audio_processor_options.h b/content/renderer/media/media_stream_audio_processor_options.h
index dde2d23..ee9619e 100644
--- a/content/renderer/media/media_stream_audio_processor_options.h
+++ b/content/renderer/media/media_stream_audio_processor_options.h
@@ -117,7 +117,7 @@
   // TODO(guidou): Remove this function. http://crbug.com/706408
   static AudioProcessingProperties FromConstraints(
       const blink::WebMediaConstraints& constraints,
-      const MediaStreamDevice::AudioDeviceParameters& input_params);
+      const media::AudioParameters& input_params);
 
   bool enable_sw_echo_cancellation = true;
   bool disable_hw_echo_cancellation = false;
@@ -213,7 +213,7 @@
 // TODO(guidou): Remove this function. http://crbug.com/706408
 CONTENT_EXPORT std::vector<media::Point> GetArrayGeometryPreferringConstraints(
     const MediaAudioConstraints& audio_constraints,
-    const MediaStreamDevice::AudioDeviceParameters& input_params);
+    const media::AudioParameters& input_params);
 
 // TODO(guidou): Remove this function. http://crbug.com/706408
 CONTENT_EXPORT bool IsOldAudioConstraints();
diff --git a/content/renderer/media/media_stream_audio_processor_unittest.cc b/content/renderer/media/media_stream_audio_processor_unittest.cc
index 9aada88..5f55168 100644
--- a/content/renderer/media/media_stream_audio_processor_unittest.cc
+++ b/content/renderer/media/media_stream_audio_processor_unittest.cc
@@ -220,7 +220,6 @@
 
   base::MessageLoop main_thread_message_loop_;
   media::AudioParameters params_;
-  MediaStreamDevice::AudioDeviceParameters input_device_params_;
 
   // TODO(guidou): Remove this field. http://crbug.com/706408
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -383,7 +382,7 @@
   {
     // Both geometries empty.
     MockConstraintFactory constraint_factory;
-    MediaStreamDevice::AudioDeviceParameters input_params;
+    media::AudioParameters input_params;
 
     const auto& actual_geometry = GetArrayGeometryPreferringConstraints(
         MakeMediaAudioConstraints(constraint_factory), input_params);
@@ -392,9 +391,13 @@
   {
     // Constraints geometry empty.
     MockConstraintFactory constraint_factory;
-    MediaStreamDevice::AudioDeviceParameters input_params;
-    input_params.mic_positions.push_back(media::Point(0, 0, 0));
-    input_params.mic_positions.push_back(media::Point(0, 0.05f, 0));
+
+    std::vector<media::Point> mic_positions;
+    mic_positions.push_back(media::Point(0, 0, 0));
+    mic_positions.push_back(media::Point(0, 0.05f, 0));
+
+    media::AudioParameters input_params;
+    input_params.set_mic_positions(mic_positions);
 
     const auto& actual_geometry = GetArrayGeometryPreferringConstraints(
         MakeMediaAudioConstraints(constraint_factory), input_params);
@@ -405,7 +408,7 @@
     MockConstraintFactory constraint_factory;
     constraint_factory.AddAdvanced().goog_array_geometry.SetExact(
         blink::WebString::FromUTF8("-0.02 0 0 0.02 0 0"));
-    MediaStreamDevice::AudioDeviceParameters input_params;
+    media::AudioParameters input_params;
 
     const auto& actual_geometry = GetArrayGeometryPreferringConstraints(
         MakeMediaAudioConstraints(constraint_factory), input_params);
@@ -416,9 +419,13 @@
     MockConstraintFactory constraint_factory;
     constraint_factory.AddAdvanced().goog_array_geometry.SetExact(
         blink::WebString::FromUTF8("-0.02 0 0 0.02 0 0"));
-    MediaStreamDevice::AudioDeviceParameters input_params;
-    input_params.mic_positions.push_back(media::Point(0, 0, 0));
-    input_params.mic_positions.push_back(media::Point(0, 0.05f, 0));
+
+    std::vector<media::Point> mic_positions;
+    mic_positions.push_back(media::Point(0, 0, 0));
+    mic_positions.push_back(media::Point(0, 0.05f, 0));
+
+    media::AudioParameters input_params;
+    input_params.set_mic_positions(mic_positions);
 
     // Constraints geometry is preferred.
     const auto& actual_geometry = GetArrayGeometryPreferringConstraints(
diff --git a/content/renderer/media/mock_media_stream_dispatcher.cc b/content/renderer/media/mock_media_stream_dispatcher.cc
index a4b3fe5..5af1804c 100644
--- a/content/renderer/media/mock_media_stream_dispatcher.cc
+++ b/content/renderer/media/mock_media_stream_dispatcher.cc
@@ -96,9 +96,10 @@
         kAudioOutputDeviceIdPrefix + base::IntToString(session_id_);
   }
   audio.session_id = session_id_;
-  audio.device.input.sample_rate = media::AudioParameters::kAudioCDSampleRate;
-  audio.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO;
-  audio.device.input.frames_per_buffer = audio.device.input.sample_rate / 100;
+  audio.device.input = media::AudioParameters(
+      media::AudioParameters::AUDIO_FAKE, media::CHANNEL_LAYOUT_STEREO,
+      media::AudioParameters::kAudioCDSampleRate, 0,
+      media::AudioParameters::kAudioCDSampleRate / 100);
   audio_input_array_.push_back(audio);
 }
 
diff --git a/content/renderer/media/user_media_client_impl.cc b/content/renderer/media/user_media_client_impl.cc
index ed7ec65..bb26affa 100644
--- a/content/renderer/media/user_media_client_impl.cc
+++ b/content/renderer/media/user_media_client_impl.cc
@@ -1086,7 +1086,7 @@
     for (auto& device_info : overridden_audio_array) {
       device_info.device.matched_output_device_id = "";
       device_info.device.matched_output =
-          MediaStreamDevice::AudioDeviceParameters();
+          media::AudioParameters::UnavailableDeviceParams();
     }
   }
 
diff --git a/content/renderer/media/webrtc/processed_local_audio_source.cc b/content/renderer/media/webrtc/processed_local_audio_source.cc
index 6fea798..d3a4a35 100644
--- a/content/renderer/media/webrtc/processed_local_audio_source.cc
+++ b/content/renderer/media/webrtc/processed_local_audio_source.cc
@@ -87,21 +87,22 @@
       ", channel_layout=%d, sample_rate=%d, buffer_size=%d"
       ", session_id=%d, paired_output_sample_rate=%d"
       ", paired_output_frames_per_buffer=%d, effects=%d. ",
-      consumer_render_frame_id_, device_info().device.input.channel_layout,
-      device_info().device.input.sample_rate,
-      device_info().device.input.frames_per_buffer, device_info().session_id,
-      device_info().device.matched_output.sample_rate,
-      device_info().device.matched_output.frames_per_buffer,
-      device_info().device.input.effects));
+      consumer_render_frame_id_, device_info().device.input.channel_layout(),
+      device_info().device.input.sample_rate(),
+      device_info().device.input.frames_per_buffer(), device_info().session_id,
+      device_info().device.matched_output.sample_rate(),
+      device_info().device.matched_output.frames_per_buffer(),
+      device_info().device.input.effects()));
 
   // Disable HW echo cancellation if constraints explicitly specified no
   // echo cancellation.
   if (audio_processing_properties_.disable_hw_echo_cancellation &&
-      (device_info().device.input.effects &
+      (device_info().device.input.effects() &
        media::AudioParameters::ECHO_CANCELLER)) {
     StreamDeviceInfo modified_device_info(device_info());
-    modified_device_info.device.input.effects &=
-        ~media::AudioParameters::ECHO_CANCELLER;
+    modified_device_info.device.input.set_effects(
+        modified_device_info.device.input.effects() &
+        ~media::AudioParameters::ECHO_CANCELLER);
     SetDeviceInfo(modified_device_info);
   }
 
@@ -119,9 +120,9 @@
 
   // If KEYBOARD_MIC effect is set, change the layout to the corresponding
   // layout that includes the keyboard mic.
-  media::ChannelLayout channel_layout = static_cast<media::ChannelLayout>(
-      device_info().device.input.channel_layout);
-  if ((device_info().device.input.effects &
+  media::ChannelLayout channel_layout =
+      device_info().device.input.channel_layout();
+  if ((device_info().device.input.effects() &
        media::AudioParameters::KEYBOARD_MIC) &&
       audio_processing_properties_.goog_experimental_noise_suppression) {
     if (channel_layout == media::CHANNEL_LAYOUT_STEREO) {
@@ -150,14 +151,15 @@
   }
 
   DVLOG(1) << "Audio input hardware sample rate: "
-           << device_info().device.input.sample_rate;
+           << device_info().device.input.sample_rate();
   media::AudioSampleRate asr;
-  if (media::ToAudioSampleRate(device_info().device.input.sample_rate, &asr)) {
+  if (media::ToAudioSampleRate(device_info().device.input.sample_rate(),
+                               &asr)) {
     UMA_HISTOGRAM_ENUMERATION(
         "WebRTC.AudioInputSampleRate", asr, media::kAudioSampleRateMax + 1);
   } else {
     UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected",
-                         device_info().device.input.sample_rate);
+                         device_info().device.input.sample_rate());
   }
 
   // Determine the audio format required of the AudioCapturerSource. Then, pass
@@ -165,9 +167,9 @@
   // ProcessedLocalAudioSource to the processor's output format.
   media::AudioParameters params(
       media::AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
-      device_info().device.input.sample_rate, 16,
-      GetBufferSize(device_info().device.input.sample_rate));
-  params.set_effects(device_info().device.input.effects);
+      device_info().device.input.sample_rate(), 16,
+      GetBufferSize(device_info().device.input.sample_rate()));
+  params.set_effects(device_info().device.input.effects());
   DCHECK(params.IsValid());
   audio_processor_->OnCaptureFormatChanged(params);
   MediaStreamAudioSource::SetFormat(audio_processor_->OutputFormat());
@@ -356,7 +358,7 @@
   // If audio processing is off and the native hardware buffer size was
   // provided, use it. It can be harmful, in terms of CPU/power consumption, to
   // use smaller buffer sizes than the native size (http://crbug.com/362261).
-  if (int hardware_buffer_size = device_info().device.input.frames_per_buffer)
+  if (int hardware_buffer_size = device_info().device.input.frames_per_buffer())
     return hardware_buffer_size;
 
   // If the buffer size is missing from the StreamDeviceInfo, provide 10ms as a
diff --git a/content/renderer/media/webrtc_audio_device_impl.cc b/content/renderer/media/webrtc_audio_device_impl.cc
index a7111d0e..147c1f5 100644
--- a/content/renderer/media/webrtc_audio_device_impl.cc
+++ b/content/renderer/media/webrtc_audio_device_impl.cc
@@ -487,15 +487,15 @@
   // Don't set output parameters unless all of them are valid.
   const StreamDeviceInfo& device_info = capturers_.back()->device_info();
   if (device_info.session_id <= 0 ||
-      !device_info.device.matched_output.sample_rate ||
-      !device_info.device.matched_output.frames_per_buffer) {
+      !device_info.device.matched_output.sample_rate() ||
+      !device_info.device.matched_output.frames_per_buffer()) {
     return false;
   }
 
   *session_id = device_info.session_id;
-  *output_sample_rate = device_info.device.matched_output.sample_rate;
+  *output_sample_rate = device_info.device.matched_output.sample_rate();
   *output_frames_per_buffer =
-      device_info.device.matched_output.frames_per_buffer;
+      device_info.device.matched_output.frames_per_buffer();
 
   return true;
 }
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index ea83dcf..f4692bf 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -110,9 +110,9 @@
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/platform/WebURLError.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebDocumentLoader.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebPluginContainer.h"
 #include "third_party/WebKit/public/web/WebPluginScriptForbiddenScope.h"
@@ -945,22 +945,19 @@
 bool PepperPluginInstanceImpl::SendCompositionEventToPlugin(
     PP_InputEvent_Type type,
     const base::string16& text) {
-  std::vector<blink::WebCompositionUnderline> empty;
-  return SendCompositionEventWithUnderlineInformationToPlugin(
-      type,
-      text,
-      empty,
-      static_cast<int>(text.size()),
+  std::vector<blink::WebImeTextSpan> empty;
+  return SendCompositionEventWithImeTextSpanInformationToPlugin(
+      type, text, empty, static_cast<int>(text.size()),
       static_cast<int>(text.size()));
 }
 
-bool
-PepperPluginInstanceImpl::SendCompositionEventWithUnderlineInformationToPlugin(
-    PP_InputEvent_Type type,
-    const base::string16& text,
-    const std::vector<blink::WebCompositionUnderline>& underlines,
-    int selection_start,
-    int selection_end) {
+bool PepperPluginInstanceImpl::
+    SendCompositionEventWithImeTextSpanInformationToPlugin(
+        PP_InputEvent_Type type,
+        const base::string16& text,
+        const std::vector<blink::WebImeTextSpan>& ime_text_spans,
+        int selection_start,
+        int selection_end) {
   // Keep a reference on the stack. See NOTE above.
   scoped_refptr<PepperPluginInstanceImpl> ref(this);
 
@@ -981,9 +978,9 @@
   std::vector<size_t> utf16_offsets;
   utf16_offsets.push_back(selection_start);
   utf16_offsets.push_back(selection_end);
-  for (size_t i = 0; i < underlines.size(); ++i) {
-    utf16_offsets.push_back(underlines[i].start_offset);
-    utf16_offsets.push_back(underlines[i].end_offset);
+  for (size_t i = 0; i < ime_text_spans.size(); ++i) {
+    utf16_offsets.push_back(ime_text_spans[i].start_offset);
+    utf16_offsets.push_back(ime_text_spans[i].end_offset);
   }
   std::vector<size_t> utf8_offsets(utf16_offsets);
   event.character_text = base::UTF16ToUTF8AndAdjustOffsets(text, &utf8_offsets);
@@ -1006,8 +1003,8 @@
                                            offset_set.end());
 
   // Set the composition target.
-  for (size_t i = 0; i < underlines.size(); ++i) {
-    if (underlines[i].thick) {
+  for (size_t i = 0; i < ime_text_spans.size(); ++i) {
+    if (ime_text_spans[i].thick) {
       std::vector<uint32_t>::iterator it =
           std::find(event.composition_segment_offsets.begin(),
                     event.composition_segment_offsets.end(),
@@ -1049,15 +1046,12 @@
 
 bool PepperPluginInstanceImpl::HandleCompositionUpdate(
     const base::string16& text,
-    const std::vector<blink::WebCompositionUnderline>& underlines,
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans,
     int selection_start,
     int selection_end) {
-  return SendCompositionEventWithUnderlineInformationToPlugin(
-      PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE,
-      text,
-      underlines,
-      selection_start,
-      selection_end);
+  return SendCompositionEventWithImeTextSpanInformationToPlugin(
+      PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE, text, ime_text_spans,
+      selection_start, selection_end);
 }
 
 bool PepperPluginInstanceImpl::HandleCompositionEnd(
@@ -2335,7 +2329,7 @@
         return false;
       render_frame_->SimulateImeCommitText(
           base::UTF8ToUTF16(input_event.character_text),
-          std::vector<blink::WebCompositionUnderline>(), gfx::Range());
+          std::vector<blink::WebImeTextSpan>(), gfx::Range());
       break;
     default:
       return false;
@@ -2358,18 +2352,18 @@
   base::string16 utf16_text =
       base::UTF8ToUTF16AndAdjustOffsets(input_event.character_text, &offsets);
 
-  std::vector<blink::WebCompositionUnderline> underlines;
+  std::vector<blink::WebImeTextSpan> ime_text_spans;
   for (size_t i = 2; i + 1 < offsets.size(); ++i) {
-    blink::WebCompositionUnderline underline;
-    underline.start_offset = offsets[i];
-    underline.end_offset = offsets[i + 1];
+    blink::WebImeTextSpan ime_text_span;
+    ime_text_span.start_offset = offsets[i];
+    ime_text_span.end_offset = offsets[i + 1];
     if (input_event.composition_target_segment == static_cast<int32_t>(i - 2))
-      underline.thick = true;
-    underlines.push_back(underline);
+      ime_text_span.thick = true;
+    ime_text_spans.push_back(ime_text_span);
   }
 
-  render_frame_->SimulateImeSetComposition(
-      utf16_text, underlines, offsets[0], offsets[1]);
+  render_frame_->SimulateImeSetComposition(utf16_text, ime_text_spans,
+                                           offsets[0], offsets[1]);
 }
 
 ContentDecryptorDelegate*
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index f0cc2331..11e1961d 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -78,7 +78,7 @@
 class WebMouseEvent;
 class WebPluginContainer;
 class WebURLResponse;
-struct WebCompositionUnderline;
+struct WebImeTextSpan;
 struct WebCursorInfo;
 struct WebURLError;
 struct WebPrintParams;
@@ -240,7 +240,7 @@
   bool HandleCompositionStart(const base::string16& text);
   bool HandleCompositionUpdate(
       const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans,
       int selection_start,
       int selection_end);
   bool HandleCompositionEnd(const base::string16& text);
@@ -688,10 +688,10 @@
   // Internal helper functions for HandleCompositionXXX().
   bool SendCompositionEventToPlugin(PP_InputEvent_Type type,
                                     const base::string16& text);
-  bool SendCompositionEventWithUnderlineInformationToPlugin(
+  bool SendCompositionEventWithImeTextSpanInformationToPlugin(
       PP_InputEvent_Type type,
       const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans,
       int selection_start,
       int selection_end);
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index f0b6745..971139b7 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1478,19 +1478,19 @@
 
 void RenderFrameImpl::SimulateImeSetComposition(
     const base::string16& text,
-    const std::vector<blink::WebCompositionUnderline>& underlines,
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans,
     int selection_start,
     int selection_end) {
-  render_view_->OnImeSetComposition(
-      text, underlines, gfx::Range::InvalidRange(),
-      selection_start, selection_end);
+  render_view_->OnImeSetComposition(text, ime_text_spans,
+                                    gfx::Range::InvalidRange(), selection_start,
+                                    selection_end);
 }
 
 void RenderFrameImpl::SimulateImeCommitText(
     const base::string16& text,
-    const std::vector<blink::WebCompositionUnderline>& underlines,
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range) {
-  render_view_->OnImeCommitText(text, underlines, replacement_range, 0);
+  render_view_->OnImeCommitText(text, ime_text_spans, replacement_range, 0);
 }
 
 void RenderFrameImpl::SimulateImeFinishComposingText(bool keep_selection) {
@@ -1499,7 +1499,7 @@
 
 void RenderFrameImpl::OnImeSetComposition(
     const base::string16& text,
-    const std::vector<blink::WebCompositionUnderline>& underlines,
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans,
     int selection_start,
     int selection_end) {
   // When a PPAPI plugin has focus, we bypass WebKit.
@@ -1523,7 +1523,8 @@
     // Nonempty: composition is ongoing.
     if (!pepper_composition_text_.empty()) {
       focused_pepper_plugin_->HandleCompositionUpdate(
-          pepper_composition_text_, underlines, selection_start, selection_end);
+          pepper_composition_text_, ime_text_spans, selection_start,
+          selection_end);
     }
   }
 }
@@ -2215,10 +2216,11 @@
 }
 
 void RenderFrameImpl::OnSetCompositionFromExistingText(
-    int start, int end,
-    const std::vector<blink::WebCompositionUnderline>& underlines) {
+    int start,
+    int end,
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans) {
   ImeEventGuard guard(GetRenderWidget());
-  frame_->SetCompositionFromExistingText(start, end, underlines);
+  frame_->SetCompositionFromExistingText(start, end, ime_text_spans);
 }
 
 void RenderFrameImpl::OnExecuteNoValueEditCommand(const std::string& name) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index d83c434..8b5770dd 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -103,7 +103,7 @@
 class WebPresentationClient;
 class WebPushClient;
 class WebSecurityOrigin;
-struct WebCompositionUnderline;
+struct WebImeTextSpan;
 struct WebContextMenuData;
 struct WebCursorInfo;
 struct WebFindOptions;
@@ -372,12 +372,12 @@
   // Simulates IME events for testing purpose.
   void SimulateImeSetComposition(
       const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans,
       int selection_start,
       int selection_end);
   void SimulateImeCommitText(
       const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans,
       const gfx::Range& replacement_range);
   void SimulateImeFinishComposingText(bool keep_selection);
 
@@ -385,7 +385,7 @@
   // RenderFrame.
   void OnImeSetComposition(
       const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans,
       int selection_start,
       int selection_end);
   void OnImeCommitText(const base::string16& text,
@@ -940,8 +940,9 @@
   void OnVisualStateRequest(uint64_t key);
   void OnSetEditableSelectionOffsets(int start, int end);
   void OnSetCompositionFromExistingText(
-      int start, int end,
-      const std::vector<blink::WebCompositionUnderline>& underlines);
+      int start,
+      int end,
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans);
   void OnExecuteNoValueEditCommand(const std::string& name);
   void OnExtendSelectionAndDelete(int before, int after);
   void OnDeleteSurroundingText(int before, int after);
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 6ce744eb..ed991b7 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -669,13 +669,13 @@
       IsRunningInMash() ? ui::mojom::kServiceName : mojom::kBrowserServiceName,
       GetIOTaskRunner());
 
-  cc::mojom::SharedBitmapAllocationNotifierPtr
+  viz::mojom::SharedBitmapAllocationNotifierPtr
       shared_bitmap_allocation_notifier_ptr;
   GetConnector()->BindInterface(
       mojom::kBrowserServiceName,
       mojo::MakeRequest(&shared_bitmap_allocation_notifier_ptr));
   shared_bitmap_manager_ = base::MakeUnique<viz::ClientSharedBitmapManager>(
-      cc::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr::Create(
+      viz::mojom::ThreadSafeSharedBitmapAllocationNotifierPtr::Create(
           shared_bitmap_allocation_notifier_ptr.PassInterface(),
           GetChannel()->ipc_task_runner_refptr()));
 
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index dec950f..8a8b844 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -1256,15 +1256,13 @@
       case IME_SETCOMPOSITION:
         view()->OnImeSetComposition(
             base::WideToUTF16(ime_message->ime_string),
-            std::vector<blink::WebCompositionUnderline>(),
-            gfx::Range::InvalidRange(),
-            ime_message->selection_start,
-            ime_message->selection_end);
+            std::vector<blink::WebImeTextSpan>(), gfx::Range::InvalidRange(),
+            ime_message->selection_start, ime_message->selection_end);
         break;
 
       case IME_COMMITTEXT:
         view()->OnImeCommitText(base::WideToUTF16(ime_message->ime_string),
-                                std::vector<blink::WebCompositionUnderline>(),
+                                std::vector<blink::WebImeTextSpan>(),
                                 gfx::Range::InvalidRange(), 0);
         break;
 
@@ -1273,11 +1271,9 @@
         break;
 
       case IME_CANCELCOMPOSITION:
-        view()->OnImeSetComposition(
-            base::string16(),
-            std::vector<blink::WebCompositionUnderline>(),
-            gfx::Range::InvalidRange(),
-            0, 0);
+        view()->OnImeSetComposition(base::string16(),
+                                    std::vector<blink::WebImeTextSpan>(),
+                                    gfx::Range::InvalidRange(), 0, 0);
         break;
     }
 
@@ -1511,48 +1507,44 @@
   ExecuteJavaScriptForTests("document.getElementById('test').focus();");
 
   const base::string16 empty_string;
-  const std::vector<blink::WebCompositionUnderline> empty_underline;
+  const std::vector<blink::WebImeTextSpan> empty_ime_text_span;
   std::vector<gfx::Rect> bounds;
   view()->OnSetFocus(true);
 
   // ASCII composition
   const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo");
-  view()->OnImeSetComposition(ascii_composition, empty_underline,
+  view()->OnImeSetComposition(ascii_composition, empty_ime_text_span,
                               gfx::Range::InvalidRange(), 0, 0);
   view()->GetCompositionCharacterBounds(&bounds);
   ASSERT_EQ(ascii_composition.size(), bounds.size());
 
   for (size_t i = 0; i < bounds.size(); ++i)
     EXPECT_LT(0, bounds[i].width());
-  view()->OnImeCommitText(empty_string,
-                          std::vector<blink::WebCompositionUnderline>(),
+  view()->OnImeCommitText(empty_string, std::vector<blink::WebImeTextSpan>(),
                           gfx::Range::InvalidRange(), 0);
 
   // Non surrogate pair unicode character.
   const base::string16 unicode_composition = base::UTF8ToUTF16(
       "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
-  view()->OnImeSetComposition(unicode_composition, empty_underline,
+  view()->OnImeSetComposition(unicode_composition, empty_ime_text_span,
                               gfx::Range::InvalidRange(), 0, 0);
   view()->GetCompositionCharacterBounds(&bounds);
   ASSERT_EQ(unicode_composition.size(), bounds.size());
   for (size_t i = 0; i < bounds.size(); ++i)
     EXPECT_LT(0, bounds[i].width());
-  view()->OnImeCommitText(empty_string, empty_underline,
+  view()->OnImeCommitText(empty_string, empty_ime_text_span,
                           gfx::Range::InvalidRange(), 0);
 
   // Surrogate pair character.
   const base::string16 surrogate_pair_char =
       base::UTF8ToUTF16("\xF0\xA0\xAE\x9F");
-  view()->OnImeSetComposition(surrogate_pair_char,
-                              empty_underline,
-                              gfx::Range::InvalidRange(),
-                              0,
-                              0);
+  view()->OnImeSetComposition(surrogate_pair_char, empty_ime_text_span,
+                              gfx::Range::InvalidRange(), 0, 0);
   view()->GetCompositionCharacterBounds(&bounds);
   ASSERT_EQ(surrogate_pair_char.size(), bounds.size());
   EXPECT_LT(0, bounds[0].width());
   EXPECT_EQ(0, bounds[1].width());
-  view()->OnImeCommitText(empty_string, empty_underline,
+  view()->OnImeCommitText(empty_string, empty_ime_text_span,
                           gfx::Range::InvalidRange(), 0);
 
   // Mixed string.
@@ -1563,10 +1555,8 @@
   const bool is_surrogate_pair_empty_rect[8] = {
     false, true, false, false, true, false, false, true };
   view()->OnImeSetComposition(surrogate_pair_mixed_composition,
-                              empty_underline,
-                              gfx::Range::InvalidRange(),
-                              0,
-                              0);
+                              empty_ime_text_span, gfx::Range::InvalidRange(),
+                              0, 0);
   view()->GetCompositionCharacterBounds(&bounds);
   ASSERT_EQ(utf16_length, bounds.size());
   for (size_t i = 0; i < utf16_length; ++i) {
@@ -1576,7 +1566,7 @@
       EXPECT_LT(0, bounds[i].width());
     }
   }
-  view()->OnImeCommitText(empty_string, empty_underline,
+  view()->OnImeCommitText(empty_string, empty_ime_text_span,
                           gfx::Range::InvalidRange(), 0);
 }
 #endif
@@ -1592,8 +1582,8 @@
            "</html>");
   ExecuteJavaScriptForTests("document.getElementById('test1').focus();");
   frame()->SetEditableSelectionOffsets(4, 8);
-  const std::vector<blink::WebCompositionUnderline> empty_underline;
-  frame()->SetCompositionFromExistingText(7, 10, empty_underline);
+  const std::vector<blink::WebImeTextSpan> empty_ime_text_span;
+  frame()->SetCompositionFromExistingText(7, 10, empty_ime_text_span);
   blink::WebInputMethodController* controller =
       frame()->GetWebFrame()->GetInputMethodController();
   blink::WebTextInputInfo info = controller->TextInputInfo();
@@ -2457,13 +2447,13 @@
   ExecuteJavaScriptForTests("document.getElementById('test').focus();");
 
   const base::string16 empty_string;
-  const std::vector<blink::WebCompositionUnderline> empty_underline;
+  const std::vector<blink::WebImeTextSpan> empty_ime_text_span;
   std::vector<gfx::Rect> bounds_at_1x;
   view()->OnSetFocus(true);
 
   // ASCII composition
   const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo");
-  view()->OnImeSetComposition(ascii_composition, empty_underline,
+  view()->OnImeSetComposition(ascii_composition, empty_ime_text_span,
                               gfx::Range::InvalidRange(), 0, 0);
   view()->GetCompositionCharacterBounds(&bounds_at_1x);
   ASSERT_EQ(ascii_composition.size(), bounds_at_1x.size());
diff --git a/content/renderer/render_view_browsertest_mac.mm b/content/renderer/render_view_browsertest_mac.mm
index c425813..21e28f8 100644
--- a/content/renderer/render_view_browsertest_mac.mm
+++ b/content/renderer/render_view_browsertest_mac.mm
@@ -23,7 +23,7 @@
 #include <Cocoa/Cocoa.h>
 
 using blink::WebFrameContentDumper;
-using blink::WebCompositionUnderline;
+using blink::WebImeTextSpan;
 
 namespace content {
 
@@ -182,11 +182,11 @@
 
   // Simulate some IME related IPCs.
   using Text = base::string16;
-  using Underlines = std::vector<blink::WebCompositionUnderline>;
+  using ImeTextSpans = std::vector<blink::WebImeTextSpan>;
   view->OnMessageReceived(InputMsg_ImeSetComposition(
-      routing_id, Text(), Underlines(), Range(), 0, 0));
+      routing_id, Text(), ImeTextSpans(), Range(), 0, 0));
   view->OnMessageReceived(
-      InputMsg_ImeCommitText(routing_id, Text(), Underlines(), Range(), 0));
+      InputMsg_ImeCommitText(routing_id, Text(), ImeTextSpans(), Range(), 0));
   view->OnMessageReceived(InputMsg_ImeFinishComposingText(routing_id, false));
 }
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 89e9a3a..5c373d4 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -120,7 +120,7 @@
 #include "content/renderer/text_input_client_observer.h"
 #endif
 
-using blink::WebCompositionUnderline;
+using blink::WebImeTextSpan;
 using blink::WebCursorInfo;
 using blink::WebDeviceEmulationParams;
 using blink::WebDragOperation;
@@ -1660,15 +1660,16 @@
 
 void RenderWidget::OnImeSetComposition(
     const base::string16& text,
-    const std::vector<WebCompositionUnderline>& underlines,
+    const std::vector<WebImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
-    int selection_start, int selection_end) {
+    int selection_start,
+    int selection_end) {
   if (!ShouldHandleImeEvents())
     return;
 
 #if BUILDFLAG(ENABLE_PLUGINS)
   if (auto* plugin = GetFocusedPepperPluginInsideWidget()) {
-    plugin->render_frame()->OnImeSetComposition(text, underlines,
+    plugin->render_frame()->OnImeSetComposition(text, ime_text_spans,
                                                 selection_start, selection_end);
     return;
   }
@@ -1677,8 +1678,7 @@
   blink::WebInputMethodController* controller = GetInputMethodController();
   if (!controller ||
       !controller->SetComposition(
-          WebString::FromUTF16(text),
-          WebVector<WebCompositionUnderline>(underlines),
+          WebString::FromUTF16(text), WebVector<WebImeTextSpan>(ime_text_spans),
           replacement_range.IsValid()
               ? WebRange(replacement_range.start(), replacement_range.length())
               : WebRange(),
@@ -1693,7 +1693,7 @@
 
 void RenderWidget::OnImeCommitText(
     const base::string16& text,
-    const std::vector<WebCompositionUnderline>& underlines,
+    const std::vector<WebImeTextSpan>& ime_text_spans,
     const gfx::Range& replacement_range,
     int relative_cursor_pos) {
   if (!ShouldHandleImeEvents())
@@ -1710,8 +1710,7 @@
   input_handler_->set_handling_input_event(true);
   if (auto* controller = GetInputMethodController()) {
     controller->CommitText(
-        WebString::FromUTF16(text),
-        WebVector<WebCompositionUnderline>(underlines),
+        WebString::FromUTF16(text), WebVector<WebImeTextSpan>(ime_text_spans),
         replacement_range.IsValid()
             ? WebRange(replacement_range.start(), replacement_range.length())
             : WebRange(),
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index ab1b3ea..3785f02 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -52,7 +52,7 @@
 #include "third_party/WebKit/public/platform/WebRect.h"
 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
 #include "third_party/WebKit/public/platform/WebTextInputInfo.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "third_party/WebKit/public/web/WebPopupType.h"
 #include "third_party/WebKit/public/web/WebTextDirection.h"
 #include "third_party/WebKit/public/web/WebWidget.h"
@@ -435,15 +435,14 @@
   void OnSetEditCommandsForNextKeyEvent(const EditCommands& edit_commands);
   void OnImeSetComposition(
       const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans,
       const gfx::Range& replacement_range,
       int selection_start,
       int selection_end);
-  void OnImeCommitText(
-      const base::string16& text,
-      const std::vector<blink::WebCompositionUnderline>& underlines,
-      const gfx::Range& replacement_range,
-      int relative_cursor_pos);
+  void OnImeCommitText(const base::string16& text,
+                       const std::vector<blink::WebImeTextSpan>& ime_text_spans,
+                       const gfx::Range& replacement_range,
+                       int relative_cursor_pos);
   void OnImeFinishComposingText(bool keep_selection);
 
   // Called by the browser process to update text input state.
diff --git a/content/renderer/render_widget_browsertest.cc b/content/renderer/render_widget_browsertest.cc
index 3f8fcf9..7d4cc20 100644
--- a/content/renderer/render_widget_browsertest.cc
+++ b/content/renderer/render_widget_browsertest.cc
@@ -37,10 +37,9 @@
   }
 
   void CommitText(std::string text) {
-    widget()->OnImeCommitText(
-        base::UTF8ToUTF16(text),
-        std::vector<blink::WebCompositionUnderline>(),
-        gfx::Range::InvalidRange(), 0);
+    widget()->OnImeCommitText(base::UTF8ToUTF16(text),
+                              std::vector<blink::WebImeTextSpan>(),
+                              gfx::Range::InvalidRange(), 0);
   }
 
   ui::TextInputType GetTextInputType() { return widget()->GetTextInputType(); }
@@ -128,10 +127,10 @@
   LoadHTML(
       "<div contenteditable>EDITABLE</div>"
       "<script> document.querySelector('div').focus(); </script>");
-  blink::WebVector<blink::WebCompositionUnderline> emptyUnderlines;
+  blink::WebVector<blink::WebImeTextSpan> empty_ime_text_spans;
   DCHECK(widget()->GetInputMethodController());
-  widget()->GetInputMethodController()->SetComposition("hello", emptyUnderlines,
-                                                       blink::WebRange(), 3, 3);
+  widget()->GetInputMethodController()->SetComposition(
+      "hello", empty_ime_text_spans, blink::WebRange(), 3, 3);
   gfx::Range range;
   GetCompositionRange(&range);
   EXPECT_TRUE(range.IsValid());
diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc
index a40db58..4206136 100644
--- a/content/renderer/render_widget_fullscreen_pepper.cc
+++ b/content/renderer/render_widget_fullscreen_pepper.cc
@@ -30,7 +30,7 @@
 
 using blink::WebCanvas;
 using blink::WebCoalescedInputEvent;
-using blink::WebCompositionUnderline;
+using blink::WebImeTextSpan;
 using blink::WebCursorInfo;
 using blink::WebGestureEvent;
 using blink::WebInputEvent;
diff --git a/content/renderer/renderer.sb b/content/renderer/renderer.sb
index fd54bf7..daa3666 100644
--- a/content/renderer/renderer.sb
+++ b/content/renderer/renderer.sb
@@ -33,8 +33,12 @@
 
 ; https://crbug.com/288697
 (allow file-read*
-  (path "/private/etc/localtime")
-  (subpath "/usr/share/zoneinfo"))
+  (path "/private/etc/localtime"))
+
+; https://crbug.com/754280
+(if (param-true? macos-1013)
+  (allow file-read-data (subpath "/usr/share/zoneinfo.default"))
+  (allow file-read-data (subpath "/usr/share/zoneinfo")))
 
 (allow file-read-metadata (path "/private/etc"))
 
diff --git a/content/shell/test_runner/text_input_controller.cc b/content/shell/test_runner/text_input_controller.cc
index 9e7d7ba..d9f0929 100644
--- a/content/shell/test_runner/text_input_controller.cc
+++ b/content/shell/test_runner/text_input_controller.cc
@@ -14,8 +14,8 @@
 #include "third_party/WebKit/public/platform/WebCoalescedInputEvent.h"
 #include "third_party/WebKit/public/platform/WebInputEventResult.h"
 #include "third_party/WebKit/public/platform/WebKeyboardEvent.h"
-#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "third_party/WebKit/public/web/WebFrameWidget.h"
+#include "third_party/WebKit/public/web/WebImeTextSpan.h"
 #include "third_party/WebKit/public/web/WebInputMethodController.h"
 #include "third_party/WebKit/public/web/WebKit.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
@@ -173,7 +173,7 @@
 void TextInputController::InsertText(const std::string& text) {
   if (auto* controller = GetInputMethodController()) {
     controller->CommitText(blink::WebString::FromUTF8(text),
-                           std::vector<blink::WebCompositionUnderline>(),
+                           std::vector<blink::WebImeTextSpan>(),
                            blink::WebRange(), 0);
   }
 }
@@ -202,28 +202,28 @@
   blink::WebString web_text(blink::WebString::FromUTF8(text));
 
   // Split underline into up to 3 elements (before, selection, and after).
-  std::vector<blink::WebCompositionUnderline> underlines;
-  blink::WebCompositionUnderline underline;
+  std::vector<blink::WebImeTextSpan> ime_text_spans;
+  blink::WebImeTextSpan ime_text_span;
   if (!start) {
-    underline.end_offset = length;
+    ime_text_span.end_offset = length;
   } else {
-    underline.end_offset = start;
-    underlines.push_back(underline);
-    underline.start_offset = start;
-    underline.end_offset = start + length;
+    ime_text_span.end_offset = start;
+    ime_text_spans.push_back(ime_text_span);
+    ime_text_span.start_offset = start;
+    ime_text_span.end_offset = start + length;
   }
-  underline.thick = true;
-  underlines.push_back(underline);
+  ime_text_span.thick = true;
+  ime_text_spans.push_back(ime_text_span);
   if (start + length < static_cast<int>(web_text.length())) {
-    underline.start_offset = underline.end_offset;
-    underline.end_offset = web_text.length();
-    underline.thick = false;
-    underlines.push_back(underline);
+    ime_text_span.start_offset = ime_text_span.end_offset;
+    ime_text_span.end_offset = web_text.length();
+    ime_text_span.thick = false;
+    ime_text_spans.push_back(ime_text_span);
   }
 
   if (auto* controller = GetInputMethodController()) {
-    controller->SetComposition(web_text, underlines, blink::WebRange(), start,
-                               start + length);
+    controller->SetComposition(web_text, ime_text_spans, blink::WebRange(),
+                               start, start + length);
   }
 }
 
@@ -311,12 +311,12 @@
   blink::WebString newText = blink::WebString::FromUTF8(text);
   size_t textLength = newText.length();
 
-  std::vector<blink::WebCompositionUnderline> underlines;
-  underlines.push_back(blink::WebCompositionUnderline(
-      0, textLength, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
+  std::vector<blink::WebImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(blink::WebImeTextSpan(0, textLength, SK_ColorBLACK,
+                                                 false, SK_ColorTRANSPARENT));
   if (auto* controller = GetInputMethodController()) {
     controller->SetComposition(
-        newText, blink::WebVector<blink::WebCompositionUnderline>(underlines),
+        newText, blink::WebVector<blink::WebImeTextSpan>(ime_text_spans),
         blink::WebRange(), textLength, textLength);
   }
 }
diff --git a/content/test/data/accessibility/aria/aria-col-attr-expected-mac.txt b/content/test/data/accessibility/aria/aria-col-attr-expected-mac.txt
index 2f53480..e2f6b921 100644
--- a/content/test/data/accessibility/aria/aria-col-attr-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-col-attr-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid AXARIAColumnCount='5'
+++AXTable AXARIAColumnCount='5'
 ++++AXRow
 ++++++AXCell AXTitle='cell 2' AXARIAColumnIndex='2'
 ++++++++AXStaticText AXValue='cell 2'
diff --git a/content/test/data/accessibility/aria/aria-columnheader-expected-mac.txt b/content/test/data/accessibility/aria/aria-columnheader-expected-mac.txt
index ada8210..7c778711 100644
--- a/content/test/data/accessibility/aria/aria-columnheader-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-columnheader-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Browser'
 ++++++++AXStaticText AXValue='Browser'
diff --git a/content/test/data/accessibility/aria/aria-grid-expected-mac.txt b/content/test/data/accessibility/aria/aria-grid-expected-mac.txt
index 22c6b08..68f4871 100644
--- a/content/test/data/accessibility/aria/aria-grid-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-grid-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea AXRoleDescription='HTML content'
-++AXGrid AXRoleDescription='grid'
+++AXTable AXRoleDescription='table'
 ++++AXRow AXRoleDescription='row'
 ++++++AXCell AXRoleDescription='cell' AXTitle='Browser'
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Browser'
diff --git a/content/test/data/accessibility/aria/aria-gridcell-expected-mac.txt b/content/test/data/accessibility/aria/aria-gridcell-expected-mac.txt
index 22c6b08..68f4871 100644
--- a/content/test/data/accessibility/aria/aria-gridcell-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-gridcell-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea AXRoleDescription='HTML content'
-++AXGrid AXRoleDescription='grid'
+++AXTable AXRoleDescription='table'
 ++++AXRow AXRoleDescription='row'
 ++++++AXCell AXRoleDescription='cell' AXTitle='Browser'
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Browser'
diff --git a/content/test/data/accessibility/aria/aria-row-attr-expected-mac.txt b/content/test/data/accessibility/aria/aria-row-attr-expected-mac.txt
index 35def74..a4fc45e 100644
--- a/content/test/data/accessibility/aria/aria-row-attr-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-row-attr-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid AXARIARowCount='5'
+++AXTable AXARIARowCount='5'
 ++++AXRow
 ++++++AXCell AXTitle='cell 2' AXARIARowIndex='3'
 ++++++++AXStaticText AXValue='cell 2'
diff --git a/content/test/data/accessibility/aria/aria-row-expected-mac.txt b/content/test/data/accessibility/aria/aria-row-expected-mac.txt
index ada8210..7c778711 100644
--- a/content/test/data/accessibility/aria/aria-row-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-row-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Browser'
 ++++++++AXStaticText AXValue='Browser'
diff --git a/content/test/data/accessibility/aria/aria-rowgroup-expected-mac.txt b/content/test/data/accessibility/aria/aria-rowgroup-expected-mac.txt
index f22f6ef8..b8033e6 100644
--- a/content/test/data/accessibility/aria/aria-rowgroup-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-rowgroup-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid
+++AXTable
 ++++AXRow
 ++++AXRow
 ++++AXGroup
diff --git a/content/test/data/accessibility/aria/aria-rowheader-expected-mac.txt b/content/test/data/accessibility/aria/aria-rowheader-expected-mac.txt
index c8d13ff..5b6be0f 100644
--- a/content/test/data/accessibility/aria/aria-rowheader-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-rowheader-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Browser'
 ++++++++AXStaticText AXValue='Browser'
diff --git a/content/test/data/accessibility/aria/aria-sort-aria-grid-expected-mac.txt b/content/test/data/accessibility/aria/aria-sort-aria-grid-expected-mac.txt
index 747f5a01..b4b49bd 100644
--- a/content/test/data/accessibility/aria/aria-sort-aria-grid-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-sort-aria-grid-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -11,7 +11,7 @@
 ++++++++AXStaticText AXValue='B'
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -23,7 +23,7 @@
 ++++++++AXStaticText AXValue='B'
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet' AXSortDirection='AXAscendingSortDirection'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -35,7 +35,7 @@
 ++++++++AXStaticText AXValue='B'
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet' AXSortDirection='AXDescendingSortDirection'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -47,7 +47,7 @@
 ++++++++AXStaticText AXValue='A'
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet' AXSortDirection='AXUnknownSortDirection'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -59,7 +59,7 @@
 ++++++++AXStaticText AXValue='A'
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -71,7 +71,7 @@
 ++++AXColumn
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -83,7 +83,7 @@
 ++++AXColumn
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet' AXSortDirection='AXAscendingSortDirection'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -95,7 +95,7 @@
 ++++AXColumn
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet' AXSortDirection='AXDescendingSortDirection'
 ++++++++AXStaticText AXValue='Alphabet'
@@ -107,7 +107,7 @@
 ++++AXColumn
 ++++AXColumn
 ++++AXGroup
-++AXGrid
+++AXTable
 ++++AXRow
 ++++++AXCell AXTitle='Alphabet' AXSortDirection='AXUnknownSortDirection'
 ++++++++AXStaticText AXValue='Alphabet'
diff --git a/content/test/data/accessibility/aria/table-column-hidden-expected-mac.txt b/content/test/data/accessibility/aria/table-column-hidden-expected-mac.txt
index 073f663..a178596 100644
--- a/content/test/data/accessibility/aria/table-column-hidden-expected-mac.txt
+++ b/content/test/data/accessibility/aria/table-column-hidden-expected-mac.txt
@@ -1,5 +1,5 @@
 AXWebArea
-++AXGrid AXARIAColumnCount='4'
+++AXTable AXARIAColumnCount='4'
 ++++AXRow
 ++++++AXCell AXTitle='Month' AXARIAColumnIndex='1' AXARIARowIndex='1'
 ++++++++AXStaticText AXValue='Month'
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 0019ccc5..b4702b5c 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -264,19 +264,6 @@
       'swarming': True,
       'os_type': 'win',
     },
-    'Win10 Release (Intel HD 530)': {
-      'swarming_dimensions': [
-        {
-          'gpu': '8086:1912',
-          'os': 'Windows-10',
-        },
-      ],
-      'build_config': 'Release',
-      # This bot is a one-off and doesn't have similar slaves in the
-      # swarming pool.
-      'swarming': False,
-      'os_type': 'win',
-    },
     'Win10 Release (Intel HD 630)': {
       'swarming_dimensions': [
         {
@@ -290,19 +277,6 @@
       'swarming': False,
       'os_type': 'win',
     },
-    'Win10 Debug (Intel HD 530)': {
-      'swarming_dimensions': [
-        {
-          'gpu': '8086:1912',
-          'os': 'Windows-10',
-        },
-      ],
-      'build_config': 'Debug',
-      # This bot is a one-off and doesn't have similar slaves in the
-      # swarming pool.
-      'swarming': False,
-      'os_type': 'win',
-    },
     'Win10 Release (NVIDIA Quadro P400)': {
       'swarming_dimensions': [
         {
@@ -538,19 +512,6 @@
       'swarming': True,
       'os_type': 'linux',
     },
-    'Linux Release (Intel HD 530)': {
-      'swarming_dimensions': [
-        {
-          'gpu': '8086:1912',
-          'os': 'Ubuntu'
-        },
-      ],
-      'build_config': 'Release',
-      # This bot is a one-off and doesn't have similar slaves in the
-      # swarming pool.
-      'swarming': False,
-      'os_type': 'linux',
-    },
     'Linux Release (Intel HD 630)': {
       'swarming_dimensions': [
         {
@@ -564,19 +525,6 @@
       'swarming': False,
       'os_type': 'linux',
     },
-    'Linux Debug (Intel HD 530)': {
-      'swarming_dimensions': [
-        {
-          'gpu': '8086:1912',
-          'os': 'Ubuntu'
-        },
-      ],
-      'build_config': 'Debug',
-      # This bot is a one-off and doesn't have similar slaves in the
-      # swarming pool.
-      'swarming': False,
-      'os_type': 'linux',
-    },
     'Linux Release (AMD R7 240)': {
       'swarming_dimensions': [
         {
@@ -1351,6 +1299,25 @@
     ],
     'desktop_args': ['--use-gpu-in-tests']
   },
+  'gl_tests_passthrough': {
+    'tester_configs': [
+      {
+        'os_types': ['win'],
+      }
+    ],
+    'disabled_tester_configs': [
+      {
+        'names': [
+          'Linux ChromiumOS Ozone (Intel)',
+        ],
+      },
+    ],
+    'test': 'gl_tests',
+    'desktop_args': [
+      '--use-gpu-in-tests',
+      '--use-passthrough-cmd-decoder',
+     ]
+  },
   'gl_unittests': {
     'disabled_tester_configs': [
       {
@@ -1543,8 +1510,6 @@
     'disabled_tester_configs': [
       {
         'names': [
-          'Win10 Debug (Intel HD 530)',
-          'Win10 Release (Intel HD 530)',
           'Win10 Release (Intel HD 630)',
         ],
       },
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index 09a4d4b..d6c1bb15 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -106,8 +106,8 @@
 void TestRenderFrame::SetCompositionFromExistingText(
     int start,
     int end,
-    const std::vector<blink::WebCompositionUnderline>& underlines) {
-  OnSetCompositionFromExistingText(start, end, underlines);
+    const std::vector<blink::WebImeTextSpan>& ime_text_spans) {
+  OnSetCompositionFromExistingText(start, end, ime_text_spans);
 }
 
 blink::WebNavigationPolicy TestRenderFrame::DecidePolicyForNavigation(
diff --git a/content/test/test_render_frame.h b/content/test/test_render_frame.h
index 567b355..cc888aa 100644
--- a/content/test/test_render_frame.h
+++ b/content/test/test_render_frame.h
@@ -49,7 +49,7 @@
   void SetCompositionFromExistingText(
       int start,
       int end,
-      const std::vector<blink::WebCompositionUnderline>& underlines);
+      const std::vector<blink::WebImeTextSpan>& ime_text_spans);
 
   blink::WebNavigationPolicy DecidePolicyForNavigation(
       const blink::WebFrameClient::NavigationPolicyInfo& info) override;
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index afe554ec6..9f0bc62 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -28,17 +28,14 @@
     const std::string& name,
     const std::vector<device::BluetoothUUID>& known_service_uuids,
     SimulatePreconnectedPeripheralCallback callback) {
-  auto device_iter = devices_.find(address);
-  if (device_iter == devices_.end()) {
-    auto fake_peripheral = base::MakeUnique<FakePeripheral>(this, address);
-
-    auto insert_iter = devices_.emplace(address, std::move(fake_peripheral));
-    DCHECK(insert_iter.second);
-    device_iter = insert_iter.first;
+  FakePeripheral* fake_peripheral = GetFakePeripheral(address);
+  if (fake_peripheral == nullptr) {
+    auto fake_peripheral_ptr = base::MakeUnique<FakePeripheral>(this, address);
+    fake_peripheral = fake_peripheral_ptr.get();
+    auto pair = devices_.emplace(address, std::move(fake_peripheral_ptr));
+    DCHECK(pair.second);
   }
 
-  FakePeripheral* fake_peripheral =
-      static_cast<FakePeripheral*>(device_iter->second.get());
   fake_peripheral->SetName(name);
   fake_peripheral->SetSystemConnected(true);
   fake_peripheral->SetServiceUUIDs(device::BluetoothDevice::UUIDSet(
@@ -51,14 +48,12 @@
     const std::string& address,
     uint16_t code,
     SetNextGATTConnectionResponseCallback callback) {
-  auto device_iter = devices_.find(address);
-  if (device_iter == devices_.end()) {
+  FakePeripheral* fake_peripheral = GetFakePeripheral(address);
+  if (fake_peripheral == nullptr) {
     std::move(callback).Run(false);
     return;
   }
 
-  FakePeripheral* fake_peripheral =
-      static_cast<FakePeripheral*>(device_iter->second.get());
   fake_peripheral->SetNextGATTConnectionResponse(code);
   std::move(callback).Run(true);
 }
@@ -67,13 +62,12 @@
     const std::string& address,
     uint16_t code,
     SetNextGATTDiscoveryResponseCallback callback) {
-  auto device_iter = devices_.find(address);
-  if (device_iter == devices_.end()) {
+  FakePeripheral* fake_peripheral = GetFakePeripheral(address);
+  if (fake_peripheral == nullptr) {
     std::move(callback).Run(false);
+    return;
   }
 
-  FakePeripheral* fake_peripheral =
-      static_cast<FakePeripheral*>(device_iter->second.get());
   fake_peripheral->SetNextGATTDiscoveryResponse(code);
   std::move(callback).Run(true);
 }
@@ -81,19 +75,24 @@
 void FakeCentral::SimulateGATTServicesChanged(
     const std::string& address,
     SimulateGATTServicesChangedCallback callback) {
+  FakePeripheral* fake_peripheral = GetFakePeripheral(address);
+  if (fake_peripheral == nullptr) {
+    std::move(callback).Run(false);
+    return;
+  }
+
   std::move(callback).Run(true);
 }
 
 void FakeCentral::AddFakeService(const std::string& peripheral_address,
                                  const device::BluetoothUUID& service_uuid,
                                  AddFakeServiceCallback callback) {
-  auto device_iter = devices_.find(peripheral_address);
-  if (device_iter == devices_.end()) {
+  FakePeripheral* fake_peripheral = GetFakePeripheral(peripheral_address);
+  if (fake_peripheral == nullptr) {
     std::move(callback).Run(base::nullopt);
+    return;
   }
 
-  FakePeripheral* fake_peripheral =
-      static_cast<FakePeripheral*>(device_iter->second.get());
   std::move(callback).Run(fake_peripheral->AddFakeService(service_uuid));
 }
 
@@ -103,16 +102,11 @@
     const std::string& service_id,
     const std::string& peripheral_address,
     AddFakeCharacteristicCallback callback) {
-  auto device_iter = devices_.find(peripheral_address);
-  if (device_iter == devices_.end()) {
-    std::move(callback).Run(base::nullopt);
-  }
-
   FakeRemoteGattService* fake_remote_gatt_service =
-      static_cast<FakeRemoteGattService*>(
-          device_iter->second.get()->GetGattService(service_id));
+      GetFakeRemoteGattService(peripheral_address, service_id);
   if (fake_remote_gatt_service == nullptr) {
     std::move(callback).Run(base::nullopt);
+    return;
   }
 
   std::move(callback).Run(fake_remote_gatt_service->AddFakeCharacteristic(
@@ -124,15 +118,8 @@
     const std::string& service_id,
     const std::string& peripheral_address,
     RemoveFakeCharacteristicCallback callback) {
-  auto device_iter = devices_.find(peripheral_address);
-  if (device_iter == devices_.end()) {
-    std::move(callback).Run(false);
-    return;
-  }
-
   FakeRemoteGattService* fake_remote_gatt_service =
-      static_cast<FakeRemoteGattService*>(
-          device_iter->second.get()->GetGattService(service_id));
+      GetFakeRemoteGattService(peripheral_address, service_id);
   if (fake_remote_gatt_service == nullptr) {
     std::move(callback).Run(false);
     return;
@@ -374,18 +361,34 @@
 
 FakeCentral::~FakeCentral() {}
 
-FakeRemoteGattCharacteristic* FakeCentral::GetFakeRemoteGattCharacteristic(
-    const std::string& peripheral_address,
-    const std::string& service_id,
-    const std::string& characteristic_id) const {
+FakePeripheral* FakeCentral::GetFakePeripheral(
+    const std::string& peripheral_address) const {
   auto device_iter = devices_.find(peripheral_address);
   if (device_iter == devices_.end()) {
     return nullptr;
   }
 
+  return static_cast<FakePeripheral*>(device_iter->second.get());
+}
+
+FakeRemoteGattService* FakeCentral::GetFakeRemoteGattService(
+    const std::string& peripheral_address,
+    const std::string& service_id) const {
+  FakePeripheral* fake_peripheral = GetFakePeripheral(peripheral_address);
+  if (fake_peripheral == nullptr) {
+    return nullptr;
+  }
+
+  return static_cast<FakeRemoteGattService*>(
+      fake_peripheral->GetGattService(service_id));
+}
+
+FakeRemoteGattCharacteristic* FakeCentral::GetFakeRemoteGattCharacteristic(
+    const std::string& peripheral_address,
+    const std::string& service_id,
+    const std::string& characteristic_id) const {
   FakeRemoteGattService* fake_remote_gatt_service =
-      static_cast<FakeRemoteGattService*>(
-          device_iter->second.get()->GetGattService(service_id));
+      GetFakeRemoteGattService(peripheral_address, service_id);
   if (fake_remote_gatt_service == nullptr) {
     return nullptr;
   }
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 775e114..5aa27f22 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -15,8 +15,10 @@
 
 namespace bluetooth {
 
+class FakePeripheral;
 class FakeRemoteGattCharacteristic;
 class FakeRemoteGattDescriptor;
+class FakeRemoteGattService;
 
 // Implementation of FakeCentral in
 // src/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom.
@@ -150,6 +152,11 @@
  private:
   ~FakeCentral() override;
 
+  FakePeripheral* GetFakePeripheral(
+      const std::string& peripheral_address) const;
+  FakeRemoteGattService* GetFakeRemoteGattService(
+      const std::string& peripheral_address,
+      const std::string& service_id) const;
   FakeRemoteGattCharacteristic* GetFakeRemoteGattCharacteristic(
       const std::string& peripheral_address,
       const std::string& service_id,
diff --git a/docs/updating_clang.md b/docs/updating_clang.md
index 28b1ac13..5ef098d 100644
--- a/docs/updating_clang.md
+++ b/docs/updating_clang.md
@@ -27,7 +27,8 @@
 1.  Run an exhaustive set of try jobs to test the new compiler:
 ```
     git cl try &&
-    git cl try -m tryserver.chromium.mac -b mac_chromium_asan_rel_ng &&
+    git cl try -m tryserver.chromium.mac -b mac_chromium_asan_rel_ng \
+      -b ios-device && \
     git cl try -m tryserver.chromium.linux -b linux_chromium_chromeos_dbg_ng \
       -b linux_chromium_chromeos_asan_rel_ng -b linux_chromium_msan_rel_ng \
       -b fuchsia -b linux_chromium_cfi_rel_ng &&
diff --git a/extensions/browser/api/app_current_window_internal/app_current_window_internal_api.cc b/extensions/browser/api/app_current_window_internal/app_current_window_internal_api.cc
index 779e9a2c..6130865 100644
--- a/extensions/browser/api/app_current_window_internal/app_current_window_internal_api.cc
+++ b/extensions/browser/api/app_current_window_internal/app_current_window_internal_api.cc
@@ -330,27 +330,21 @@
   std::unique_ptr<SetShape::Params> params(SetShape::Params::Create(*args_));
   const Region& shape = params->region;
 
-  // Build a region from the supplied list of rects.
+  // Build the list of hit-test rects from the supplied list of rects.
   // If |rects| is missing, then the input region is removed. This clears the
   // input region so that the entire window accepts input events.
   // To specify an empty input region (so the window ignores all input),
   // |rects| should be an empty list.
-  std::unique_ptr<SkRegion> region(new SkRegion);
+  std::unique_ptr<AppWindow::ShapeRects> shape_rects;
   if (shape.rects) {
+    shape_rects = base::MakeUnique<AppWindow::ShapeRects>();
+    shape_rects->reserve(shape.rects->size());
     for (const RegionRect& input_rect : *shape.rects) {
-      int32_t x = input_rect.left;
-      int32_t y = input_rect.top;
-      int32_t width = input_rect.width;
-      int32_t height = input_rect.height;
-
-      SkIRect rect = SkIRect::MakeXYWH(x, y, width, height);
-      region->op(rect, SkRegion::kUnion_Op);
+      shape_rects->emplace_back(input_rect.left, input_rect.top,
+                                input_rect.width, input_rect.height);
     }
-  } else {
-    region.reset(NULL);
   }
-
-  window()->UpdateShape(std::move(region));
+  window()->UpdateShape(std::move(shape_rects));
 
   return RespondNow(NoArguments());
 }
diff --git a/extensions/browser/api/feedback_private/feedback_private_delegate.h b/extensions/browser/api/feedback_private/feedback_private_delegate.h
index feb1f066..ab9e048 100644
--- a/extensions/browser/api/feedback_private/feedback_private_delegate.h
+++ b/extensions/browser/api/feedback_private/feedback_private_delegate.h
@@ -15,6 +15,10 @@
 class BrowserContext;
 }  // namespace content
 
+namespace system_logs {
+class SystemLogsFetcher;
+}  // namespace system_logs
+
 namespace extensions {
 
 // Delegate class for embedder-specific chrome.feedbackPrivate behavior.
@@ -29,6 +33,10 @@
   virtual std::unique_ptr<base::DictionaryValue> GetStrings(
       content::BrowserContext* browser_context,
       bool from_crash) const = 0;
+
+  // Returns a SystemLogsFetcher for responding to a request for system logs.
+  virtual system_logs::SystemLogsFetcher* CreateSystemLogsFetcher(
+      content::BrowserContext* context) const = 0;
 };
 
 }  // namespace extensions
diff --git a/extensions/browser/api/storage/backend_task_runner.cc b/extensions/browser/api/storage/backend_task_runner.cc
index 0d5e614..50794e1 100644
--- a/extensions/browser/api/storage/backend_task_runner.cc
+++ b/extensions/browser/api/storage/backend_task_runner.cc
@@ -4,15 +4,14 @@
 
 #include "extensions/browser/api/storage/backend_task_runner.h"
 
-#include "base/task_scheduler/lazy_task_runner.h"
+#include "extensions/browser/extension_file_task_runner.h"
 
 namespace extensions {
 
-base::LazySequencedTaskRunner g_sequenced_task_task_runner =
-    LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER({base::MayBlock()});
-
+// TODO(stanisc): consider switching all calls of GetBackendTaskRunner() to
+// GetExtensionFileTaskRunner().
 scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner() {
-  return g_sequenced_task_task_runner.Get();
+  return GetExtensionFileTaskRunner();
 }
 
 bool IsOnBackendSequence() {
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index ed4ba26..ca452e18 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -578,8 +578,8 @@
                  image_loader_ptr_factory_.GetWeakPtr()));
 }
 
-void AppWindow::UpdateShape(std::unique_ptr<SkRegion> region) {
-  native_app_window_->UpdateShape(std::move(region));
+void AppWindow::UpdateShape(std::unique_ptr<ShapeRects> rects) {
+  native_app_window_->UpdateShape(std::move(rects));
 }
 
 void AppWindow::UpdateDraggableRegions(
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index 7d07ae89..09536a8 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -118,6 +118,8 @@
     FULLSCREEN_TYPE_FORCED = 1 << 3,
   };
 
+  using ShapeRects = std::vector<gfx::Rect>;
+
   struct BoundsSpecification {
     // INT_MIN represents an unspecified position component.
     static const int kUnspecifiedPosition;
@@ -275,8 +277,8 @@
   // Specifies a url for the launcher icon.
   void SetAppIconUrl(const GURL& icon_url);
 
-  // Set the window shape. Passing a NULL |region| sets the default shape.
-  void UpdateShape(std::unique_ptr<SkRegion> region);
+  // Sets the window shape. Passing a nullptr |rects| sets the default shape.
+  void UpdateShape(std::unique_ptr<ShapeRects> rects);
 
   // Called from the render interface to modify the draggable regions.
   void UpdateDraggableRegions(const std::vector<DraggableRegion>& regions);
diff --git a/extensions/browser/app_window/native_app_window.h b/extensions/browser/app_window/native_app_window.h
index 719180bb..0ae528f 100644
--- a/extensions/browser/app_window/native_app_window.h
+++ b/extensions/browser/app_window/native_app_window.h
@@ -29,6 +29,8 @@
 class NativeAppWindow : public ui::BaseWindow,
                         public web_modal::WebContentsModalDialogHost {
  public:
+  using ShapeRects = std::vector<gfx::Rect>;
+
   // Sets whether the window is fullscreen and the type of fullscreen.
   // |fullscreen_types| is a bit field of AppWindow::FullscreenType.
   virtual void SetFullscreen(int fullscreen_types) = 0;
@@ -46,12 +48,13 @@
   virtual void UpdateDraggableRegions(
       const std::vector<DraggableRegion>& regions) = 0;
 
-  // Returns the region used by frameless windows for dragging. May return NULL.
+  // Returns the region used by frameless windows for dragging. May return
+  // nullptr.
   virtual SkRegion* GetDraggableRegion() = 0;
 
-  // Called when the window shape is changed. If |region| is NULL then the
+  // Called when the window shape is changed. If |region| is nullptr then the
   // window is restored to the default shape.
-  virtual void UpdateShape(std::unique_ptr<SkRegion> region) = 0;
+  virtual void UpdateShape(std::unique_ptr<ShapeRects> rects) = 0;
 
   // Allows the window to handle unhandled keyboard messages coming back from
   // the renderer.
diff --git a/extensions/common/extension_api.cc b/extensions/common/extension_api.cc
index a60af27..8dbf447 100644
--- a/extensions/common/extension_api.cc
+++ b/extensions/common/extension_api.cc
@@ -183,11 +183,10 @@
 
   // Check to see if there are any parts of this API that are allowed in this
   // context.
-  const std::vector<Feature*> features = provider->second->GetChildren(api);
-  for (std::vector<Feature*>::const_iterator it = features.begin();
-       it != features.end();
-       ++it) {
-    if ((*it)->IsAvailableToContext(extension, context, url).is_available())
+  const std::vector<const Feature*> features =
+      provider->second->GetChildren(api);
+  for (const Feature* feature : features) {
+    if (feature->IsAvailableToContext(extension, context, url).is_available())
       return true;
   }
 
@@ -210,7 +209,7 @@
                                                 Feature::Context context,
                                                 const GURL& url,
                                                 CheckAliasStatus check_alias) {
-  Feature* feature = GetFeatureDependency(full_name);
+  const Feature* feature = GetFeatureDependency(full_name);
   if (!feature) {
     return Feature::Availability(Feature::NOT_PRESENT,
                                  std::string("Unknown feature: ") + full_name);
@@ -222,7 +221,7 @@
     return availability;
 
   Feature::Availability alias_availability =
-      IsAliasAvailable(full_name, feature, extension, context, url);
+      IsAliasAvailable(full_name, *feature, extension, context, url);
   return alias_availability.is_available() ? alias_availability : availability;
 }
 
@@ -272,7 +271,8 @@
   return result;
 }
 
-Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) {
+const Feature* ExtensionAPI::GetFeatureDependency(
+    const std::string& full_name) {
   std::string feature_type;
   std::string feature_name;
   SplitDependencyName(full_name, &feature_type, &feature_name);
@@ -282,7 +282,7 @@
   if (provider == dependency_providers_.end())
     return NULL;
 
-  Feature* feature = provider->second->GetFeature(feature_name);
+  const Feature* feature = provider->second->GetFeature(feature_name);
   // Try getting the feature for the parent API, if this was a child.
   if (!feature) {
     std::string child_name;
@@ -328,11 +328,11 @@
 
 Feature::Availability ExtensionAPI::IsAliasAvailable(
     const std::string& full_name,
-    Feature* feature,
+    const Feature& feature,
     const Extension* extension,
     Feature::Context context,
     const GURL& url) {
-  const std::string& alias = feature->alias();
+  const std::string& alias = feature.alias();
   if (alias.empty())
     return Feature::Availability(Feature::NOT_PRESENT, "Alias not defined");
 
@@ -348,7 +348,7 @@
   std::string child_name;
   GetAPINameFromFullName(full_name, &child_name);
   std::string full_alias_name = alias + "." + child_name;
-  Feature* alias_feature = provider->second->GetFeature(full_alias_name);
+  const Feature* alias_feature = provider->second->GetFeature(full_alias_name);
 
   // If there is no child feature, use the alias API feature to check
   // availability.
@@ -356,7 +356,7 @@
     alias_feature = provider->second->GetFeature(alias);
 
   CHECK(alias_feature) << "Cannot find alias feature " << alias
-                       << " for API feature " << feature->name();
+                       << " for API feature " << feature.name();
 
   return alias_feature->IsAvailableToContext(extension, context, url);
 }
diff --git a/extensions/common/extension_api.h b/extensions/common/extension_api.h
index 493674aecc4..01761a6 100644
--- a/extensions/common/extension_api.h
+++ b/extensions/common/extension_api.h
@@ -142,7 +142,7 @@
 
   // Gets a feature from any dependency provider registered with ExtensionAPI.
   // Returns NULL if the feature could not be found.
-  Feature* GetFeatureDependency(const std::string& dependency_name);
+  const Feature* GetFeatureDependency(const std::string& dependency_name);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(ExtensionAPITest, DefaultConfigurationFeatures);
@@ -157,7 +157,7 @@
   // Checks if |full_name| is available to provided context and extension under
   // associated API's alias name.
   Feature::Availability IsAliasAvailable(const std::string& full_name,
-                                         Feature* feature,
+                                         const Feature& feature,
                                          const Extension* extension,
                                          Feature::Context context,
                                          const GURL& url);
diff --git a/extensions/common/features/feature_provider.cc b/extensions/common/features/feature_provider.cc
index 2d75f82..51a697c 100644
--- a/extensions/common/features/feature_provider.cc
+++ b/extensions/common/features/feature_provider.cc
@@ -127,7 +127,7 @@
   return GetFeatureFromProviderByName("behavior", name);
 }
 
-Feature* FeatureProvider::GetFeature(const std::string& name) const {
+const Feature* FeatureProvider::GetFeature(const std::string& name) const {
   FeatureMap::const_iterator iter = features_.find(name);
   if (iter != features_.end())
     return iter->second.get();
@@ -149,7 +149,7 @@
 
 // Children of a given API are named starting with parent.name()+".", which
 // means they'll be contiguous in the features_ std::map.
-std::vector<Feature*> FeatureProvider::GetChildren(
+std::vector<const Feature*> FeatureProvider::GetChildren(
     const Feature& parent) const {
   std::string prefix = parent.name() + ".";
   const FeatureMap::const_iterator first_child = features_.lower_bound(prefix);
@@ -159,7 +159,7 @@
   const FeatureMap::const_iterator after_children =
       features_.lower_bound(prefix);
 
-  std::vector<Feature*> result;
+  std::vector<const Feature*> result;
   result.reserve(std::distance(first_child, after_children));
   for (FeatureMap::const_iterator it = first_child; it != after_children;
        ++it) {
diff --git a/extensions/common/features/feature_provider.h b/extensions/common/features/feature_provider.h
index c5cde10..0576dc7c 100644
--- a/extensions/common/features/feature_provider.h
+++ b/extensions/common/features/feature_provider.h
@@ -18,7 +18,7 @@
 
 // Note: Binding code (specifically native_extension_bindings_system.cc) relies
 // on this being a sorted map.
-using FeatureMap = std::map<std::string, std::unique_ptr<Feature>>;
+using FeatureMap = std::map<std::string, std::unique_ptr<const Feature>>;
 
 // Implemented by classes that can vend features.
 class FeatureProvider {
@@ -46,13 +46,13 @@
   static const Feature* GetBehaviorFeature(const std::string& name);
 
   // Returns the feature with the specified name.
-  Feature* GetFeature(const std::string& name) const;
+  const Feature* GetFeature(const std::string& name) const;
 
   // Returns the parent feature of |feature|, or null if there isn't one.
   const Feature* GetParent(const Feature& feature) const;
 
   // Returns the features inside the |parent| namespace, recursively.
-  std::vector<Feature*> GetChildren(const Feature& parent) const;
+  std::vector<const Feature*> GetChildren(const Feature& parent) const;
 
   // Returns a map containing all features described by this instance.
   // TODO(devlin): Rename this to be features().
diff --git a/extensions/common/features/feature_provider_unittest.cc b/extensions/common/features/feature_provider_unittest.cc
index 2bf161f..1af86f9 100644
--- a/extensions/common/features/feature_provider_unittest.cc
+++ b/extensions/common/features/feature_provider_unittest.cc
@@ -56,7 +56,7 @@
           .Build();
   ASSERT_TRUE(extension.get());
 
-  Feature* feature = provider->GetFeature("description");
+  const Feature* feature = provider->GetFeature("description");
   EXPECT_EQ(Feature::IS_AVAILABLE,
             feature
                 ->IsAvailableToContext(extension.get(),
@@ -125,7 +125,7 @@
   ASSERT_TRUE(app->is_platform_app());
 
   // A permission requested in the manifest is available.
-  Feature* feature = provider->GetFeature("power");
+  const Feature* feature = provider->GetFeature("power");
   EXPECT_EQ(Feature::IS_AVAILABLE,
             feature
                 ->IsAvailableToContext(app.get(), Feature::UNSPECIFIED_CONTEXT,
@@ -169,9 +169,9 @@
   add_feature("parent.other_child.other_grandchild");
   add_feature("parent.unparented_child", true);
 
-  Feature* parent = provider.GetFeature("parent");
+  const Feature* parent = provider.GetFeature("parent");
   ASSERT_TRUE(parent);
-  std::vector<Feature*> children = provider.GetChildren(*parent);
+  std::vector<const Feature*> children = provider.GetChildren(*parent);
   std::set<std::string> children_names;
   for (const Feature* child : children)
     children_names.insert(child->name());
diff --git a/extensions/common/features/simple_feature.cc b/extensions/common/features/simple_feature.cc
index 4e68be6..55b5da0 100644
--- a/extensions/common/features/simple_feature.cc
+++ b/extensions/common/features/simple_feature.cc
@@ -469,7 +469,7 @@
 Feature::Availability SimpleFeature::CheckDependencies(
     const base::Callback<Availability(const Feature*)>& checker) const {
   for (const auto& dep_name : dependencies_) {
-    Feature* dependency =
+    const Feature* dependency =
         ExtensionAPI::GetSharedInstance()->GetFeatureDependency(dep_name);
     if (!dependency)
       return CreateAvailability(NOT_PRESENT);
diff --git a/extensions/common/manifest.cc b/extensions/common/manifest.cc
index c2e608f..0aef218 100644
--- a/extensions/common/manifest.cc
+++ b/extensions/common/manifest.cc
@@ -247,7 +247,8 @@
 }
 
 bool Manifest::CanAccessKey(const std::string& key) const {
-  Feature* feature = FeatureProvider::GetManifestFeatures()->GetFeature(key);
+  const Feature* feature =
+      FeatureProvider::GetManifestFeatures()->GetFeature(key);
   if (!feature)
     return true;
 
diff --git a/extensions/common/manifest_handlers/permissions_parser.cc b/extensions/common/manifest_handlers/permissions_parser.cc
index af8ba0c..5cbe8bc 100644
--- a/extensions/common/manifest_handlers/permissions_parser.cc
+++ b/extensions/common/manifest_handlers/permissions_parser.cc
@@ -117,7 +117,7 @@
   for (APIPermissionSet::const_iterator iter = api_permissions->begin();
        iter != api_permissions->end();
        ++iter) {
-    Feature* feature = permission_features->GetFeature(iter->name());
+    const Feature* feature = permission_features->GetFeature(iter->name());
 
     // The feature should exist since we just got an APIPermission for it. The
     // two systems should be updated together whenever a permission is added.
diff --git a/extensions/components/native_app_window/native_app_window_views.cc b/extensions/components/native_app_window/native_app_window_views.cc
index 4081af9..0104b55 100644
--- a/extensions/components/native_app_window/native_app_window_views.cc
+++ b/extensions/components/native_app_window/native_app_window_views.cc
@@ -372,7 +372,7 @@
   return draggable_region_.get();
 }
 
-void NativeAppWindowViews::UpdateShape(std::unique_ptr<SkRegion> region) {
+void NativeAppWindowViews::UpdateShape(std::unique_ptr<ShapeRects> rects) {
   // Stub implementation. See also ChromeNativeAppWindowViews.
 }
 
diff --git a/extensions/components/native_app_window/native_app_window_views.h b/extensions/components/native_app_window/native_app_window_views.h
index f89c263..a782d40 100644
--- a/extensions/components/native_app_window/native_app_window_views.h
+++ b/extensions/components/native_app_window/native_app_window_views.h
@@ -130,7 +130,7 @@
   void UpdateDraggableRegions(
       const std::vector<extensions::DraggableRegion>& regions) override;
   SkRegion* GetDraggableRegion() override;
-  void UpdateShape(std::unique_ptr<SkRegion> region) override;
+  void UpdateShape(std::unique_ptr<ShapeRects> rects) override;
   void HandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
   bool IsFrameless() const override;
diff --git a/extensions/renderer/bindings/argument_spec.cc b/extensions/renderer/bindings/argument_spec.cc
index d97263d..b7929cf 100644
--- a/extensions/renderer/bindings/argument_spec.cc
+++ b/extensions/renderer/bindings/argument_spec.cc
@@ -214,7 +214,10 @@
 
   switch (type_) {
     case ArgumentType::INTEGER:
-      is_valid_type = value->IsInt32();
+      // -0 is treated internally as a double, but we classify it as an integer.
+      is_valid_type =
+          value->IsInt32() ||
+          (value->IsNumber() && value.As<v8::Number>()->Value() == 0.0);
       break;
     case ArgumentType::DOUBLE:
       is_valid_type = value->IsNumber();
@@ -383,8 +386,15 @@
     std::string* error) const {
   switch (type_) {
     case ArgumentType::INTEGER: {
-      DCHECK(value->IsInt32());
-      int int_val = value.As<v8::Int32>()->Value();
+      DCHECK(value->IsNumber());
+      int int_val = 0;
+      if (value->IsInt32()) {
+        int_val = value.As<v8::Int32>()->Value();
+      } else {
+        // See comment in IsCorrectType().
+        DCHECK_EQ(0.0, value.As<v8::Number>()->Value());
+        int_val = 0;
+      }
       if (!CheckFundamentalBounds(int_val, minimum_, maximum_, error))
         return false;
       if (out_value)
diff --git a/extensions/renderer/bindings/argument_spec_unittest.cc b/extensions/renderer/bindings/argument_spec_unittest.cc
index a12b487..1ebabea 100644
--- a/extensions/renderer/bindings/argument_spec_unittest.cc
+++ b/extensions/renderer/bindings/argument_spec_unittest.cc
@@ -154,6 +154,10 @@
     ExpectSuccess(spec, "-1", "-1");
     ExpectSuccess(spec, "0", "0");
     ExpectSuccess(spec, "0.0", "0");
+    ExpectSuccess(spec, "-0.0", "0");
+    ExpectSuccess(spec, "-0.", "0");
+    ExpectSuccess(spec, "-.0", "0");
+    ExpectSuccess(spec, "-0", "0");
     ExpectFailure(spec, "undefined", InvalidType(kTypeInteger, kTypeUndefined));
     ExpectFailure(spec, "null", InvalidType(kTypeInteger, kTypeNull));
     ExpectFailure(spec, "1.1", InvalidType(kTypeInteger, kTypeDouble));
diff --git a/extensions/renderer/feature_cache.cc b/extensions/renderer/feature_cache.cc
index df10bd2..11e28d7 100644
--- a/extensions/renderer/feature_cache.cc
+++ b/extensions/renderer/feature_cache.cc
@@ -61,7 +61,7 @@
   GURL empty_url;
   const Extension* extension = context->extension();
   for (const auto& map_entry : api_feature_provider->GetAllFeatures()) {
-    Feature* feature = map_entry.second.get();
+    const Feature* feature = map_entry.second.get();
     // Exclude internal APIs.
     if (feature->IsInternal())
       continue;
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 495aa1c..d821baf 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -174,6 +174,8 @@
 
   if (use_aura) {
     sources += [
+      "browser/root_window_controller.cc",
+      "browser/root_window_controller.h",
       "browser/shell_app_window_client_aura.cc",
       "browser/shell_desktop_controller_aura.cc",
       "browser/shell_desktop_controller_aura.h",
@@ -309,6 +311,7 @@
 
   if (use_aura) {
     sources += [
+      "browser/root_window_controller_unittest.cc",
       "browser/shell_desktop_controller_aura_unittest.cc",
       "browser/shell_native_app_window_aura_unittest.cc",
       "browser/shell_screen_unittest.cc",
diff --git a/extensions/shell/browser/default_shell_browser_main_delegate.cc b/extensions/shell/browser/default_shell_browser_main_delegate.cc
index 1275f32..c81eb702 100644
--- a/extensions/shell/browser/default_shell_browser_main_delegate.cc
+++ b/extensions/shell/browser/default_shell_browser_main_delegate.cc
@@ -101,9 +101,10 @@
 void DefaultShellBrowserMainDelegate::Shutdown() {
 }
 
-DesktopController* DefaultShellBrowserMainDelegate::CreateDesktopController() {
+DesktopController* DefaultShellBrowserMainDelegate::CreateDesktopController(
+    content::BrowserContext* context) {
 #if defined(USE_AURA)
-  return new ShellDesktopControllerAura();
+  return new ShellDesktopControllerAura(context);
 #elif defined(OS_MACOSX)
   return new ShellDesktopControllerMac();
 #else
diff --git a/extensions/shell/browser/default_shell_browser_main_delegate.h b/extensions/shell/browser/default_shell_browser_main_delegate.h
index 05ce558..64a132c 100644
--- a/extensions/shell/browser/default_shell_browser_main_delegate.h
+++ b/extensions/shell/browser/default_shell_browser_main_delegate.h
@@ -21,7 +21,8 @@
   // ShellBrowserMainDelegate:
   void Start(content::BrowserContext* context) override;
   void Shutdown() override;
-  DesktopController* CreateDesktopController() override;
+  DesktopController* CreateDesktopController(
+      content::BrowserContext* context) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(DefaultShellBrowserMainDelegate);
diff --git a/extensions/shell/browser/root_window_controller.cc b/extensions/shell/browser/root_window_controller.cc
new file mode 100644
index 0000000..4988082
--- /dev/null
+++ b/extensions/shell/browser/root_window_controller.cc
@@ -0,0 +1,169 @@
+// 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 "extensions/shell/browser/root_window_controller.h"
+
+#include "extensions/browser/app_window/app_window.h"
+#include "extensions/browser/app_window/native_app_window.h"
+#include "extensions/shell/browser/shell_app_delegate.h"
+#include "extensions/shell/browser/shell_screen.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace extensions {
+
+namespace {
+
+// A simple layout manager that makes each new window fill its parent.
+class FillLayout : public aura::LayoutManager {
+ public:
+  FillLayout(aura::Window* owner) : owner_(owner) { DCHECK(owner_); }
+  ~FillLayout() override = default;
+
+ private:
+  // aura::LayoutManager:
+  void OnWindowResized() override {
+    // Size the owner's immediate child windows.
+    aura::WindowTracker children_tracker(owner_->children());
+    while (!children_tracker.windows().empty()) {
+      aura::Window* child = children_tracker.Pop();
+      child->SetBounds(gfx::Rect(owner_->bounds().size()));
+    }
+  }
+
+  void OnWindowAddedToLayout(aura::Window* child) override {
+    DCHECK_EQ(owner_, child->parent());
+
+    // Create a rect at 0,0 with the size of the parent.
+    gfx::Size parent_size = child->parent()->bounds().size();
+    child->SetBounds(gfx::Rect(parent_size));
+  }
+
+  void OnWillRemoveWindowFromLayout(aura::Window* child) override {}
+
+  void OnWindowRemovedFromLayout(aura::Window* child) override {}
+
+  void OnChildWindowVisibilityChanged(aura::Window* child,
+                                      bool visible) override {}
+
+  void SetChildBounds(aura::Window* child,
+                      const gfx::Rect& requested_bounds) override {
+    SetChildBoundsDirect(child, requested_bounds);
+  }
+
+  aura::Window* owner_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(FillLayout);
+};
+
+}  // namespace
+
+RootWindowController::RootWindowController(
+    DesktopDelegate* desktop_delegate,
+    ShellScreen* screen,
+    const gfx::Rect& bounds,
+    content::BrowserContext* browser_context)
+    : desktop_delegate_(desktop_delegate),
+      screen_(screen),
+      browser_context_(browser_context) {
+  DCHECK(desktop_delegate_);
+  DCHECK(screen_);
+  DCHECK(browser_context_);
+  host_.reset(aura::WindowTreeHost::Create(
+      gfx::Rect(screen_->GetPrimaryDisplay().GetSizeInPixel())));
+
+  // The ShellScreen will resize the primary display when the WindowTreeHost is
+  // resized, so the display is always the size of the WindowTreeHost.
+  host_->AddObserver(screen_);
+
+  host_->InitHost();
+  host_->window()->Show();
+
+  aura::client::SetWindowParentingClient(host_->window(), this);
+
+  // Ensure the window fills the display.
+  host_->window()->SetLayoutManager(new FillLayout(host_->window()));
+
+  host_->AddObserver(this);
+  host_->Show();
+}
+
+RootWindowController::~RootWindowController() {
+  CloseAppWindows();
+  DestroyWindowTreeHost();
+}
+
+AppWindow* RootWindowController::CreateAppWindow(
+    content::BrowserContext* browser_context,
+    const Extension* extension) {
+  // Only one BrowserContext should ever be used with this class.
+  DCHECK_EQ(browser_context_, browser_context);
+
+  if (app_windows_.empty()) {
+    // Start observing for OnAppWindowRemoved.
+    AppWindowRegistry* registry = AppWindowRegistry::Get(browser_context_);
+    registry->AddObserver(this);
+  }
+
+  app_windows_.push_back(
+      new AppWindow(browser_context, new ShellAppDelegate, extension));
+  return app_windows_.back();
+}
+
+void RootWindowController::AddAppWindow(gfx::NativeWindow window) {
+  aura::Window* root_window = host_->window();
+  root_window->AddChild(window);
+}
+
+void RootWindowController::CloseAppWindows() {
+  if (app_windows_.empty())
+    return;
+
+  // Remove the observer before closing windows to avoid triggering
+  // OnAppWindowRemoved, which would mutate |app_windows_|.
+  AppWindowRegistry::Get(browser_context_)->RemoveObserver(this);
+  for (AppWindow* app_window : app_windows_)
+    app_window->GetBaseWindow()->Close();  // Close() deletes |app_window|.
+  app_windows_.clear();
+}
+
+void RootWindowController::UpdateSize(const gfx::Size& size) {
+  host_->UpdateRootWindowSizeInPixels(size);
+}
+
+aura::Window* RootWindowController::GetDefaultParent(aura::Window* window,
+                                                     const gfx::Rect& bounds) {
+  return host_->window();
+}
+
+void RootWindowController::OnHostCloseRequested(aura::WindowTreeHost* host) {
+  DCHECK_EQ(host_.get(), host);
+  CloseAppWindows();
+
+  // The ShellDesktopControllerAura will delete us.
+  desktop_delegate_->CloseRootWindowController(this);
+}
+
+void RootWindowController::OnAppWindowRemoved(AppWindow* window) {
+  if (app_windows_.empty())
+    return;
+
+  // If we created this AppWindow, remove it from our list so we don't try to
+  // close it again later.
+  app_windows_.remove(window);
+  if (app_windows_.empty())
+    AppWindowRegistry::Get(browser_context_)->RemoveObserver(this);
+}
+
+void RootWindowController::DestroyWindowTreeHost() {
+  host_->RemoveObserver(screen_);
+  host_->RemoveObserver(this);
+  host_.reset();
+}
+
+}  // namespace extensions
diff --git a/extensions/shell/browser/root_window_controller.h b/extensions/shell/browser/root_window_controller.h
new file mode 100644
index 0000000..92bf969
--- /dev/null
+++ b/extensions/shell/browser/root_window_controller.h
@@ -0,0 +1,109 @@
+// 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 EXTENSIONS_SHELL_BROWSER_ROOT_WINDOW_CONTROLLER_H_
+#define EXTENSIONS_SHELL_BROWSER_ROOT_WINDOW_CONTROLLER_H_
+
+#include <list>
+#include <memory>
+
+#include "base/macros.h"
+#include "extensions/browser/app_window/app_window_registry.h"
+#include "ui/aura/client/window_parenting_client.h"
+#include "ui/aura/window_tree_host_observer.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace aura {
+class WindowTreeHost;
+}  // namespace aura
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace gfx {
+class Rect;
+class Size;
+}  // namespace gfx
+
+namespace extensions {
+class AppWindow;
+class Extension;
+class ShellScreen;
+
+// Owns and manages a WindowTreeHost for a display. New AppWindows will fill
+// the entire root window. Any additional AppWindows are simply drawn over the
+// existing AppWindow(s) and cannot be resized except by resizing the
+// WindowTreeHost.
+class RootWindowController : public aura::client::WindowParentingClient,
+                             public aura::WindowTreeHostObserver,
+                             public AppWindowRegistry::Observer {
+ public:
+  class DesktopDelegate {
+   public:
+    virtual ~DesktopDelegate() = default;
+
+    // Called when the root window requests to be closed. This should eventually
+    // destroy |root_window_controller|.
+    virtual void CloseRootWindowController(
+        RootWindowController* root_window_controller) = 0;
+  };
+
+  // RootWindowController initializes and displays a WindowTreeHost within
+  // |bounds|. |desktop_delegate| must outlive the RootWindowController.
+  RootWindowController(DesktopDelegate* desktop_delegate,
+                       ShellScreen* screen,
+                       const gfx::Rect& bounds,
+                       content::BrowserContext* browser_context);
+  ~RootWindowController() override;
+
+  // Creates a new app window and adds it to the desktop. The AppWindow deletes
+  // itself when its native window closes.
+  AppWindow* CreateAppWindow(content::BrowserContext* context,
+                             const Extension* extension);
+
+  // Attaches a NativeAppWindow's window to our root window.
+  void AddAppWindow(gfx::NativeWindow window);
+
+  // Closes the root window's AppWindows, resulting in their destruction.
+  void CloseAppWindows();
+
+  // Updates the size of the root window.
+  void UpdateSize(const gfx::Size& size);
+
+  aura::WindowTreeHost* host() { return host_.get(); }
+
+  // aura::client::WindowParentingClient:
+  aura::Window* GetDefaultParent(aura::Window* window,
+                                 const gfx::Rect& bounds) override;
+
+  // aura::WindowTreeHostObserver:
+  void OnHostCloseRequested(aura::WindowTreeHost* host) override;
+
+  // AppWindowRegistry::Observer:
+  void OnAppWindowRemoved(AppWindow* app_window) override;
+
+ private:
+  void DestroyWindowTreeHost();
+
+  DesktopDelegate* const desktop_delegate_;
+  ShellScreen* const screen_;
+
+  // The BrowserContext used to create AppWindows.
+  content::BrowserContext* const browser_context_;
+
+  // The host we create.
+  std::unique_ptr<aura::WindowTreeHost> host_;
+
+  // List of AppWindows we've created. Used to close any remaining app windows
+  // when |host_| is closed or |this| is destroyed.
+  // Note: Pointers are unowned. NativeAppWindow::Close() will delete them.
+  std::list<AppWindow*> app_windows_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowController);
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_SHELL_BROWSER_ROOT_WINDOW_CONTROLLER_H_
diff --git a/extensions/shell/browser/root_window_controller_unittest.cc b/extensions/shell/browser/root_window_controller_unittest.cc
new file mode 100644
index 0000000..1455c7b
--- /dev/null
+++ b/extensions/shell/browser/root_window_controller_unittest.cc
@@ -0,0 +1,236 @@
+// 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 "extensions/shell/browser/root_window_controller.h"
+
+#include <algorithm>
+#include <list>
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/app_window/app_window.h"
+#include "extensions/browser/app_window/native_app_window.h"
+#include "extensions/common/test_util.h"
+#include "extensions/shell/browser/root_window_controller.h"
+#include "extensions/shell/browser/shell_app_window_client.h"
+#include "extensions/shell/browser/shell_native_app_window_aura.h"
+#include "extensions/shell/browser/shell_screen.h"
+#include "extensions/shell/test/shell_test_base_aura.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace extensions {
+
+namespace {
+
+// A fake that creates and exposes RootWindowControllers.
+class FakeDesktopDelegate : public RootWindowController::DesktopDelegate {
+ public:
+  explicit FakeDesktopDelegate(content::BrowserContext* browser_context,
+                               ShellScreen* screen,
+                               const gfx::Rect& bounds)
+      : browser_context_(browser_context), screen_(screen), bounds_(bounds) {}
+  ~FakeDesktopDelegate() override = default;
+
+  RootWindowController* CreateRootWindowController() {
+    root_window_controllers_.emplace_back(
+        base::MakeUnique<RootWindowController>(this, screen_, bounds_,
+                                               browser_context_));
+    return root_window_controllers_.back().get();
+  }
+
+  // RootWindowController::DesktopDelegate:
+  void CloseRootWindowController(
+      RootWindowController* root_window_controller) override {
+    auto it = std::find_if(root_window_controllers_.begin(),
+                           root_window_controllers_.end(),
+                           [&](const auto& candidate) {
+                             return candidate.get() == root_window_controller;
+                           });
+    DCHECK(it != root_window_controllers_.end());
+    root_window_controllers_.erase(it);
+  }
+
+  auto root_window_controller_count() {
+    return root_window_controllers_.size();
+  }
+
+ private:
+  content::BrowserContext* browser_context_;
+  ShellScreen* screen_;
+  const gfx::Rect bounds_;
+  std::list<std::unique_ptr<RootWindowController>> root_window_controllers_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeDesktopDelegate);
+};
+
+// An AppWindowClient for use without a DesktopController.
+class TestAppWindowClient : public ShellAppWindowClient {
+ public:
+  TestAppWindowClient() = default;
+  ~TestAppWindowClient() override = default;
+
+  NativeAppWindow* CreateNativeAppWindow(
+      AppWindow* window,
+      AppWindow::CreateParams* params) override {
+    return new ShellNativeAppWindowAura(window, *params);
+  }
+};
+
+constexpr gfx::Rect kScreenBounds = gfx::Rect(0, 0, 800, 600);
+
+}  // namespace
+
+class RootWindowControllerTest : public ShellTestBaseAura {
+ public:
+  RootWindowControllerTest() = default;
+  ~RootWindowControllerTest() override = default;
+
+  void SetUp() override {
+    ShellTestBaseAura::SetUp();
+
+    AppWindowClient::Set(&app_window_client_);
+    screen_ = base::MakeUnique<ShellScreen>(nullptr, kScreenBounds.size());
+    display::Screen::SetScreenInstance(screen_.get());
+    extension_ = test_util::CreateEmptyExtension();
+
+    desktop_delegate_ = base::MakeUnique<FakeDesktopDelegate>(
+        browser_context(), screen_.get(), kScreenBounds);
+  }
+
+  void TearDown() override {
+    desktop_delegate_.reset();
+    display::Screen::SetScreenInstance(nullptr);
+    screen_.reset();
+    AppWindowClient::Set(nullptr);
+    ShellTestBaseAura::TearDown();
+  }
+
+ protected:
+  // Creates and returns an AppWindow using the RootWindowController.
+  AppWindow* CreateAppWindow(RootWindowController* root) {
+    AppWindow* app_window =
+        root->CreateAppWindow(browser_context(), extension());
+    InitAppWindow(app_window);
+    root->AddAppWindow(app_window->GetNativeWindow());
+    return app_window;
+  }
+
+  // Checks that there are |num_expected| AppWindows open.
+  void ExpectNumAppWindows(RootWindowController* root, size_t expected) {
+    EXPECT_EQ(expected, root->host()->window()->children().size());
+    EXPECT_EQ(expected,
+              AppWindowRegistry::Get(browser_context())->app_windows().size());
+  }
+
+  FakeDesktopDelegate* desktop_delegate() { return desktop_delegate_.get(); }
+  const Extension* extension() { return extension_.get(); }
+
+  ShellScreen* screen() { return screen_.get(); }
+
+ private:
+  TestAppWindowClient app_window_client_;
+
+  scoped_refptr<Extension> extension_;
+  std::unique_ptr<ShellScreen> screen_;
+  std::unique_ptr<FakeDesktopDelegate> desktop_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootWindowControllerTest);
+};
+
+// Tests RootWindowController's basic setup and teardown.
+TEST_F(RootWindowControllerTest, Basic) {
+  RootWindowController* root_window_controller =
+      desktop_delegate()->CreateRootWindowController();
+  EXPECT_TRUE(root_window_controller->host());
+
+  // The RootWindowController destroys itself when the root window closes.
+  root_window_controller->OnHostCloseRequested(root_window_controller->host());
+  EXPECT_EQ(0u, desktop_delegate()->root_window_controller_count());
+}
+
+// Tests the window layout.
+TEST_F(RootWindowControllerTest, FillLayout) {
+  RootWindowController* root_window_controller =
+      desktop_delegate()->CreateRootWindowController();
+
+  root_window_controller->host()->SetBoundsInPixels(gfx::Rect(0, 0, 500, 700));
+
+  AppWindow* app_window =
+      root_window_controller->CreateAppWindow(browser_context(), extension());
+  InitAppWindow(app_window);
+  root_window_controller->AddAppWindow(app_window->GetNativeWindow());
+
+  ExpectNumAppWindows(root_window_controller, 1u);
+
+  // Test that reshaping the host window also resizes the child window, and
+  // moving the host doesn't affect the child's position relative to the host.
+  root_window_controller->host()->SetBoundsInPixels(
+      gfx::Rect(100, 200, 300, 400));
+
+  const aura::Window* root_window = root_window_controller->host()->window();
+  EXPECT_EQ(gfx::Rect(0, 0, 300, 400), root_window->bounds());
+  EXPECT_EQ(gfx::Rect(0, 0, 300, 400), root_window->children()[0]->bounds());
+
+  // The AppWindow will close on shutdown.
+}
+
+// Tests creating and removing AppWindows.
+TEST_F(RootWindowControllerTest, AppWindows) {
+  RootWindowController* root_window_controller =
+      desktop_delegate()->CreateRootWindowController();
+
+  {
+    // Create some AppWindows.
+    CreateAppWindow(root_window_controller);
+    AppWindow* middle_window = CreateAppWindow(root_window_controller);
+    CreateAppWindow(root_window_controller);
+
+    ExpectNumAppWindows(root_window_controller, 3u);
+
+    // Close one window, which deletes |middle_window|.
+    middle_window->GetBaseWindow()->Close();
+  }
+
+  ExpectNumAppWindows(root_window_controller, 2u);
+
+  // Close all remaining windows.
+  root_window_controller->CloseAppWindows();
+  ExpectNumAppWindows(root_window_controller, 0u);
+}
+
+// Tests that a second RootWindowController can be used independently of the
+// first.
+TEST_F(RootWindowControllerTest, Multiple) {
+  // Create the longer-lived RootWindowController before the shorter-lived one
+  // is deleted. Otherwise it may be created at the same address, preventing
+  // the test from failing on use-after-free.
+  RootWindowController* longer_lived =
+      desktop_delegate()->CreateRootWindowController();
+  {
+    RootWindowController* shorter_lived =
+        desktop_delegate()->CreateRootWindowController();
+    {
+      AppWindow* app_window = CreateAppWindow(shorter_lived);
+      ExpectNumAppWindows(shorter_lived, 1u);
+      app_window->GetBaseWindow()->Close();  // Deletes the AppWindow.
+    }
+    ExpectNumAppWindows(shorter_lived, 0u);
+
+    // Simulate a close request to delete the controller.
+    shorter_lived->OnHostCloseRequested(shorter_lived->host());
+  }
+
+  // The still-living RootWindowController can still be used.
+  AppWindow* app_window = CreateAppWindow(longer_lived);
+  ExpectNumAppWindows(longer_lived, 1u);
+  app_window->GetBaseWindow()->Close();
+}
+
+}  // namespace extensions
diff --git a/extensions/shell/browser/shell_browser_main_delegate.h b/extensions/shell/browser/shell_browser_main_delegate.h
index a3ddf7f..67d2d7c 100644
--- a/extensions/shell/browser/shell_browser_main_delegate.h
+++ b/extensions/shell/browser/shell_browser_main_delegate.h
@@ -28,7 +28,8 @@
   // Creates the ShellDesktopControllerAura instance to initialize the root
   // window and window manager. Subclass may return its subclass to customize
   // the window manager.
-  virtual DesktopController* CreateDesktopController() = 0;
+  virtual DesktopController* CreateDesktopController(
+      content::BrowserContext* context) = 0;
 };
 
 }  // namespace extensions
diff --git a/extensions/shell/browser/shell_browser_main_parts.cc b/extensions/shell/browser/shell_browser_main_parts.cc
index ea8076a1..b772850a 100644
--- a/extensions/shell/browser/shell_browser_main_parts.cc
+++ b/extensions/shell/browser/shell_browser_main_parts.cc
@@ -179,7 +179,8 @@
 
   storage_monitor::StorageMonitor::Create();
 
-  desktop_controller_.reset(browser_main_delegate_->CreateDesktopController());
+  desktop_controller_.reset(
+      browser_main_delegate_->CreateDesktopController(browser_context_.get()));
 
   // TODO(jamescook): Initialize user_manager::UserManager.
 
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.cc b/extensions/shell/browser/shell_desktop_controller_aura.cc
index 5ecf36f..c563510 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura.cc
@@ -4,36 +4,28 @@
 
 #include "extensions/shell/browser/shell_desktop_controller_aura.h"
 
-#include <algorithm>
 #include <string>
 
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "extensions/browser/app_window/app_window.h"
-#include "extensions/browser/app_window/native_app_window.h"
-#include "extensions/shell/browser/shell_app_delegate.h"
 #include "extensions/shell/browser/shell_app_window_client.h"
 #include "extensions/shell/browser/shell_screen.h"
 #include "extensions/shell/common/switches.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/default_capture_client.h"
-#include "ui/aura/layout_manager.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_tracker.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/image_cursors.h"
 #include "ui/base/ime/input_method.h"
+#include "ui/base/ime/input_method_factory.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 #include "ui/display/screen.h"
-#include "ui/events/event_sink.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
@@ -58,55 +50,14 @@
 namespace extensions {
 namespace {
 
-// A simple layout manager that makes each new window fill the root window.
-class FillLayout : public aura::LayoutManager {
- public:
-  FillLayout(aura::Window* owner) : owner_(owner) { DCHECK(owner_); }
-
-  ~FillLayout() override {}
-
- private:
-  // aura::LayoutManager:
-  void OnWindowResized() override {
-    // Size the owner's immediate child windows.
-    aura::WindowTracker children_tracker(owner_->children());
-    while (!children_tracker.windows().empty()) {
-      aura::Window* child = children_tracker.Pop();
-      child->SetBounds(gfx::Rect(owner_->bounds().size()));
-    }
-  }
-
-  void OnWindowAddedToLayout(aura::Window* child) override {
-    DCHECK_EQ(owner_, child->parent());
-
-    // Create a rect at 0,0 with the size of the parent.
-    gfx::Size parent_size = child->parent()->bounds().size();
-    child->SetBounds(gfx::Rect(parent_size));
-  }
-
-  void OnWillRemoveWindowFromLayout(aura::Window* child) override {}
-
-  void OnWindowRemovedFromLayout(aura::Window* child) override {}
-
-  void OnChildWindowVisibilityChanged(aura::Window* child,
-                                      bool visible) override {}
-
-  void SetChildBounds(aura::Window* child,
-                      const gfx::Rect& requested_bounds) override {
-    SetChildBoundsDirect(child, requested_bounds);
-  }
-
-  aura::Window* owner_;  // Not owned.
-
-  DISALLOW_COPY_AND_ASSIGN(FillLayout);
-};
-
 // A class that bridges the gap between CursorManager and Aura. It borrows
 // heavily from NativeCursorManagerAsh.
 class ShellNativeCursorManager : public wm::NativeCursorManager {
  public:
-  explicit ShellNativeCursorManager(aura::WindowTreeHost* host)
-      : host_(host), image_cursors_(new ui::ImageCursors) {}
+  explicit ShellNativeCursorManager(
+      ShellDesktopControllerAura* desktop_controller)
+      : desktop_controller_(desktop_controller),
+        image_cursors_(new ui::ImageCursors) {}
   ~ShellNativeCursorManager() override {}
 
   // wm::NativeCursorManager overrides.
@@ -123,7 +74,7 @@
     delegate->CommitCursor(cursor);
 
     if (delegate->IsCursorVisible())
-      ApplyCursor(cursor);
+      SetCursorOnAllRootWindows(cursor);
   }
 
   void SetVisibility(bool visible,
@@ -135,7 +86,7 @@
     } else {
       gfx::NativeCursor invisible_cursor(ui::CursorType::kNone);
       image_cursors_->SetPlatformCursor(&invisible_cursor);
-      ApplyCursor(invisible_cursor);
+      SetCursorOnAllRootWindows(invisible_cursor);
     }
   }
 
@@ -156,9 +107,12 @@
 
  private:
   // Sets |cursor| as the active cursor within Aura.
-  void ApplyCursor(gfx::NativeCursor cursor) { host_->SetCursor(cursor); }
+  void SetCursorOnAllRootWindows(gfx::NativeCursor cursor) {
+    if (desktop_controller_->GetPrimaryHost())
+      desktop_controller_->GetPrimaryHost()->SetCursor(cursor);
+  }
 
-  aura::WindowTreeHost* host_;  // Not owned.
+  ShellDesktopControllerAura* desktop_controller_;  // Not owned.
 
   std::unique_ptr<ui::ImageCursors> image_cursors_;
 
@@ -180,8 +134,10 @@
 
 }  // namespace
 
-ShellDesktopControllerAura::ShellDesktopControllerAura()
-    : app_window_client_(new ShellAppWindowClient) {
+ShellDesktopControllerAura::ShellDesktopControllerAura(
+    content::BrowserContext* browser_context)
+    : browser_context_(browser_context),
+      app_window_client_(new ShellAppWindowClient) {
   extensions::AppWindowClient::Set(app_window_client_.get());
 
 #if defined(OS_CHROMEOS)
@@ -193,12 +149,12 @@
   display_configurator_->ForceInitialConfigure();
   display_configurator_->AddObserver(this);
 #endif
-  CreateRootWindow();
+
+  InitWindowManager();
 }
 
 ShellDesktopControllerAura::~ShellDesktopControllerAura() {
-  CloseAppWindows();
-  DestroyRootWindow();
+  TearDownWindowManager();
 #if defined(OS_CHROMEOS)
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
       this);
@@ -216,41 +172,26 @@
 AppWindow* ShellDesktopControllerAura::CreateAppWindow(
     content::BrowserContext* context,
     const Extension* extension) {
-  // Start observing for OnAppWindowClosed. Note: We can't remove this observer
-  // later because this class expects to outlive the AppWindowRegistry.
-  AppWindowRegistry* registry = AppWindowRegistry::Get(context);
-  if (!registry->HasObserver(this))
-    registry->AddObserver(this);
-
-  app_windows_.push_back(
-      new AppWindow(context, new ShellAppDelegate, extension));
-  return app_windows_.back();
+  return root_window_controller_->CreateAppWindow(context, extension);
 }
 
 void ShellDesktopControllerAura::AddAppWindow(gfx::NativeWindow window) {
-  aura::Window* root_window = host_->window();
-  root_window->AddChild(window);
+  root_window_controller_->AddAppWindow(window);
 }
 
 void ShellDesktopControllerAura::CloseAppWindows() {
-  // Move the original list into a temporary one: Closing the windows will
-  // trigger OnAppWindowRemoved, which would invalidate iterators into the
-  // original list.
-  std::list<AppWindow*> app_windows = std::move(app_windows_);
-  for (AppWindow* app_window : app_windows)
-    app_window->GetBaseWindow()->Close();  // Close() deletes |app_window|.
+  root_window_controller_->CloseAppWindows();
 }
 
-void ShellDesktopControllerAura::OnAppWindowRemoved(AppWindow* window) {
-  // If we created this AppWindow, remove it from our list so we don't try to
-  // close it again later.
-  app_windows_.remove(window);
-}
+void ShellDesktopControllerAura::CloseRootWindowController(
+    RootWindowController* root_window_controller) {
+  DCHECK_EQ(root_window_controller_.get(), root_window_controller);
 
-aura::Window* ShellDesktopControllerAura::GetDefaultParent(
-    aura::Window* window,
-    const gfx::Rect& bounds) {
-  return host_->window();
+  // Just quit. The RootWindowController must stay alive until we begin
+  // TearDownWindowManager().
+  // run_loop_ may be null in tests.
+  if (run_loop_)
+    run_loop_->QuitWhenIdle();
 }
 
 #if defined(OS_CHROMEOS)
@@ -268,56 +209,103 @@
     const display::DisplayConfigurator::DisplayStateList& displays) {
   gfx::Size size = GetPrimaryDisplaySize();
   if (!size.IsEmpty())
-    host_->UpdateRootWindowSizeInPixels(size);
+    root_window_controller_->UpdateSize(size);
 }
 #endif
 
-void ShellDesktopControllerAura::OnHostCloseRequested(
-    aura::WindowTreeHost* host) {
-  DCHECK_EQ(host_.get(), host);
-  CloseAppWindows();
-
-  // run_loop_ may be null in tests.
-  if (run_loop_)
-    run_loop_->QuitWhenIdle();
-}
-
 ui::EventDispatchDetails ShellDesktopControllerAura::DispatchKeyEventPostIME(
     ui::KeyEvent* key_event) {
-  return host_->DispatchKeyEventPostIME(key_event);
+  DCHECK(root_window_controller_);
+
+  // TODO(michaelpg): With multiple windows, determine which root window
+  // triggered the event. See ash::WindowTreeHostManager for example.
+  return GetPrimaryHost()->DispatchKeyEventPostIME(key_event);
+}
+
+aura::WindowTreeHost* ShellDesktopControllerAura::GetPrimaryHost() {
+  if (!root_window_controller_)
+    return nullptr;
+  return root_window_controller_->host();
 }
 
 void ShellDesktopControllerAura::InitWindowManager() {
-  wm::FocusController* focus_controller =
-      new wm::FocusController(new AppsFocusRules());
-  aura::client::SetFocusClient(host_->window(), focus_controller);
-  host_->window()->AddPreTargetHandler(focus_controller);
-  wm::SetActivationClient(host_->window(), focus_controller);
-  focus_client_.reset(focus_controller);
+  root_window_event_filter_ = base::MakeUnique<wm::CompoundEventFilter>();
 
-  capture_client_.reset(
-      new aura::client::DefaultCaptureClient(host_->window()));
+  // Set up basic pieces of ui::wm.
+  screen_ = base::MakeUnique<ShellScreen>(this, GetStartingWindowSize());
+  display::Screen::SetScreenInstance(screen_.get());
 
-  // Ensure new windows fill the display.
-  host_->window()->SetLayoutManager(new FillLayout(host_->window()));
+  focus_controller_ =
+      base::MakeUnique<wm::FocusController>(new AppsFocusRules());
 
-  cursor_manager_.reset(
-      new wm::CursorManager(std::unique_ptr<wm::NativeCursorManager>(
-          new ShellNativeCursorManager(host_.get()))));
-  cursor_manager_->SetDisplay(
-      display::Screen::GetScreen()->GetPrimaryDisplay());
-  cursor_manager_->SetCursor(ui::CursorType::kPointer);
-  aura::client::SetCursorClient(host_->window(), cursor_manager_.get());
+  cursor_manager_ = base::MakeUnique<wm::CursorManager>(
+      base::MakeUnique<ShellNativeCursorManager>(this));
 
-  user_activity_detector_.reset(new ui::UserActivityDetector);
+  user_activity_detector_ = base::MakeUnique<ui::UserActivityDetector>();
 #if defined(OS_CHROMEOS)
-  user_activity_notifier_.reset(
-      new ui::UserActivityPowerManagerNotifier(user_activity_detector_.get()));
+  user_activity_notifier_ =
+      base::MakeUnique<ui::UserActivityPowerManagerNotifier>(
+          user_activity_detector_.get());
 #endif
+
+  // Create the root window, then set it up as our primary window.
+  CreateRootWindowController();
+  FinalizeWindowManager();
 }
 
-void ShellDesktopControllerAura::CreateRootWindow() {
-  // Set up basic pieces of ui::wm.
+void ShellDesktopControllerAura::CreateRootWindowController() {
+  // TODO(michaelpg): Support creating a host for a secondary display.
+  root_window_controller_ = base::MakeUnique<RootWindowController>(
+      this, screen_.get(),
+      gfx::Rect(screen_->GetPrimaryDisplay().GetSizeInPixel()),
+      browser_context_);
+
+  // Initialize the root window with our clients.
+  aura::Window* root_window = root_window_controller_->host()->window();
+  root_window->AddPreTargetHandler(root_window_event_filter_.get());
+  aura::client::SetFocusClient(root_window, focus_controller_.get());
+  root_window->AddPreTargetHandler(focus_controller_.get());
+  wm::SetActivationClient(root_window, focus_controller_.get());
+  aura::client::SetCursorClient(root_window, cursor_manager_.get());
+}
+
+void ShellDesktopControllerAura::FinalizeWindowManager() {
+  // Create an input method and become its delegate.
+  input_method_ =
+      ui::CreateInputMethod(this, GetPrimaryHost()->GetAcceleratedWidget());
+
+  cursor_manager_->SetDisplay(screen_->GetPrimaryDisplay());
+  cursor_manager_->SetCursor(ui::CursorType::kPointer);
+
+  // TODO(michaelpg): Replace with a capture client supporting multiple
+  // WindowTreeHosts.
+  capture_client_ = base::MakeUnique<aura::client::DefaultCaptureClient>(
+      GetPrimaryHost()->window());
+}
+
+void ShellDesktopControllerAura::TearDownWindowManager() {
+  GetPrimaryHost()->window()->RemovePreTargetHandler(
+      root_window_event_filter_.get());
+  root_window_event_filter_.reset();
+
+  // The DefaultCaptureClient must be destroyed before the root window.
+  capture_client_.reset();
+
+  GetPrimaryHost()->window()->RemovePreTargetHandler(focus_controller_.get());
+  wm::SetActivationClient(GetPrimaryHost()->window(), nullptr);
+  root_window_controller_.reset();
+
+  focus_controller_.reset();
+  cursor_manager_.reset();
+#if defined(OS_CHROMEOS)
+  user_activity_notifier_.reset();
+#endif
+  user_activity_detector_.reset();
+  display::Screen::SetScreenInstance(nullptr);
+  screen_.reset();
+}
+
+gfx::Size ShellDesktopControllerAura::GetStartingWindowSize() {
   gfx::Size size;
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kAppShellHostWindowSize)) {
@@ -331,49 +319,7 @@
   }
   if (size.IsEmpty())
     size = gfx::Size(1920, 1080);
-
-  screen_.reset(new ShellScreen(size));
-  display::Screen::SetScreenInstance(screen_.get());
-
-  host_.reset(screen_->CreateHostForPrimaryDisplay());
-  aura::client::SetWindowParentingClient(host_->window(), this);
-  root_window_event_filter_.reset(new wm::CompoundEventFilter);
-  host_->window()->AddPreTargetHandler(root_window_event_filter_.get());
-
-  // Trigger creation of an input method and become its delegate.
-  ui::InputMethod* input_method = host_->GetInputMethod();
-  input_method->SetDelegate(this);
-
-  InitWindowManager();
-
-  host_->AddObserver(this);
-
-  // Ensure the X window gets mapped.
-  host_->Show();
-}
-
-void ShellDesktopControllerAura::DestroyRootWindow() {
-  host_->RemoveObserver(this);
-  wm::FocusController* focus_controller =
-      static_cast<wm::FocusController*>(focus_client_.get());
-  if (focus_controller) {
-    host_->window()->RemovePreTargetHandler(focus_controller);
-    wm::SetActivationClient(host_->window(), NULL);
-  }
-
-  host_->window()->RemovePreTargetHandler(root_window_event_filter_.get());
-  root_window_event_filter_.reset();
-
-  capture_client_.reset();
-  focus_client_.reset();
-  cursor_manager_.reset();
-#if defined(OS_CHROMEOS)
-  user_activity_notifier_.reset();
-#endif
-  user_activity_detector_.reset();
-  host_.reset();
-  display::Screen::SetScreenInstance(nullptr);
-  screen_.reset();
+  return size;
 }
 
 gfx::Size ShellDesktopControllerAura::GetPrimaryDisplaySize() {
diff --git a/extensions/shell/browser/shell_desktop_controller_aura.h b/extensions/shell/browser/shell_desktop_controller_aura.h
index 4dae572..d18a1b4 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura.h
+++ b/extensions/shell/browser/shell_desktop_controller_aura.h
@@ -5,16 +5,13 @@
 #ifndef EXTENSIONS_SHELL_BROWSER_SHELL_DESKTOP_CONTROLLER_AURA_H_
 #define EXTENSIONS_SHELL_BROWSER_SHELL_DESKTOP_CONTROLLER_AURA_H_
 
-#include <list>
 #include <memory>
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/shell/browser/desktop_controller.h"
-#include "ui/aura/client/window_parenting_client.h"
-#include "ui/aura/window_tree_host_observer.h"
+#include "extensions/shell/browser/root_window_controller.h"
 #include "ui/base/ime/input_method_delegate.h"
 
 #if defined(OS_CHROMEOS)
@@ -23,57 +20,56 @@
 #endif
 
 namespace aura {
-class Window;
 class WindowTreeHost;
 namespace client {
 class DefaultCaptureClient;
-class FocusClient;
-}
-}
+}  // namespace client
+}  // namespace aura
 
 namespace base {
 class RunLoop;
-}
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}  // namespace content
 
 namespace gfx {
 class Size;
-}
+}  // namespace gfx
 
 namespace ui {
+class InputMethod;
 class UserActivityDetector;
 #if defined(OS_CHROMEOS)
 class UserActivityPowerManagerNotifier;
 #endif
-}
+}  // namespace ui
 
 namespace wm {
 class CompoundEventFilter;
 class CursorManager;
-}
+class FocusController;
+}  // namespace wm
 
 namespace extensions {
 class AppWindowClient;
-class Extension;
 class ShellScreen;
 
 // Simple desktop controller for app_shell. Sets up a root Aura window for the
 // primary display.
 class ShellDesktopControllerAura
     : public DesktopController,
-      public AppWindowRegistry::Observer,
-      public aura::client::WindowParentingClient,
+      public RootWindowController::DesktopDelegate,
 #if defined(OS_CHROMEOS)
       public chromeos::PowerManagerClient::Observer,
       public display::DisplayConfigurator::Observer,
 #endif
-      public aura::WindowTreeHostObserver,
       public ui::internal::InputMethodDelegate {
  public:
-  ShellDesktopControllerAura();
+  explicit ShellDesktopControllerAura(content::BrowserContext* browser_context);
   ~ShellDesktopControllerAura() override;
 
-  aura::WindowTreeHost* host() { return host_.get(); }
-
   // DesktopController:
   void Run() override;
   AppWindow* CreateAppWindow(content::BrowserContext* context,
@@ -81,12 +77,9 @@
   void AddAppWindow(gfx::NativeWindow window) override;
   void CloseAppWindows() override;
 
-  // AppWindowRegistry::Observer overrides:
-  void OnAppWindowRemoved(AppWindow* app_window) override;
-
-  // aura::client::WindowParentingClient overrides:
-  aura::Window* GetDefaultParent(aura::Window* window,
-                                 const gfx::Rect& bounds) override;
+  // RootWindowController::DesktopDelegate:
+  void CloseRootWindowController(
+      RootWindowController* root_window_controller) override;
 
 #if defined(OS_CHROMEOS)
   // chromeos::PowerManagerClient::Observer overrides:
@@ -98,42 +91,58 @@
       const display::DisplayConfigurator::DisplayStateList& displays) override;
 #endif
 
-  // aura::WindowTreeHostObserver overrides:
-  void OnHostCloseRequested(aura::WindowTreeHost* host) override;
-
   // ui::internal::InputMethodDelegate overrides:
   ui::EventDispatchDetails DispatchKeyEventPostIME(
       ui::KeyEvent* key_event) override;
 
+  // Returns the RootWindowController's WindowTreeHost.
+  aura::WindowTreeHost* GetPrimaryHost();
+
+  RootWindowController* root_window_controller() {
+    return root_window_controller_.get();
+  }
+
  protected:
   // Creates and sets the aura clients and window manager stuff. Subclass may
   // initialize different sets of the clients.
   virtual void InitWindowManager();
 
- private:
-  // Creates the window that hosts the app.
-  void CreateRootWindow();
+  // Tears down the window manager stuff set up in InitWindowManager().
+  virtual void TearDownWindowManager();
 
-  // Closes and destroys the root window hosting the app.
-  void DestroyRootWindow();
+ private:
+  // Creates the RootWindowController that hosts the app.
+  void CreateRootWindowController();
+
+  // Finishes set-up using the RootWindowController.
+  void FinalizeWindowManager();
+
+  // Returns the desired dimensions of the RootWindowController from the command
+  // line, or falls back to a default size.
+  gfx::Size GetStartingWindowSize();
 
   // Returns the dimensions (in pixels) of the primary display, or an empty size
   // if the dimensions can't be determined or no display is connected.
   gfx::Size GetPrimaryDisplaySize();
 
+  content::BrowserContext* const browser_context_;
+
 #if defined(OS_CHROMEOS)
   std::unique_ptr<display::DisplayConfigurator> display_configurator_;
 #endif
 
   std::unique_ptr<ShellScreen> screen_;
 
-  std::unique_ptr<aura::WindowTreeHost> host_;
-
   std::unique_ptr<wm::CompoundEventFilter> root_window_event_filter_;
 
+  // TODO(michaelpg): Support one RootWindowController per display.
+  std::unique_ptr<RootWindowController> root_window_controller_;
+
+  std::unique_ptr<ui::InputMethod> input_method_;
+
   std::unique_ptr<aura::client::DefaultCaptureClient> capture_client_;
 
-  std::unique_ptr<aura::client::FocusClient> focus_client_;
+  std::unique_ptr<wm::FocusController> focus_controller_;
 
   std::unique_ptr<wm::CursorManager> cursor_manager_;
 
diff --git a/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc b/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc
index 4590aff8..af55714b 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc
@@ -56,12 +56,7 @@
 #endif
 
     ShellTestBaseAura::SetUp();
-
-    // The input method will be used for the next CreateInputMethod call,
-    // causing the host to take ownership.
-    ui::SetUpInputMethodForTesting(new ui::InputMethodMinimal(nullptr));
-
-    controller_.reset(new ShellDesktopControllerAura());
+    controller_.reset(new ShellDesktopControllerAura(browser_context()));
   }
 
   void TearDown() override {
@@ -73,30 +68,6 @@
   }
 
  protected:
-  // Creates an AppWindow, which should delete itself when closed.
-  AppWindow* CreateAppWindow(Extension* extension) {
-    AppWindow* app_window =
-        controller_->CreateAppWindow(browser_context(), extension);
-
-    std::unique_ptr<content::WebContents> web_contents(
-        content::WebContents::Create(
-            content::WebContents::CreateParams(browser_context())));
-    std::unique_ptr<TestAppWindowContents> app_window_contents =
-        base::MakeUnique<TestAppWindowContents>(std::move(web_contents));
-
-    // Init the ShellExtensionsWebContentsObserver.
-    app_window->app_delegate()->InitWebContents(
-        app_window_contents->GetWebContents());
-
-    content::RenderFrameHost* main_frame =
-        app_window_contents->GetWebContents()->GetMainFrame();
-    EXPECT_TRUE(main_frame);
-
-    app_window->Init(GURL(std::string()), app_window_contents.release(),
-                     main_frame, AppWindow::CreateParams());
-    return app_window;
-  }
-
   std::unique_ptr<ShellDesktopControllerAura> controller_;
 
 #if defined(OS_CHROMEOS)
@@ -126,7 +97,8 @@
 // Tests that basic input events are handled and forwarded to the host.
 // TODO(michaelpg): Test other types of input.
 TEST_F(ShellDesktopControllerAuraTest, InputEvents) {
-  ui::InputMethod* input_method = controller_->host()->GetInputMethod();
+  ui::InputMethod* input_method =
+      controller_->GetPrimaryHost()->GetInputMethod();
   ASSERT_TRUE(input_method);
 
   // Set up a focused text input to receive the keypress event.
@@ -137,8 +109,8 @@
   // Dispatch a keypress on the window tree host to verify it is processed.
   ui::KeyEvent key_press(base::char16(97), ui::VKEY_A, ui::EF_NONE);
   ui::EventDispatchDetails details =
-      controller_->host()->dispatcher()->DispatchEvent(
-          controller_->host()->window(), &key_press);
+      controller_->GetPrimaryHost()->dispatcher()->DispatchEvent(
+          controller_->GetPrimaryHost()->window(), &key_press);
   EXPECT_FALSE(details.dispatcher_destroyed);
   EXPECT_FALSE(details.target_destroyed);
   EXPECT_TRUE(key_press.handled());
@@ -148,58 +120,35 @@
   input_method->DetachTextInputClient(&client);
 }
 
-// Tests the basic window layout.
-TEST_F(ShellDesktopControllerAuraTest, FillLayout) {
-  controller_->host()->SetBoundsInPixels(gfx::Rect(0, 0, 500, 700));
-
-  scoped_refptr<Extension> extension = test_util::CreateEmptyExtension();
-  CreateAppWindow(extension.get());
-
-  aura::Window* root_window = controller_->host()->window();
-  EXPECT_EQ(1u, root_window->children().size());
-
-  // Test that reshaping the host window also resizes the child window.
-  controller_->host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
-
-  EXPECT_EQ(400, root_window->bounds().width());
-  EXPECT_EQ(400, root_window->children()[0]->bounds().width());
-
-  // The AppWindow will close on shutdown.
-}
-
-// Tests that the AppWindows are removed when closed.
-TEST_F(ShellDesktopControllerAuraTest, OnAppWindowClose) {
-  scoped_refptr<Extension> extension = test_util::CreateEmptyExtension();
-  AppWindow* app_window1 = CreateAppWindow(extension.get());
-
-  aura::Window* root_window = controller_->host()->window();
-  EXPECT_EQ(1u, root_window->children().size());
-
-  AppWindow* app_window2 = CreateAppWindow(extension.get());
-  EXPECT_EQ(2u, root_window->children().size());
-
-  app_window1->GetBaseWindow()->Close();
-  app_window1 = nullptr;  // Close() deletes the AppWindow.
-  // The second window is still open.
-  EXPECT_EQ(1u, root_window->children().size());
-
-  app_window2->GetBaseWindow()->Close();
-  app_window2 = nullptr;  // Close() deletes the AppWindow.
-  EXPECT_EQ(0u, root_window->children().size());
-}
-
 // Tests closing all AppWindows.
 TEST_F(ShellDesktopControllerAuraTest, CloseAppWindows) {
+  const AppWindowRegistry* app_window_registry =
+      AppWindowRegistry::Get(browser_context());
   scoped_refptr<Extension> extension = test_util::CreateEmptyExtension();
-  CreateAppWindow(extension.get());
-  CreateAppWindow(extension.get());
-  CreateAppWindow(extension.get());
-
-  aura::Window* root_window = controller_->host()->window();
-  EXPECT_EQ(3u, root_window->children().size());
+  for (int i = 0; i < 3; i++) {
+    InitAppWindow(
+        controller_->CreateAppWindow(browser_context(), extension.get()));
+  }
+  EXPECT_EQ(3u, app_window_registry->app_windows().size());
 
   controller_->CloseAppWindows();
-  EXPECT_EQ(0u, root_window->children().size());
+  EXPECT_EQ(0u, app_window_registry->app_windows().size());
+}
+
+// Tests that the AppWindows are removed when the desktop controller goes away.
+TEST_F(ShellDesktopControllerAuraTest, OnAppWindowClose) {
+  const AppWindowRegistry* app_window_registry =
+      AppWindowRegistry::Get(browser_context());
+  scoped_refptr<Extension> extension = test_util::CreateEmptyExtension();
+  for (int i = 0; i < 3; i++) {
+    InitAppWindow(
+        controller_->CreateAppWindow(browser_context(), extension.get()));
+  }
+  EXPECT_EQ(3u, app_window_registry->app_windows().size());
+
+  // Deleting the controller closes all app windows.
+  controller_.reset();
+  EXPECT_EQ(0u, app_window_registry->app_windows().size());
 }
 
 }  // namespace extensions
diff --git a/extensions/shell/browser/shell_extensions_api_client.cc b/extensions/shell/browser/shell_extensions_api_client.cc
index d1fa28e..2471ef4 100644
--- a/extensions/shell/browser/shell_extensions_api_client.cc
+++ b/extensions/shell/browser/shell_extensions_api_client.cc
@@ -51,4 +51,10 @@
   return messaging_delegate_.get();
 }
 
+FeedbackPrivateDelegate*
+ShellExtensionsAPIClient::GetFeedbackPrivateDelegate() {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
 }  // namespace extensions
diff --git a/extensions/shell/browser/shell_extensions_api_client.h b/extensions/shell/browser/shell_extensions_api_client.h
index 9db406fe..66154af 100644
--- a/extensions/shell/browser/shell_extensions_api_client.h
+++ b/extensions/shell/browser/shell_extensions_api_client.h
@@ -9,6 +9,7 @@
 
 #include "extensions/browser/api/extensions_api_client.h"
 
+#include "base/macros.h"
 #include "build/build_config.h"
 
 namespace extensions {
@@ -31,6 +32,7 @@
   FileSystemDelegate* GetFileSystemDelegate() override;
 #endif
   MessagingDelegate* GetMessagingDelegate() override;
+  FeedbackPrivateDelegate* GetFeedbackPrivateDelegate() override;
 
  private:
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
diff --git a/extensions/shell/browser/shell_native_app_window.cc b/extensions/shell/browser/shell_native_app_window.cc
index 02c137d..676e20a 100644
--- a/extensions/shell/browser/shell_native_app_window.cc
+++ b/extensions/shell/browser/shell_native_app_window.cc
@@ -131,7 +131,7 @@
   return NULL;
 }
 
-void ShellNativeAppWindow::UpdateShape(std::unique_ptr<SkRegion> region) {
+void ShellNativeAppWindow::UpdateShape(std::unique_ptr<ShapeRects> rects) {
   NOTIMPLEMENTED();
 }
 
diff --git a/extensions/shell/browser/shell_native_app_window.h b/extensions/shell/browser/shell_native_app_window.h
index fcc3f6e..a282a41 100644
--- a/extensions/shell/browser/shell_native_app_window.h
+++ b/extensions/shell/browser/shell_native_app_window.h
@@ -52,7 +52,7 @@
   void UpdateDraggableRegions(
       const std::vector<DraggableRegion>& regions) override;
   SkRegion* GetDraggableRegion() override;
-  void UpdateShape(std::unique_ptr<SkRegion> region) override;
+  void UpdateShape(std::unique_ptr<ShapeRects> rects) override;
   void HandleKeyboardEvent(
       const content::NativeWebKeyboardEvent& event) override;
   bool IsFrameless() const override;
diff --git a/extensions/shell/browser/shell_screen.cc b/extensions/shell/browser/shell_screen.cc
index 3881f0d0..1be0067c 100644
--- a/extensions/shell/browser/shell_screen.cc
+++ b/extensions/shell/browser/shell_screen.cc
@@ -8,9 +8,12 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "extensions/shell/browser/root_window_controller.h"
+#include "extensions/shell/browser/shell_desktop_controller_aura.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/display/display.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
@@ -22,8 +25,11 @@
 
 }  // namespace
 
-ShellScreen::ShellScreen(const gfx::Size& size) : host_(nullptr) {
+ShellScreen::ShellScreen(ShellDesktopControllerAura* desktop_controller,
+                         const gfx::Size& size)
+    : desktop_controller_(desktop_controller) {
   DCHECK(!size.IsEmpty());
+
   // Screen is positioned at (0,0).
   display::Display display(kDisplayId);
   gfx::Rect bounds(size);
@@ -32,38 +38,17 @@
 }
 
 ShellScreen::~ShellScreen() {
-  DCHECK(!host_) << "Window not closed before destroying ShellScreen";
+  DCHECK(!desktop_controller_ || !desktop_controller_->root_window_controller())
+      << "WindowTreeHost not closed before destroying ShellScreen";
 }
 
-aura::WindowTreeHost* ShellScreen::CreateHostForPrimaryDisplay() {
-  DCHECK(!host_);
-  host_ = aura::WindowTreeHost::Create(
-      gfx::Rect(GetPrimaryDisplay().GetSizeInPixel()));
-  host_->window()->AddObserver(this);
-  host_->InitHost();
-  host_->window()->Show();
-  return host_;
-}
-
-// aura::WindowObserver overrides:
-
-void ShellScreen::OnWindowBoundsChanged(aura::Window* window,
-                                        const gfx::Rect& old_bounds,
-                                        const gfx::Rect& new_bounds) {
-  DCHECK_EQ(host_->window(), window);
-  display::Display display(GetPrimaryDisplay());
-  display.SetSize(new_bounds.size());
+void ShellScreen::OnHostResized(aura::WindowTreeHost* host) {
+  // Based on ash::WindowTreeHostManager.
+  display::Display display = GetDisplayNearestWindow(host->window());
+  display.SetSize(host->GetBoundsInPixels().size());
   display_list().UpdateDisplay(display);
 }
 
-void ShellScreen::OnWindowDestroying(aura::Window* window) {
-  DCHECK_EQ(host_->window(), window);
-  host_->window()->RemoveObserver(this);
-  host_ = nullptr;
-}
-
-// display::Screen overrides:
-
 gfx::Point ShellScreen::GetCursorScreenPoint() {
   return aura::Env::GetInstance()->last_mouse_location();
 }
@@ -73,7 +58,9 @@
 }
 
 gfx::NativeWindow ShellScreen::GetWindowAtScreenPoint(const gfx::Point& point) {
-  return host_->window()->GetEventHandlerForPoint(point);
+  return desktop_controller_->GetPrimaryHost()
+      ->window()
+      ->GetEventHandlerForPoint(point);
 }
 
 display::Display ShellScreen::GetDisplayNearestWindow(
diff --git a/extensions/shell/browser/shell_screen.h b/extensions/shell/browser/shell_screen.h
index 9746212..6d4cb55 100644
--- a/extensions/shell/browser/shell_screen.h
+++ b/extensions/shell/browser/shell_screen.h
@@ -6,7 +6,7 @@
 #define EXTENSIONS_SHELL_BROWSER_SHELL_SCREEN_H_
 
 #include "base/macros.h"
-#include "ui/aura/window_observer.h"
+#include "ui/aura/window_tree_host_observer.h"
 #include "ui/display/display.h"
 #include "ui/display/screen_base.h"
 
@@ -19,23 +19,21 @@
 }
 
 namespace extensions {
+class ShellDesktopControllerAura;
 
 // A minimal Aura implementation of a screen. Scale factor is locked at 1.0.
 // When running on a Linux desktop resizing the main window resizes the screen.
-class ShellScreen : public display::ScreenBase, public aura::WindowObserver {
+class ShellScreen : public display::ScreenBase,
+                    public aura::WindowTreeHostObserver {
  public:
-  // Creates a screen occupying |size| physical pixels.
-  explicit ShellScreen(const gfx::Size& size);
+  // Creates a screen occupying |size| physical pixels. |desktop_controller|
+  // can be null in tests.
+  ShellScreen(ShellDesktopControllerAura* desktop_controller,
+              const gfx::Size& size);
   ~ShellScreen() override;
 
-  // Caller owns the returned object.
-  aura::WindowTreeHost* CreateHostForPrimaryDisplay();
-
-  // WindowObserver overrides:
-  void OnWindowBoundsChanged(aura::Window* window,
-                             const gfx::Rect& old_bounds,
-                             const gfx::Rect& new_bounds) override;
-  void OnWindowDestroying(aura::Window* window) override;
+  // aura::WindowTreeHostObserver overrides:
+  void OnHostResized(aura::WindowTreeHost* host) override;
 
   // display::Screen overrides:
   gfx::Point GetCursorScreenPoint() override;
@@ -45,7 +43,7 @@
       gfx::NativeWindow window) const override;
 
  private:
-  aura::WindowTreeHost* host_;  // Not owned.
+  ShellDesktopControllerAura* const desktop_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellScreen);
 };
diff --git a/extensions/shell/browser/shell_screen_unittest.cc b/extensions/shell/browser/shell_screen_unittest.cc
index 75a5cd16..b3f31bd 100644
--- a/extensions/shell/browser/shell_screen_unittest.cc
+++ b/extensions/shell/browser/shell_screen_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "extensions/shell/test/shell_test_base_aura.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/display/display.h"
@@ -16,11 +16,11 @@
 
 namespace extensions {
 
-using ShellScreenTest = aura::test::AuraTestBase;
+using ShellScreenTest = ShellTestBaseAura;
 
 // Basic sanity tests for ShellScreen.
 TEST_F(ShellScreenTest, ShellScreen) {
-  ShellScreen screen(gfx::Size(640, 480));
+  ShellScreen screen(nullptr, gfx::Size(640, 480));
 
   // There is only one display.
   EXPECT_EQ(1, screen.GetNumDisplays());
@@ -30,13 +30,16 @@
   EXPECT_EQ("640x480", screen.GetPrimaryDisplay().size().ToString());
 
   // Tests that reshaping the host window reshapes the display.
-  // NOTE: AuraTestBase already has its own WindowTreeHost. This is creating a
-  // second one.
-  std::unique_ptr<aura::WindowTreeHost> host(
-      screen.CreateHostForPrimaryDisplay());
+  std::unique_ptr<aura::WindowTreeHost> host(aura::WindowTreeHost::Create(
+      gfx::Rect(screen.GetPrimaryDisplay().GetSizeInPixel())));
+  host->AddObserver(&screen);
+  host->InitHost();
   EXPECT_TRUE(host->window());
-  host->window()->SetBounds(gfx::Rect(0, 0, 800, 600));
+
+  host->SetBoundsInPixels(gfx::Rect(0, 0, 800, 600));
   EXPECT_EQ("800x600", screen.GetPrimaryDisplay().size().ToString());
+
+  host->RemoveObserver(&screen);
 }
 
 }  // namespace extensions
diff --git a/extensions/shell/test/shell_test_base_aura.cc b/extensions/shell/test/shell_test_base_aura.cc
index 20912c5..74e033a 100644
--- a/extensions/shell/test/shell_test_base_aura.cc
+++ b/extensions/shell/test/shell_test_base_aura.cc
@@ -5,9 +5,14 @@
 #include "extensions/shell/test/shell_test_base_aura.h"
 
 #include "base/memory/ptr_util.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/browser/app_window/app_window.h"
+#include "extensions/browser/app_window/test_app_window_contents.h"
+#include "extensions/shell/browser/shell_app_delegate.h"
 #include "extensions/shell/test/shell_test_extensions_browser_client.h"
 #include "extensions/shell/test/shell_test_helper_aura.h"
+#include "url/gurl.h"
 
 namespace extensions {
 
@@ -30,4 +35,25 @@
   helper_->TearDown();
 }
 
+void ShellTestBaseAura::InitAppWindow(AppWindow* app_window) {
+  // Create a TestAppWindowContents for the ShellAppDelegate to initialize the
+  // ShellExtensionWebContentsObserver with.
+  std::unique_ptr<content::WebContents> web_contents(
+      content::WebContents::Create(
+          content::WebContents::CreateParams(browser_context())));
+  std::unique_ptr<TestAppWindowContents> app_window_contents =
+      base::MakeUnique<TestAppWindowContents>(std::move(web_contents));
+
+  // Initialize the web contents and AppWindow.
+  app_window->app_delegate()->InitWebContents(
+      app_window_contents->GetWebContents());
+
+  content::RenderFrameHost* main_frame =
+      app_window_contents->GetWebContents()->GetMainFrame();
+  DCHECK(main_frame);
+
+  app_window->Init(GURL(), app_window_contents.release(), main_frame,
+                   AppWindow::CreateParams());
+}
+
 }  // namespace extensions
diff --git a/extensions/shell/test/shell_test_base_aura.h b/extensions/shell/test/shell_test_base_aura.h
index 65d7ef2..5e9d8e6 100644
--- a/extensions/shell/test/shell_test_base_aura.h
+++ b/extensions/shell/test/shell_test_base_aura.h
@@ -12,6 +12,7 @@
 #include "extensions/browser/extensions_test.h"
 
 namespace extensions {
+class AppWindow;
 class ShellTestHelperAura;
 
 class ShellTestBaseAura : public ExtensionsTest {
@@ -23,6 +24,9 @@
   void SetUp() override;
   void TearDown() override;
 
+  // Initializes |app_window| for testing.
+  void InitAppWindow(AppWindow* app_window);
+
  private:
   std::unique_ptr<ShellTestHelperAura> helper_;
 
diff --git a/extensions/utility/utility_handler.cc b/extensions/utility/utility_handler.cc
index 250330f..320e1fba 100644
--- a/extensions/utility/utility_handler.cc
+++ b/extensions/utility/utility_handler.cc
@@ -4,8 +4,14 @@
 
 #include "extensions/utility/utility_handler.h"
 
+#include <memory>
+#include <string>
+#include <utility>
+
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/task_scheduler/post_task.h"
 #include "content/public/utility/utility_thread.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_l10n_util.h"
@@ -27,6 +33,33 @@
 
 namespace {
 
+struct UnpackResult {
+  std::unique_ptr<base::DictionaryValue> parsed_manifest;
+  base::string16 error;
+};
+
+// Unpacks the extension on background task runner.
+// On success, returns UnpackResult with |parsed_manifest| set to the parsed
+// extension manifest.
+// On failure returns UnpackResult with |error| set to the encountered error
+// message.
+UnpackResult UnpackOnBackgroundTaskRunner(const base::FilePath& path,
+                                          const std::string& extension_id,
+                                          Manifest::Location location,
+                                          int32_t creation_flags) {
+  Unpacker unpacker(path.DirName(), path, extension_id, location,
+                    creation_flags);
+
+  UnpackResult result;
+  if (unpacker.Run()) {
+    result.parsed_manifest = unpacker.TakeParsedManifest();
+  } else {
+    result.error = unpacker.error_message();
+  }
+
+  return result;
+}
+
 class ExtensionUnpackerImpl : public extensions::mojom::ExtensionUnpacker {
  public:
   ExtensionUnpackerImpl() = default;
@@ -42,13 +75,19 @@
   void Unzip(const base::FilePath& file,
              const base::FilePath& path,
              UnzipCallback callback) override {
-    std::unique_ptr<base::DictionaryValue> manifest;
-    if (UnzipFileManifestIntoPath(file, path, &manifest)) {
-      std::move(callback).Run(
-          UnzipFileIntoPath(file, path, std::move(manifest)));
-    } else {
-      std::move(callback).Run(false);
-    }
+    // Move unzip operation to background thread to avoid blocking the main
+    // utility thread for extended amont of time. For example, this prevents
+    // extension unzipping block receipt of the connection complete
+    // notification for the utility process channel to the browser process,
+    // which could cause the utility process to terminate itself due to browser
+    // process being considered unreachable.
+    base::PostTaskWithTraitsAndReplyWithResult(
+        FROM_HERE,
+        {base::TaskPriority::USER_BLOCKING, base::MayBlock(),
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce(&ExtensionUnpackerImpl::UnzipOnBackgroundTaskRunner,
+                       file, path),
+        std::move(callback));
   }
 
   void Unpack(version_info::Channel channel,
@@ -68,13 +107,20 @@
     SetCurrentChannel(channel);
     SetCurrentFeatureSessionType(type);
 
-    Unpacker unpacker(path.DirName(), path, extension_id, location,
-                      creation_flags);
-    if (unpacker.Run()) {
-      std::move(callback).Run(base::string16(), unpacker.TakeParsedManifest());
-    } else {
-      std::move(callback).Run(unpacker.error_message(), nullptr);
-    }
+    // Move unpack operation to background thread to prevent it from blocking
+    // the utility process thread for extended amount of time.
+    base::PostTaskWithTraitsAndReplyWithResult(
+        FROM_HERE,
+        {base::TaskPriority::USER_BLOCKING, base::MayBlock(),
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce(&UnpackOnBackgroundTaskRunner, path, extension_id,
+                       location, creation_flags),
+        base::BindOnce(
+            [](UnpackCallback callback, UnpackResult result) {
+              std::move(callback).Run(result.error,
+                                      std::move(result.parsed_manifest));
+            },
+            std::move(callback)));
   }
 
   static bool UnzipFileManifestIntoPath(
@@ -104,6 +150,17 @@
         true /* log_skipped_files */);
   }
 
+  // Unzips the extension from |file| to |path|.
+  // Returns whether the unzip operation succeeded.
+  static bool UnzipOnBackgroundTaskRunner(const base::FilePath& file,
+                                          const base::FilePath& path) {
+    std::unique_ptr<base::DictionaryValue> manifest;
+    if (!UnzipFileManifestIntoPath(file, path, &manifest))
+      return false;
+
+    return UnzipFileIntoPath(file, path, std::move(manifest));
+  }
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionUnpackerImpl);
 };
 
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 91987a4..d2a644d 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -223,7 +223,7 @@
   extra_deps = [ ":gen_devtools_client_api" ]
 }
 
-if (headless_fontconfig_utils) {
+if (headless_fontconfig_utils && !is_fuchsia) {
   static_library("headless_fontconfig_utils") {
     sources = [
       "public/util/fontconfig.cc",
@@ -280,8 +280,6 @@
     "lib/browser/headless_window_tree_host.h",
     "lib/headless_content_client.cc",
     "lib/headless_content_client.h",
-    "lib/headless_crash_reporter_client.cc",
-    "lib/headless_crash_reporter_client.h",
     "public/headless_browser.cc",
     "public/headless_browser.h",
     "public/headless_browser_context.h",
@@ -323,6 +321,13 @@
     "public/util/user_agent.h",
   ]
 
+  if (!is_fuchsia) {
+    sources += [
+      "lib/headless_crash_reporter_client.cc",
+      "lib/headless_crash_reporter_client.h",
+    ]
+  }
+
   sources += generated_devtools_api
 
   if (use_aura) {
@@ -391,11 +396,14 @@
     ]
 
     deps += [
-      "//components/crash/content/browser",
       "//components/security_state/content",
       "//third_party/WebKit/public:blink_headers",
     ]
 
+    if (!is_fuchsia) {
+      deps += [ "//components/crash/content/browser" ]
+    }
+
     if (enable_basic_printing) {
       deps += [
         "//components/printing/browser",
@@ -404,13 +412,6 @@
     }
   }
 
-  if (is_fuchsia) {
-    sources -= [
-      "lib/headless_crash_reporter_client.cc",
-      "lib/headless_crash_reporter_client.h",
-    ]
-  }
-
   if (is_mac) {
     deps += [ ":mac_helpers" ]
   } else {
@@ -431,7 +432,7 @@
     deps += [ "//ui/ozone" ]
   }
 
-  if (headless_fontconfig_utils) {
+  if (headless_fontconfig_utils && !is_fuchsia) {
     deps += [ ":headless_fontconfig_utils" ]
   }
 
@@ -510,7 +511,6 @@
     ":headless_renderer",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
-    "//components/crash/content/browser",
     "//components/security_state/content",
     "//content/public/app:both",
     "//content/public/child:child",
@@ -519,14 +519,14 @@
     "//testing/gtest",
   ]
 
+  if (!is_fuchsia) {
+    deps += [ "//components/crash/content/browser" ]
+  }
+
   if (enable_basic_printing) {
     sources += [ "lib/browser/headless_printing_unittest.cc" ]
     deps += [ "//components/printing/browser" ]
   }
-
-  if (is_fuchsia) {
-    deps -= [ "//components/crash/content/browser" ]
-  }
 }
 
 if (is_mac) {
@@ -655,13 +655,16 @@
   deps = [
     ":headless_renderer",
     "//base",
-    "//components/crash/content/browser",
     "//components/security_state/content",
     "//content/test:test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
 
+  if (!is_fuchsia) {
+    deps += [ "//components/crash/content/browser" ]
+  }
+
   # Only include this if we built the js_binary
   if (is_linux) {
     data += [ "$root_out_dir/headless_browser_tests.pak" ]
@@ -675,10 +678,6 @@
       "//pdf",
     ]
   }
-
-  if (is_fuchsia) {
-    deps -= [ "//components/crash/content/browser" ]
-  }
 }
 
 if (is_win) {
@@ -774,7 +773,6 @@
 
   deps = [
     ":headless_renderer",
-    "//components/crash/content/browser",
     "//components/security_state/content",
     "//content/public/app:both",
     "//content/public/browser",
@@ -782,6 +780,10 @@
     "//content/public/common",
   ]
 
+  if (!is_fuchsia) {
+    deps += [ "//components/crash/content/browser" ]
+  }
+
   if (enable_basic_printing) {
     deps += [
       "//components/printing/browser",
@@ -798,10 +800,6 @@
       "//sandbox",
     ]
   }
-
-  if (is_fuchsia) {
-    deps -= [ "//components/crash/content/browser" ]
-  }
 }
 
 if (is_fuchsia) {
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
index eec7f9105..70f103d 100644
--- a/infra/config/cq.cfg
+++ b/infra/config/cq.cfg
@@ -50,7 +50,7 @@
       builders { name: "chromium_presubmit" }
       builders {
         name: "fuchsia_compile"
-        experiment_percentage: 10
+        experiment_percentage: 100
       }
       builders { name: "linux_chromium_asan_rel_ng" }
       builders { name: "linux_chromium_chromeos_rel_ng" }
diff --git a/ios/OWNERS b/ios/OWNERS
index a5fae73..839f864 100644
--- a/ios/OWNERS
+++ b/ios/OWNERS
@@ -11,3 +11,6 @@
 # These are for the common case of adding or renaming files. If you're doing
 # structural changes, please get a review from a reviewer in this file.
 per-file BUILD.gn=*
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/build/OWNERS b/ios/build/OWNERS
index ae82009..ba9c65db 100644
--- a/ios/build/OWNERS
+++ b/ios/build/OWNERS
@@ -1,2 +1,5 @@
 rohitrao@chromium.org
 sdefresne@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/build/bots/OWNERS b/ios/build/bots/OWNERS
index 7466a7b..bab98631 100644
--- a/ios/build/bots/OWNERS
+++ b/ios/build/bots/OWNERS
@@ -3,3 +3,6 @@
 rohitrao@chromium.org
 sdefresne@chromium.org
 smut@google.com
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/build/bots/chromium.fyi/ClangToTiOS.json b/ios/build/bots/chromium.fyi/ClangToTiOS.json
index 2b5ab01..d8b662f8 100644
--- a/ios/build/bots/chromium.fyi/ClangToTiOS.json
+++ b/ios/build/bots/chromium.fyi/ClangToTiOS.json
@@ -8,6 +8,7 @@
     "is_component_build=false",
     "is_debug=false",
     "llvm_force_head_revision=true",
+    "additional_target_cpus=[\"arm64\",\"x64\",\"x86\"]",
     "target_cpu=\"arm\"",
     "target_os=\"ios\""
   ],
diff --git a/ios/build/bots/scripts/OWNERS b/ios/build/bots/scripts/OWNERS
index 1faa6041..eacf766 100644
--- a/ios/build/bots/scripts/OWNERS
+++ b/ios/build/bots/scripts/OWNERS
@@ -2,3 +2,6 @@
 baxley@chromium.org
 sergeyberezin@chromium.org
 smut@google.com
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/OWNERS b/ios/chrome/OWNERS
index df13981..1a9f1a2 100644
--- a/ios/chrome/OWNERS
+++ b/ios/chrome/OWNERS
@@ -12,3 +12,6 @@
 per-file ios_share_extension_resources.gyp=*
 per-file ios_today_extension_resources.gyp=*
 per-file ios_today_extension_resources_bundle.gypi=*
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 567b08f..96ab0ee 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -153,6 +153,7 @@
     "//components/keyed_service/ios",
     "//components/metrics",
     "//components/ntp_snippets",
+    "//components/payments/core",
     "//components/prefs",
     "//components/proxy_config",
     "//components/signin/core/browser",
@@ -189,6 +190,7 @@
     "//ios/chrome/browser/net",
     "//ios/chrome/browser/ntp_snippets",
     "//ios/chrome/browser/omaha",
+    "//ios/chrome/browser/payments",
     "//ios/chrome/browser/prefs",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/search_engines",
diff --git a/ios/chrome/app/DEPS b/ios/chrome/app/DEPS
index 522a5e99..e52ccc7 100644
--- a/ios/chrome/app/DEPS
+++ b/ios/chrome/app/DEPS
@@ -14,6 +14,7 @@
   "+components/history/core/browser",
   "+components/metrics",
   "+components/ntp_snippets",
+  "+components/payments/core",
   "+components/prefs",
   "+components/reading_list/core",
   "+components/signin/core/browser",
diff --git a/ios/chrome/app/OWNERS b/ios/chrome/app/OWNERS
index 9e2503a..cb182dd 100644
--- a/ios/chrome/app/OWNERS
+++ b/ios/chrome/app/OWNERS
@@ -1,2 +1,5 @@
 marq@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index d984b50..6f346ea 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -100,6 +100,7 @@
     "//components/feature_engagement",
     "//components/handoff",
     "//components/metrics",
+    "//components/payments/core",
     "//components/prefs",
     "//ios/chrome/app",
     "//ios/chrome/app/safe_mode",
@@ -114,6 +115,7 @@
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/metrics:metrics_internal",
     "//ios/chrome/browser/net",
+    "//ios/chrome/browser/payments",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/u2f",
     "//ios/chrome/browser/ui",
diff --git a/ios/chrome/app/application_delegate/OWNERS b/ios/chrome/app/application_delegate/OWNERS
index f49ed38..01e8c98 100644
--- a/ios/chrome/app/application_delegate/OWNERS
+++ b/ios/chrome/app/application_delegate/OWNERS
@@ -1 +1,4 @@
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 17c365a6..2072d63 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -357,13 +357,18 @@
                           startupInformation:_startupInformation
                       browserViewInformation:[_browserLauncher
                                                  browserViewInformation]];
-  } else if (_shouldOpenNTPTabOnActive &&
-             ![tabSwitcher openNewTabFromTabSwitcher]) {
-    BrowserViewController* bvc =
-        [[_browserLauncher browserViewInformation] currentBVC];
-    BOOL incognito = bvc == [[_browserLauncher browserViewInformation] otrBVC];
-    [bvc.dispatcher
-        openNewTab:[OpenNewTabCommand commandWithIncognito:incognito]];
+  } else if (_shouldOpenNTPTabOnActive) {
+    if (![tabSwitcher openNewTabFromTabSwitcher]) {
+      BrowserViewController* bvc =
+          [[_browserLauncher browserViewInformation] currentBVC];
+      BOOL incognito =
+          bvc == [[_browserLauncher browserViewInformation] otrBVC];
+      [bvc.dispatcher
+          openNewTab:[OpenNewTabCommand commandWithIncognito:incognito]];
+    }
+  } else {
+    [[[_browserLauncher browserViewInformation] currentBVC]
+        presentBubblesIfEligible];
   }
   _shouldOpenNTPTabOnActive = NO;
 
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.mm b/ios/chrome/app/application_delegate/mock_tab_opener.mm
index e41a08d..ca19a67 100644
--- a/ios/chrome/app/application_delegate/mock_tab_opener.mm
+++ b/ios/chrome/app/application_delegate/mock_tab_opener.mm
@@ -50,4 +50,10 @@
   return nil;
 }
 
+- (BOOL)shouldCompletePaymentRequestOnCurrentTab:
+    (id<StartupInformation>)startupInformation {
+  // Stub.
+  return NO;
+}
+
 @end
diff --git a/ios/chrome/app/application_delegate/tab_opening.h b/ios/chrome/app/application_delegate/tab_opening.h
index ff5ae8d..2d83df9 100644
--- a/ios/chrome/app/application_delegate/tab_opening.h
+++ b/ios/chrome/app/application_delegate/tab_opening.h
@@ -41,6 +41,11 @@
 - (ProceduralBlock)completionBlockForTriggeringAction:
     (NTPTabOpeningPostOpeningAction)action;
 
+// Attempts to complete a Payment Request flow with a payment response from a
+// a third party app. Returns whether or not this operation was successful.
+- (BOOL)shouldCompletePaymentRequestOnCurrentTab:
+    (id<StartupInformation>)startupInformation;
+
 @end
 
 #endif  // IOS_CHROME_APP_APPLICATION_DELEGATE_TAB_OPENING_H_
diff --git a/ios/chrome/app/application_delegate/user_activity_handler.mm b/ios/chrome/app/application_delegate/user_activity_handler.mm
index 3dcee1f..784ee54 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler.mm
@@ -17,6 +17,7 @@
 #import "ios/chrome/app/application_delegate/startup_information.h"
 #import "ios/chrome/app/application_delegate/tab_opening.h"
 #include "ios/chrome/app/application_mode.h"
+#include "ios/chrome/app/chrome_app_startup_parameters.h"
 #import "ios/chrome/app/spotlight/actions_spotlight_manager.h"
 #import "ios/chrome/app/spotlight/spotlight_util.h"
 #include "ios/chrome/browser/app_startup_parameters.h"
@@ -42,6 +43,7 @@
 NSString* const kShortcutNewIncognitoTab = @"OpenIncognitoTab";
 NSString* const kShortcutVoiceSearch = @"OpenVoiceSearch";
 NSString* const kShortcutQRScanner = @"OpenQRScanner";
+
 }  // namespace
 
 @interface UserActivityHandler ()
@@ -73,15 +75,21 @@
                               handoff::ORIGIN_COUNT);
   } else if ([userActivity.activityType
                  isEqualToString:NSUserActivityTypeBrowsingWeb]) {
-    // App was launched as the result of a Universal Link navigation. The value
-    // of userActivity.webpageURL is not used. The only supported action
-    // at this time is opening a New Tab Page.
-    GURL newTabURL(kChromeUINewTabURL);
-    webpageURL = net::NSURLWithGURL(newTabURL);
+    // App was launched as the result of a Universal Link navigation.
+    GURL gurl = net::GURLWithNSURL(webpageURL);
     AppStartupParameters* startupParams =
-        [[AppStartupParameters alloc] initWithExternalURL:newTabURL];
+        [[AppStartupParameters alloc] initWithUniversalLink:gurl];
     [startupInformation setStartupParameters:startupParams];
     base::RecordAction(base::UserMetricsAction("IOSLaunchedByUniversalLink"));
+
+    if (startupParams)
+      webpageURL = net::NSURLWithGURL([startupParams externalURL]);
+
+    // Don't call continueUserActivityURL if the completePaymentRequest flag
+    // is set since the startup parameters need to be handled in
+    // -handleStartupParametersWithTabOpener:
+    if (startupParams && startupParams.completePaymentRequest)
+      return YES;
   } else if (spotlight::IsSpotlightAvailable() &&
              [userActivity.activityType
                  isEqualToString:CSSearchableItemActionType]) {
@@ -226,6 +234,13 @@
     // synchronously.
     [startupInformation setStartupParameters:nil];
   } else {
+    // Depending on the startup parameters the user may need to stay on the
+    // current tab rather than open a new one in order to complete a Payment
+    // Request. This attempts to complete any Payment Request instances on
+    // the current tab, and returns if successful.
+    if ([tabOpener shouldCompletePaymentRequestOnCurrentTab:startupInformation])
+      return;
+
     // The app is already active so the applicationDidBecomeActive: method
     // will never be called. Open the requested URL after all modal UIs have
     // been dismissed. |_startupParameters| must be retained until all deferred
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 546e267..5c080ae2 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -13,6 +13,7 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/ios/block_types.h"
 #import "base/mac/bind_objc_block.h"
@@ -29,6 +30,7 @@
 #include "components/feature_engagement/public/tracker.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
+#include "components/payments/core/features.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/url_formatter/url_formatter.h"
@@ -83,6 +85,8 @@
 #import "ios/chrome/browser/metrics/previous_session_info.h"
 #import "ios/chrome/browser/net/cookie_util.h"
 #include "ios/chrome/browser/net/crl_set_fetcher.h"
+#include "ios/chrome/browser/payments/ios_payment_instrument_launcher.h"
+#include "ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/prefs/pref_observer_bridge.h"
 #import "ios/chrome/browser/reading_list/reading_list_download_service.h"
@@ -141,6 +145,7 @@
 #include "ios/web/net/request_tracker_impl.h"
 #include "ios/web/net/web_http_protocol_handler_delegate.h"
 #import "ios/web/public/navigation_manager.h"
+#include "ios/web/public/payments/payment_request.h"
 #include "ios/web/public/web_capabilities.h"
 #include "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_view_creation_util.h"
@@ -2375,6 +2380,40 @@
   }
 }
 
+- (BOOL)shouldCompletePaymentRequestOnCurrentTab:
+    (id<StartupInformation>)startupInformation {
+  if (!startupInformation.startupParameters)
+    return NO;
+
+  if (!startupInformation.startupParameters.completePaymentRequest)
+    return NO;
+
+  if (!base::FeatureList::IsEnabled(payments::features::kWebPaymentsNativeApps))
+    return NO;
+
+  payments::IOSPaymentInstrumentLauncher* paymentAppLauncher =
+      payments::IOSPaymentInstrumentLauncherFactory::GetInstance()
+          ->GetForBrowserState(_mainBrowserState);
+
+  if (!paymentAppLauncher->delegate())
+    return NO;
+
+  std::string payment_id =
+      startupInformation.startupParameters.externalURLParams
+          .find(web::kPaymentRequestIDExternal)
+          ->second;
+  if (paymentAppLauncher->payment_request_id() != payment_id)
+    return NO;
+
+  std::string payment_response =
+      startupInformation.startupParameters.externalURLParams
+          .find(web::kPaymentRequestDataExternal)
+          ->second;
+  paymentAppLauncher->ReceiveResponseFromIOSPaymentInstrument(payment_response);
+  [startupInformation setStartupParameters:nil];
+  return YES;
+}
+
 #pragma mark - SettingsNavigationControllerDelegate
 
 - (void)closeSettings {
diff --git a/ios/chrome/app/spotlight/OWNERS b/ios/chrome/app/spotlight/OWNERS
index 3ebff38d..56360bb 100644
--- a/ios/chrome/app/spotlight/OWNERS
+++ b/ios/chrome/app/spotlight/OWNERS
@@ -1 +1,4 @@
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/app/strings/OWNERS b/ios/chrome/app/strings/OWNERS
index 69a98a6..8c38e91 100644
--- a/ios/chrome/app/strings/OWNERS
+++ b/ios/chrome/app/strings/OWNERS
@@ -1,2 +1,5 @@
 per-file *.grd=*
 per-file *.xtb=*
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index eb48d60..439ffa0 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -737,6 +737,9 @@
       <message name="IDS_IOS_NET_EXPORT_NO_EMAIL_ACCOUNTS_ALERT_TITLE" desc="The title of the alert informing a user with no email accounts that they need to configure an email account to send net-export data. [Length: 20em]">
         No Email Accounts
       </message>
+      <message name="IDS_IOS_NEW_INCOGNITO_TAB_IPH_PROMOTION_TEXT" desc="Text for the New Incognito Tab Tip in-product help promotion, explaining that incognito tabs can be used to browse privately. [iOS only]" meaning="The user has never created an incognito tab before. A bubble is displayed pointing to the tools menu button and informing users about how to use incognito mode.">
+        To browse privately, open an incognito tab
+      </message>
       <message name="IDS_IOS_NEW_TAB_BOOKMARKS_PAGE_TITLE_MOBILE" desc="The 'Bookmarks' title on the new tab page [Length: 10em]">
         Bookmarks
       </message>
diff --git a/ios/chrome/app/theme/OWNERS b/ios/chrome/app/theme/OWNERS
index 40a68c7..b127701 100644
--- a/ios/chrome/app/theme/OWNERS
+++ b/ios/chrome/app/theme/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index 6fefea52..397f9e28 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -217,6 +217,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "app_startup_parameters_unittest.mm",
     "callback_counter_unittest.mm",
     "chrome_browser_provider_observer_bridge_unittest.mm",
     "chrome_url_util_unittest.mm",
diff --git a/ios/chrome/browser/OWNERS b/ios/chrome/browser/OWNERS
index def1e2a..0adc841 100644
--- a/ios/chrome/browser/OWNERS
+++ b/ios/chrome/browser/OWNERS
@@ -4,3 +4,6 @@
 per-file ios_chrome_io_thread*=rsleevi@chromium.org
 
 per-file ios_chrome_flag_descriptions.*=*
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/app_startup_parameters.h b/ios/chrome/browser/app_startup_parameters.h
index 547a79ea..165fdef4 100644
--- a/ios/chrome/browser/app_startup_parameters.h
+++ b/ios/chrome/browser/app_startup_parameters.h
@@ -7,6 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
+#include <map>
+
 enum NTPTabOpeningPostOpeningAction {
   // No action should be done
   NO_ACTION = 0,
@@ -22,9 +24,18 @@
 // of launch from another app.
 @interface AppStartupParameters : NSObject
 
-// The URL received that should be opened.
+// The URL that should be opened. This may not always be the same URL as the one
+// that was receieved. The reason for this is in the case of Universal Link
+// navigation where we may want to open up a fallback URL e.g., the New Tab
+// Page instead of the actual universal link.
 @property(nonatomic, readonly, assign) const GURL& externalURL;
 
+// The URL query string parameters in the case that the app was launched as a
+// result of Universal Link navigation. The map associates query string
+// parameters with their corresponding value.
+@property(nonatomic, assign) std::map<std::string, std::string>
+    externalURLParams;
+
 //// Boolean to track if a voice search is requested at startup.
 //@property(nonatomic, readwrite, assign) BOOL launchVoiceSearch;
 // Boolean to track if the app should launch in incognito mode.
@@ -35,12 +46,16 @@
 //@property(nonatomic, readwrite, assign) BOOL launchQRScanner;
 @property(nonatomic, readwrite, assign)
     NTPTabOpeningPostOpeningAction postOpeningAction;
+// Boolean to track if a Payment Request response is requested at startup.
+@property(nonatomic, readwrite, assign) BOOL completePaymentRequest;
 
 - (instancetype)init NS_UNAVAILABLE;
 
 - (instancetype)initWithExternalURL:(const GURL&)externalURL
     NS_DESIGNATED_INITIALIZER;
 
+- (instancetype)initWithUniversalLink:(const GURL&)universalLink;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_APP_STARTUP_PARAMETERS_H_
diff --git a/ios/chrome/browser/app_startup_parameters.mm b/ios/chrome/browser/app_startup_parameters.mm
index 742ca53..1e20047 100644
--- a/ios/chrome/browser/app_startup_parameters.mm
+++ b/ios/chrome/browser/app_startup_parameters.mm
@@ -4,6 +4,11 @@
 
 #import "ios/chrome/browser/app_startup_parameters.h"
 
+#include "base/stl_util.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/web/public/payments/payment_request.h"
+#import "net/base/mac/url_conversions.h"
+#include "net/base/url_util.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -14,8 +19,10 @@
   GURL _externalURL;
 }
 
+@synthesize externalURLParams = _externalURLParams;
 @synthesize postOpeningAction = _postOpeningAction;
 @synthesize launchInIncognito = _launchInIncognito;
+@synthesize completePaymentRequest = _completePaymentRequest;
 
 - (const GURL&)externalURL {
   return _externalURL;
@@ -29,6 +36,32 @@
   return self;
 }
 
+- (instancetype)initWithUniversalLink:(const GURL&)universalLink {
+  // If a new tab with |_externalURL| needs to be opened after the App
+  // was launched as the result of a Universal Link navigation, the only
+  // supported possibility at this time is the New Tab Page.
+  self = [self initWithExternalURL:GURL(kChromeUINewTabURL)];
+
+  if (self) {
+    std::map<std::string, std::string> parameters;
+    net::QueryIterator query_iterator(universalLink);
+    while (!query_iterator.IsAtEnd()) {
+      parameters.insert(std::make_pair(query_iterator.GetKey(),
+                                       query_iterator.GetUnescapedValue()));
+      query_iterator.Advance();
+    }
+
+    // Currently only Payment Request parameters are supported.
+    if (base::ContainsKey(parameters, web::kPaymentRequestIDExternal) &&
+        base::ContainsKey(parameters, web::kPaymentRequestDataExternal)) {
+      _externalURLParams = parameters;
+      _completePaymentRequest = YES;
+    }
+  }
+
+  return self;
+}
+
 - (NSString*)description {
   NSMutableString* description =
       [NSMutableString stringWithFormat:@"AppStartupParameters: %s",
@@ -51,6 +84,10 @@
       break;
   }
 
+  if (self.completePaymentRequest) {
+    [description appendString:@", should complete payment request"];
+  }
+
   return description;
 }
 
diff --git a/ios/chrome/browser/app_startup_parameters_unittest.mm b/ios/chrome/browser/app_startup_parameters_unittest.mm
new file mode 100644
index 0000000..953fe04
--- /dev/null
+++ b/ios/chrome/browser/app_startup_parameters_unittest.mm
@@ -0,0 +1,61 @@
+// 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 "ios/chrome/browser/app_startup_parameters.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+struct UniversalLinkDecodeTestCase {
+  GURL universal_link;
+  std::map<std::string, std::string> expected_external_url_params;
+  bool expected_complete_payment_request;
+};
+
+TEST(AppStartupParameters, QueryParametersPaymentRequest) {
+  const UniversalLinkDecodeTestCase test_cases[] = {
+      {
+          GURL("https://goo.gl/ioschrome/"), {}, false,
+      },
+      {
+          GURL("https://goo.gl/ioschrome?payment-request-id"), {}, false,
+      },
+      {
+          GURL("https://goo.gl/ioschrome?payment-request-id=092831-af18l-324"
+               "&payment-request-data="),
+          {{"payment-request-id", "092831-af18l-324"},
+           {"payment-request-data", ""}},
+          true,
+      },
+      {
+          GURL("https://goo.gl/ioschrome?payment-request-id=092831-af18l-324"
+               "&payment-request-data=JNLSKFknrlwe80dkzrnLEWR"),
+          {{"payment-request-id", "092831-af18l-324"},
+           {"payment-request-data", "JNLSKFknrlwe80dkzrnLEWR"}},
+          true,
+      },
+      {
+          GURL("https://goo.gl/ioschrome?payment-request-id=092831-af18l-324"
+               "&payment-request-data=JNLSKFknrlwe80dkzrnLEWR"
+               "&unecessary-parameter=0"),
+          {{"payment-request-id", "092831-af18l-324"},
+           {"payment-request-data", "JNLSKFknrlwe80dkzrnLEWR"},
+           {"unecessary-parameter", "0"}},
+          true,
+      },
+  };
+
+  for (size_t i = 0; i < arraysize(test_cases); ++i) {
+    const UniversalLinkDecodeTestCase& test_case = test_cases[i];
+    AppStartupParameters* params = [[AppStartupParameters alloc]
+        initWithUniversalLink:test_case.universal_link];
+    EXPECT_EQ(test_case.expected_external_url_params, params.externalURLParams);
+    EXPECT_EQ(test_case.expected_complete_payment_request,
+              params.completePaymentRequest);
+  }
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/autofill/OWNERS b/ios/chrome/browser/autofill/OWNERS
index 6a806bd..adccd5d 100644
--- a/ios/chrome/browser/autofill/OWNERS
+++ b/ios/chrome/browser/autofill/OWNERS
@@ -1 +1,4 @@
 mahmadi@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn
index 677e58d..a5b7139e 100644
--- a/ios/chrome/browser/browser_state/BUILD.gn
+++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -105,11 +105,11 @@
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/sync/glue",
     "//ios/chrome/browser/translate",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/undo",
     "//ios/net",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/signin",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/web",
     "//net",
     "//net:extras",
diff --git a/ios/chrome/browser/browser_state/OWNERS b/ios/chrome/browser/browser_state/OWNERS
index c5cd5cb..39066e1 100644
--- a/ios/chrome/browser/browser_state/OWNERS
+++ b/ios/chrome/browser/browser_state/OWNERS
@@ -1 +1,4 @@
 sdefresne@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 4f472d4..c91071c 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -47,9 +47,9 @@
 #include "ios/chrome/browser/sync/ios_user_event_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/translate/translate_accept_languages_factory.h"
+#include "ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "ios/chrome/browser/web_data_service_factory.h"
-#include "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/browser_state_metrics/OWNERS b/ios/chrome/browser/browser_state_metrics/OWNERS
index eccc04a..62cd9921 100644
--- a/ios/chrome/browser/browser_state_metrics/OWNERS
+++ b/ios/chrome/browser/browser_state_metrics/OWNERS
@@ -2,3 +2,6 @@
 erg@chromium.org
 
 # COMPONENT: UI>Browser>Profiles
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/content_suggestions/OWNERS b/ios/chrome/browser/content_suggestions/OWNERS
index 2d35f0a..f192143 100644
--- a/ios/chrome/browser/content_suggestions/OWNERS
+++ b/ios/chrome/browser/content_suggestions/OWNERS
@@ -1 +1,4 @@
 gambard@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
index 9391c7d..80e518b 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
@@ -196,11 +196,7 @@
     if (!self.sectionInformationByCategory[categoryWrapper]) {
       [self addSectionInformationForCategory:category];
     }
-    if (IsCategoryStatusAvailable(
-            self.contentService->GetCategoryStatus(category))) {
-      [sectionsInfo
-          addObject:self.sectionInformationByCategory[categoryWrapper]];
-    }
+    [sectionsInfo addObject:self.sectionInformationByCategory[categoryWrapper]];
   }
 
   [sectionsInfo addObject:self.learnMoreSectionInfo];
@@ -344,21 +340,15 @@
             (ntp_snippets::ContentSuggestionsService*)suggestionsService
                          category:(ntp_snippets::Category)category
                   statusChangedTo:(ntp_snippets::CategoryStatus)status {
-  ContentSuggestionsCategoryWrapper* wrapper =
-      [[ContentSuggestionsCategoryWrapper alloc] initWithCategory:category];
   if (!ntp_snippets::IsCategoryStatusInitOrAvailable(status)) {
     // Remove the category from the UI if it is not available.
+    ContentSuggestionsCategoryWrapper* wrapper =
+        [[ContentSuggestionsCategoryWrapper alloc] initWithCategory:category];
     ContentSuggestionsSectionInformation* sectionInfo =
         self.sectionInformationByCategory[wrapper];
 
     [self.dataSink clearSection:sectionInfo];
     [self.sectionInformationByCategory removeObjectForKey:wrapper];
-  } else {
-    if (!self.sectionInformationByCategory[wrapper]) {
-      [self addSectionInformationForCategory:category];
-    }
-    [self.dataSink
-        dataAvailableForSection:self.sectionInformationByCategory[wrapper]];
   }
 }
 
diff --git a/ios/chrome/browser/context_menu/OWNERS b/ios/chrome/browser/context_menu/OWNERS
index bf1620f..ab525656 100644
--- a/ios/chrome/browser/context_menu/OWNERS
+++ b/ios/chrome/browser/context_menu/OWNERS
@@ -1 +1,4 @@
 noyau@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/device_sharing/OWNERS b/ios/chrome/browser/device_sharing/OWNERS
index 987c0c88..7030015a3 100644
--- a/ios/chrome/browser/device_sharing/OWNERS
+++ b/ios/chrome/browser/device_sharing/OWNERS
@@ -1 +1,4 @@
 erikchen@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/dom_distiller/OWNERS b/ios/chrome/browser/dom_distiller/OWNERS
index bf1620f..ab525656 100644
--- a/ios/chrome/browser/dom_distiller/OWNERS
+++ b/ios/chrome/browser/dom_distiller/OWNERS
@@ -1 +1,4 @@
 noyau@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/feature_engagement/OWNERS b/ios/chrome/browser/feature_engagement/OWNERS
index b5a2453..782b0c3 100644
--- a/ios/chrome/browser/feature_engagement/OWNERS
+++ b/ios/chrome/browser/feature_engagement/OWNERS
@@ -1,2 +1,5 @@
 edchin@chromium.org
 gchatz@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/find_in_page/OWNERS b/ios/chrome/browser/find_in_page/OWNERS
index 40a68c7..b127701 100644
--- a/ios/chrome/browser/find_in_page/OWNERS
+++ b/ios/chrome/browser/find_in_page/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/first_run/OWNERS b/ios/chrome/browser/first_run/OWNERS
index 4905815..169936c 100644
--- a/ios/chrome/browser/first_run/OWNERS
+++ b/ios/chrome/browser/first_run/OWNERS
@@ -2,3 +2,6 @@
 msarda@chromium.org
 
 # COMPONENT: UI>Browser>FirstRun
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/history/OWNERS b/ios/chrome/browser/history/OWNERS
index 1085212..1c26985 100644
--- a/ios/chrome/browser/history/OWNERS
+++ b/ios/chrome/browser/history/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 sczs@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/infobars/OWNERS b/ios/chrome/browser/infobars/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/infobars/OWNERS
+++ b/ios/chrome/browser/infobars/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/invalidation/OWNERS b/ios/chrome/browser/invalidation/OWNERS
index 7f08e5f..2cdcafd2 100644
--- a/ios/chrome/browser/invalidation/OWNERS
+++ b/ios/chrome/browser/invalidation/OWNERS
@@ -3,3 +3,6 @@
 pavely@chromium.org
 
 # COMPONENT: Services>Invalidation
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/itunes_links/OWNERS b/ios/chrome/browser/itunes_links/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/itunes_links/OWNERS
+++ b/ios/chrome/browser/itunes_links/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/language/OWNERS b/ios/chrome/browser/language/OWNERS
index f1e82d5..bc263cd 100644
--- a/ios/chrome/browser/language/OWNERS
+++ b/ios/chrome/browser/language/OWNERS
@@ -1 +1,4 @@
 file://components/language/OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/metrics/OWNERS b/ios/chrome/browser/metrics/OWNERS
index 17a1cc1..e2a5d55 100644
--- a/ios/chrome/browser/metrics/OWNERS
+++ b/ios/chrome/browser/metrics/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/net/OWNERS b/ios/chrome/browser/net/OWNERS
index ed94051..22d5a1c 100644
--- a/ios/chrome/browser/net/OWNERS
+++ b/ios/chrome/browser/net/OWNERS
@@ -1 +1,4 @@
 droger@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ntp_snippets/OWNERS b/ios/chrome/browser/ntp_snippets/OWNERS
index 7f58514..94af0b0 100644
--- a/ios/chrome/browser/ntp_snippets/OWNERS
+++ b/ios/chrome/browser/ntp_snippets/OWNERS
@@ -1,2 +1,5 @@
 noyau@chromium.org
 gambard@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ntp_tiles/OWNERS b/ios/chrome/browser/ntp_tiles/OWNERS
index b622730..96f8a26 100644
--- a/ios/chrome/browser/ntp_tiles/OWNERS
+++ b/ios/chrome/browser/ntp_tiles/OWNERS
@@ -1,2 +1,5 @@
 sfiera@chromium.org
 file://components/ntp_tiles/OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/omaha/OWNERS b/ios/chrome/browser/omaha/OWNERS
index 40a68c7..b127701 100644
--- a/ios/chrome/browser/omaha/OWNERS
+++ b/ios/chrome/browser/omaha/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/passwords/OWNERS b/ios/chrome/browser/passwords/OWNERS
index adc8d4b..83fe1582 100644
--- a/ios/chrome/browser/passwords/OWNERS
+++ b/ios/chrome/browser/passwords/OWNERS
@@ -1,3 +1,6 @@
 melandory@chromium.org
 vabr@chromium.org
 vasilii@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn
index 5463f9a..84f2e85 100644
--- a/ios/chrome/browser/payments/BUILD.gn
+++ b/ios/chrome/browser/payments/BUILD.gn
@@ -13,6 +13,10 @@
     "ios_payment_instrument.mm",
     "ios_payment_instrument_finder.h",
     "ios_payment_instrument_finder.mm",
+    "ios_payment_instrument_launcher.h",
+    "ios_payment_instrument_launcher.mm",
+    "ios_payment_instrument_launcher_factory.h",
+    "ios_payment_instrument_launcher_factory.mm",
     "ios_payment_request_cache_factory.h",
     "ios_payment_request_cache_factory.mm",
     "origin_security_checker.h",
@@ -53,6 +57,7 @@
   testonly = true
   sources = [
     "ios_payment_instrument_finder_unittest.mm",
+    "ios_payment_instrument_launcher_unittest.mm",
     "payment_request_unittest.mm",
     "payment_response_helper_unittest.mm",
   ]
diff --git a/ios/chrome/browser/payments/OWNERS b/ios/chrome/browser/payments/OWNERS
index 4cfb56d..2a262cc2 100644
--- a/ios/chrome/browser/payments/OWNERS
+++ b/ios/chrome/browser/payments/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 mahmadi@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/payments/ios_payment_instrument.h b/ios/chrome/browser/payments/ios_payment_instrument.h
index 417e49d0..7adde13 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument.h
+++ b/ios/chrome/browser/payments/ios_payment_instrument.h
@@ -15,6 +15,7 @@
 #include "base/strings/string16.h"
 #include "components/payments/core/payment_instrument.h"
 #include "ios/chrome/browser/payments/payment_request.h"
+#include "url/gurl.h"
 
 @class PaymentRequestUIDelegate;
 
@@ -40,7 +41,7 @@
   // the native payment app from Chrome.
   IOSPaymentInstrument(
       const std::string& method_name,
-      const std::string& universal_link,
+      const GURL& universal_link,
       const std::string& app_name,
       UIImage* icon_image,
       id<PaymentRequestUIDelegate> payment_request_ui_delegate);
@@ -69,7 +70,7 @@
 
  private:
   std::string method_name_;
-  std::string universal_link_;
+  GURL universal_link_;
   std::string app_name_;
   base::scoped_nsobject<UIImage> icon_image_;
 
diff --git a/ios/chrome/browser/payments/ios_payment_instrument.mm b/ios/chrome/browser/payments/ios_payment_instrument.mm
index 29522f6..fa19e48 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument.mm
@@ -5,7 +5,6 @@
 #include "ios/chrome/browser/payments/ios_payment_instrument.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -32,7 +31,7 @@
 
 IOSPaymentInstrument::IOSPaymentInstrument(
     const std::string& method_name,
-    const std::string& universal_link,
+    const GURL& universal_link,
     const std::string& app_name,
     UIImage* icon_image,
     id<PaymentRequestUIDelegate> payment_request_ui_delegate)
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_finder.mm b/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
index ad9cf41..6578fba67 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
@@ -343,8 +343,8 @@
           UIImage* icon =
               [UIImage imageWithData:data scale:[UIScreen mainScreen].scale];
           instruments_found_.push_back(base::MakeUnique<IOSPaymentInstrument>(
-              local_method_name.spec(), local_universal_link.spec(),
-              local_app_name, icon, payment_request_ui_delegate_));
+              local_method_name.spec(), local_universal_link, local_app_name,
+              icon, payment_request_ui_delegate_));
         }
         OnPaymentInstrumentProcessed();
       };
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.h b/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
new file mode 100644
index 0000000..bab3a95
--- /dev/null
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.h
@@ -0,0 +1,111 @@
+// 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 IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_H_
+#define IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "ios/chrome/browser/payments/payment_request.h"
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+namespace web {
+class NavigationItem;
+}  // namespace web
+
+namespace payments {
+
+// Launches a native iOS third party payment app and handles the response
+// returned from that payment app. Only one instance of this object can exist
+// per browser state. This launcher can only handle one request at a time,
+// so any calls this class while another request is processing will fail and
+// will cause the in-flight request to fail.
+class IOSPaymentInstrumentLauncher : public KeyedService {
+ public:
+  IOSPaymentInstrumentLauncher();
+  ~IOSPaymentInstrumentLauncher() override;
+
+  // Attempts to launch a third party iOS payment app. Uses |payment_request|
+  // and |acitive_web_state| to build numerous parameters that get seraliazed
+  // into a JSON string and then encoded into base-64. |universal_link| is then
+  // invoked with the built parameters passed in as a query string. If the class
+  // fails to open the universal link the error callback of |delegate| will
+  // be invoked. If the class is successful in opening the universal link
+  // the success callback will be invoked when the payment app calls back into
+  // Chrome with a payment response. Finally, the class returns a boolean
+  // indicating if it made an attempt to launch the IOSPaymentInstrument. The
+  // only instance when the launcher will not attempt a launch is when there is
+  // another in-flight request already happening.
+  bool LaunchIOSPaymentInstrument(
+      payments::PaymentRequest* payment_request,
+      web::WebState* active_web_state,
+      GURL& universal_link,
+      payments::PaymentInstrument::Delegate* delegate);
+
+  // Callback for when an iOS payment app sends a response back to Chrome.
+  // |response| is a base-64 encodeded string. When decoded, |response| is
+  // is expected to contain the method name of the payment instrument used,
+  // whether or not the payment app was able to successfully complete its part
+  // of the transaction, and details that contain information for the merchant
+  // website to complete the transaction. The details are only parsed if
+  // the payment app claims to have successfully completed its part of the
+  // transaction.
+  void ReceiveResponseFromIOSPaymentInstrument(
+      const std::string& base_64_response);
+
+  // Before invoking ReceieveResponseFromIOSPaymentInstrument, callers can
+  // use delegate() to ensure that the delegate property is valid.
+  payments::PaymentInstrument::Delegate* delegate() { return delegate_; }
+
+  // Sets the delegate for the current IOSPaymentInstrumentLauncher request.
+  void set_delegate(payments::PaymentInstrument::Delegate* delegate) {
+    delegate_ = delegate;
+  }
+
+  // The payment request ID is exposed in order validate responses from
+  // third party payment apps.
+  std::string payment_request_id() { return payment_request_id_; }
+
+ private:
+  friend class IOSPaymentInstrumentLauncherTest;
+
+  // Returns the JSON-serialized dictionary from each method name the merchant
+  // requested to the corresponding method data. |stringified_method_data| is
+  // a mapping of the payment method names to the corresponding JSON-stringified
+  // payment method specific data. This function converts that map into a JSON
+  // readable object.
+  std::unique_ptr<base::DictionaryValue> SerializeMethodData(
+      const std::map<std::string, std::set<std::string>>&
+          stringified_method_data);
+
+  // Returns the JSON-serialized top-level certificate chain of the browsing
+  // context. |item| has information on the browsing state, including the
+  // SSL certificate needed to build the certificate chain.
+  std::unique_ptr<base::ListValue> SerializeCertificateChain(
+      web::NavigationItem* item);
+
+  // Returns the JSON-serialized array of web::PaymentDetailsModifier objects.
+  // |details| is the object that represents the details of a PaymentRequest
+  // object and contains the vector of web::PaymentDetailsModifier objects to
+  // serialize.
+  std::unique_ptr<base::ListValue> SerializeModifiers(
+      web::PaymentDetails details);
+
+  // Invokes the payment instrument delegate with the appropriate function.
+  // If |method_name| or |details| are empty then |delegate_| calls
+  // OnInstrumentDetailsError, otherwise |delegate_| calls
+  // OnInstrumentDetailsReady. After invoking the delegate function this method
+  // will also reset |delegate_| and |payment_request_id_|.
+  void CompleteLaunchRequest(const std::string& method_name,
+                             const std::string& details);
+
+  payments::PaymentInstrument::Delegate* delegate_;
+  std::string payment_request_id_;
+};
+
+}  // namespace payments
+
+#endif  // IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_H_
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
new file mode 100644
index 0000000..ea3bd41
--- /dev/null
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
@@ -0,0 +1,268 @@
+// 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.
+
+#import "ios/chrome/browser/payments/ios_payment_instrument_launcher.h"
+
+#include <map>
+
+#include "base/base64.h"
+#include "base/ios/ios_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/payments/core/payment_instrument.h"
+#include "ios/web/public/navigation_item.h"
+#include "ios/web/public/navigation_manager.h"
+#include "ios/web/public/payments/payment_request.h"
+#include "ios/web/public/ssl_status.h"
+#include "ios/web/public/web_state/web_state.h"
+#include "net/base/mac/url_conversions.mm"
+#include "net/base/url_util.h"
+#include "net/cert/x509_certificate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Parameters sent to a payment app.
+static const char kMethodNames[] = "methodNames";
+static const char kMethodData[] = "methodData";
+static const char kMerchantName[] = "merchantName";
+static const char kTopLevelOrigin[] = "topLevelOrigin";
+static const char kTopLevelCertificateChain[] = "topLevelCertificateChain";
+static const char kCertificate[] = "cert";
+static const char kTotal[] = "total";
+static const char kModifiers[] = "modifiers";
+
+// Parameters received from a payment app.
+static const char kSuccess[] = "success";
+static const char kMethodName[] = "methodName";
+static const char kDetails[] = "details";
+
+}  // namespace
+
+namespace payments {
+
+IOSPaymentInstrumentLauncher::IOSPaymentInstrumentLauncher()
+    : delegate_(nullptr), payment_request_id_("") {}
+
+IOSPaymentInstrumentLauncher::~IOSPaymentInstrumentLauncher() {}
+
+bool IOSPaymentInstrumentLauncher::LaunchIOSPaymentInstrument(
+    payments::PaymentRequest* payment_request,
+    web::WebState* active_web_state,
+    GURL& universal_link,
+    payments::PaymentInstrument::Delegate* delegate) {
+  DCHECK(delegate);
+
+  // Only one request can be handled at a time.
+  if (delegate_ || !payment_request_id_.empty()) {
+    return false;
+  }
+
+  delegate_ = delegate;
+
+  std::unique_ptr<base::DictionaryValue> params_to_payment_app =
+      base::MakeUnique<base::DictionaryValue>();
+
+  // TODO(crbug.com/748556): Filter the following list to only show method names
+  // that we know the payment app supports. For now, sending all the requested
+  // method names i.e., 'basic-card' and 'https://alice-pay.com" to the payment
+  // app works as the payment app provider can then decide what to do with that
+  // information, but this is not ideal nor is this consistent with Android
+  // implementation.
+  std::unique_ptr<base::ListValue> method_names =
+      base::MakeUnique<base::ListValue>();
+  for (auto const& it : payment_request->stringified_method_data())
+    method_names->GetList().emplace_back(it.first);
+  params_to_payment_app->SetList(kMethodNames, std::move(method_names));
+
+  params_to_payment_app->SetDictionary(
+      kMethodData,
+      SerializeMethodData(payment_request->stringified_method_data()));
+
+  params_to_payment_app->SetString(
+      kMerchantName, base::UTF16ToASCII(active_web_state->GetTitle()));
+
+  params_to_payment_app->SetKey(
+      kTopLevelOrigin,
+      base::Value(active_web_state->GetLastCommittedURL().host()));
+
+  params_to_payment_app->SetList(
+      kTopLevelCertificateChain,
+      SerializeCertificateChain(
+          active_web_state->GetNavigationManager()->GetVisibleItem()));
+
+  params_to_payment_app->SetDictionary(
+      kTotal, payment_request->web_payment_request()
+                  .details.total.amount.ToDictionaryValue());
+
+  params_to_payment_app->SetList(
+      kModifiers,
+      SerializeModifiers(payment_request->web_payment_request().details));
+
+  // JSON stringify the object so that it can be encoded in base-64.
+  std::string stringified_parameters;
+  base::JSONWriter::Write(*params_to_payment_app, &stringified_parameters);
+  std::string base_64_params;
+  base::Base64Encode(stringified_parameters, &base_64_params);
+
+  payment_request_id_ =
+      payment_request->web_payment_request().payment_request_id;
+
+  universal_link = net::AppendQueryParameter(
+      universal_link, web::kPaymentRequestIDExternal, payment_request_id_);
+  universal_link = net::AppendQueryParameter(
+      universal_link, web::kPaymentRequestDataExternal, base_64_params);
+  NSURL* url = net::NSURLWithGURL(universal_link);
+  [[UIApplication sharedApplication]
+      openURL:url
+      options:(base::ios::IsRunningOnIOS10OrLater()
+                   ? @{ UIApplicationOpenURLOptionUniversalLinksOnly : @YES }
+                   : nil)completionHandler:^(BOOL success) {
+        if (!success) {
+          CompleteLaunchRequest("", "");
+        }
+      }];
+
+  return true;
+}
+
+void IOSPaymentInstrumentLauncher::ReceiveResponseFromIOSPaymentInstrument(
+    const std::string& base_64_response) {
+  DCHECK(delegate_);
+
+  std::string stringified_parameters;
+  base::Base64Decode(base_64_response, &stringified_parameters);
+
+  std::unique_ptr<base::Value> value =
+      base::JSONReader::Read(stringified_parameters);
+  if (!value) {
+    CompleteLaunchRequest("", "");
+    return;
+  }
+
+  std::unique_ptr<base::DictionaryValue> dict =
+      base::DictionaryValue::From(std::move(value));
+  if (!dict) {
+    CompleteLaunchRequest("", "");
+    return;
+  }
+
+  int success;
+  if (!dict->GetInteger(kSuccess, &success) || success == 0) {
+    CompleteLaunchRequest("", "");
+    return;
+  }
+
+  std::string method_name;
+  if (!dict->GetString(kMethodName, &method_name) || method_name.empty()) {
+    CompleteLaunchRequest("", "");
+    return;
+  }
+
+  std::string stringified_details;
+  if (!dict->GetString(kDetails, &stringified_details) ||
+      stringified_details.empty()) {
+    CompleteLaunchRequest("", "");
+    return;
+  }
+
+  CompleteLaunchRequest(method_name, stringified_details);
+}
+
+std::unique_ptr<base::DictionaryValue>
+IOSPaymentInstrumentLauncher::SerializeMethodData(
+    const std::map<std::string, std::set<std::string>>&
+        stringified_method_data) {
+  std::unique_ptr<base::DictionaryValue> method_data =
+      base::MakeUnique<base::DictionaryValue>();
+
+  std::unique_ptr<base::ListValue> data_list;
+  std::unique_ptr<base::Value> data;
+  for (auto const& map_it : stringified_method_data) {
+    data_list = base::MakeUnique<base::ListValue>();
+    for (auto const& data_it : map_it.second) {
+      data = base::JSONReader().ReadToValue(data_it);
+      // We insert the stringified data, not the JSON object and only if the
+      // corresponding JSON object is valid.
+      if (data)
+        data_list->GetList().emplace_back(data_it);
+    }
+
+    method_data->SetKey(map_it.first, *data_list);
+  }
+
+  return method_data;
+}
+
+std::unique_ptr<base::ListValue>
+IOSPaymentInstrumentLauncher::SerializeCertificateChain(
+    web::NavigationItem* item) {
+  std::unique_ptr<base::ListValue> cert_chain_list =
+      base::MakeUnique<base::ListValue>();
+
+  if (!item)
+    return cert_chain_list;
+
+  scoped_refptr<net::X509Certificate> cert = item->GetSSL().certificate;
+  std::vector<std::vector<const char>> cert_chain;
+  net::X509Certificate::OSCertHandles cert_handles =
+      cert->GetIntermediateCertificates();
+  if (cert_handles.empty() || cert_handles[0] != cert->os_cert_handle())
+    cert_handles.insert(cert_handles.begin(), cert->os_cert_handle());
+
+  cert_chain.reserve(cert_handles.size());
+  for (auto* handle : cert_handles) {
+    std::string cert_bytes;
+    net::X509Certificate::GetDEREncoded(handle, &cert_bytes);
+    cert_chain.push_back(
+        std::vector<const char>(cert_bytes.begin(), cert_bytes.end()));
+  }
+
+  std::unique_ptr<base::DictionaryValue> cert_chain_dict;
+  std::unique_ptr<base::ListValue> byte_array;
+  for (std::vector<const char> cert_string : cert_chain) {
+    byte_array = base::MakeUnique<base::ListValue>();
+    cert_chain_dict = base::MakeUnique<base::DictionaryValue>();
+    for (const char byte : cert_string)
+      byte_array->GetList().emplace_back(byte);
+    cert_chain_dict->Set(kCertificate, std::move(byte_array));
+    cert_chain_list->GetList().push_back(*cert_chain_dict);
+  }
+
+  return cert_chain_list;
+}
+
+std::unique_ptr<base::ListValue>
+IOSPaymentInstrumentLauncher::SerializeModifiers(web::PaymentDetails details) {
+  std::unique_ptr<base::ListValue> modifiers =
+      base::MakeUnique<base::ListValue>();
+  size_t numModifiers = details.modifiers.size();
+  for (size_t i = 0; i < numModifiers; ++i) {
+    const std::unique_ptr<base::DictionaryValue> modifier =
+        details.modifiers[i].ToDictionaryValue();
+    modifiers->GetList().push_back(*modifier.get());
+  }
+
+  return modifiers;
+}
+
+void IOSPaymentInstrumentLauncher::CompleteLaunchRequest(
+    const std::string& method_name,
+    const std::string& details) {
+  if (!method_name.empty() && !details.empty())
+    delegate_->OnInstrumentDetailsReady(method_name, details);
+  else
+    delegate_->OnInstrumentDetailsError();
+  delegate_ = nullptr;
+  payment_request_id_ = "";
+}
+
+}  // namespace payments
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h b/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h
new file mode 100644
index 0000000..401a6ce6
--- /dev/null
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h
@@ -0,0 +1,50 @@
+// 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 IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_FACTORY_H_
+#define IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_FACTORY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace ios {
+class ChromeBrowserState;
+}  // namespace ios
+
+namespace payments {
+class IOSPaymentInstrumentLauncher;
+
+// Ensures that there's only one instance of
+// payments::IOSPaymentInstrumentLauncher per browser state.
+class IOSPaymentInstrumentLauncherFactory
+    : public BrowserStateKeyedServiceFactory {
+ public:
+  static IOSPaymentInstrumentLauncher* GetForBrowserState(
+      ios::ChromeBrowserState* browser_state);
+  static IOSPaymentInstrumentLauncherFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      IOSPaymentInstrumentLauncherFactory>;
+
+  IOSPaymentInstrumentLauncherFactory();
+  ~IOSPaymentInstrumentLauncherFactory() override;
+
+  // BrowserStateKeyedServiceFactory implementation.
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(IOSPaymentInstrumentLauncherFactory);
+};
+
+}  // namespace payments
+
+#endif  // IOS_CHROME_BROWSER_PAYMENTS_IOS_PAYMENT_INSTRUMENT_LAUNCHER_FACTORY_H_
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.mm
new file mode 100644
index 0000000..a4e4ff38
--- /dev/null
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.mm
@@ -0,0 +1,43 @@
+// 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 "ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/payments/ios_payment_instrument_launcher.h"
+#include "ios/web/public/browser_state.h"
+
+namespace payments {
+
+// static
+payments::IOSPaymentInstrumentLauncher*
+IOSPaymentInstrumentLauncherFactory::GetForBrowserState(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<payments::IOSPaymentInstrumentLauncher*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+// static
+IOSPaymentInstrumentLauncherFactory*
+IOSPaymentInstrumentLauncherFactory::GetInstance() {
+  return base::Singleton<IOSPaymentInstrumentLauncherFactory>::get();
+}
+
+IOSPaymentInstrumentLauncherFactory::IOSPaymentInstrumentLauncherFactory()
+    : BrowserStateKeyedServiceFactory(
+          "IOSPaymentInstrumentLauncher",
+          BrowserStateDependencyManager::GetInstance()) {}
+
+IOSPaymentInstrumentLauncherFactory::~IOSPaymentInstrumentLauncherFactory() {}
+
+std::unique_ptr<KeyedService>
+IOSPaymentInstrumentLauncherFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  return base::WrapUnique(new payments::IOSPaymentInstrumentLauncher);
+}
+
+}  // namespace payments
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
new file mode 100644
index 0000000..26a33eb
--- /dev/null
+++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher_unittest.mm
@@ -0,0 +1,330 @@
+// 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 "ios/chrome/browser/payments/ios_payment_instrument_launcher.h"
+
+#include <map>
+#include <memory>
+
+#include "base/base64.h"
+#include "base/ios/ios_util.h"
+#include "base/json/json_writer.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/values.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/payments/core/payment_instrument.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/payments/test_payment_request.h"
+#include "ios/web/public/payments/payment_request.h"
+#include "ios/web/public/test/fakes/test_navigation_manager.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace payments {
+
+namespace {
+
+class FakePaymentInstrumentDelegate : public PaymentInstrument::Delegate {
+ public:
+  FakePaymentInstrumentDelegate() {}
+
+  void OnInstrumentDetailsReady(
+      const std::string& method_name,
+      const std::string& stringified_details) override {
+    if (run_loop_)
+      run_loop_->Quit();
+    on_instrument_details_ready_called_ = true;
+  }
+
+  void OnInstrumentDetailsError() override {
+    if (run_loop_)
+      run_loop_->Quit();
+    on_instrument_details_error_called_ = true;
+  }
+
+  bool WasOnInstrumentDetailsReadyCalled() {
+    return on_instrument_details_ready_called_;
+  }
+
+  bool WasOnInstrumentDetailsErrorCalled() {
+    return on_instrument_details_error_called_;
+  }
+
+  void RunLoop() {
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
+  }
+
+ private:
+  bool on_instrument_details_ready_called_ = false;
+  bool on_instrument_details_error_called_ = false;
+
+  std::unique_ptr<base::RunLoop> run_loop_;
+};
+
+}  // namespace
+
+class IOSPaymentInstrumentLauncherTest : public testing::Test {
+ protected:
+  IOSPaymentInstrumentLauncherTest()
+      : chrome_browser_state_(TestChromeBrowserState::Builder().Build()) {}
+
+  std::unique_ptr<base::DictionaryValue> SerializeMethodDataWrapper(
+      const std::map<std::string, std::set<std::string>>&
+          stringified_method_data) {
+    IOSPaymentInstrumentLauncher launcher;
+    return launcher.SerializeMethodData(stringified_method_data);
+  }
+
+  void SetPaymentRequestID(IOSPaymentInstrumentLauncher& launcher) {
+    launcher.payment_request_id_ = "some-payment-request-id";
+  }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  web::TestWebState web_state_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+};
+
+// Tests that serializing empty stringified method data yields the expected
+// result.
+TEST_F(IOSPaymentInstrumentLauncherTest, EmptyStringifiedMethodDataDictionary) {
+  web::PaymentRequest web_payment_request;
+  autofill::TestPersonalDataManager personal_data_manager;
+  TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
+                                     &personal_data_manager);
+
+  base::DictionaryValue expected_dict;
+
+  EXPECT_TRUE(expected_dict.Equals(
+      SerializeMethodDataWrapper(payment_request.stringified_method_data())
+          .get()));
+}
+
+// Tests that serializing populated stringified method data yields the expected
+// result.
+TEST_F(IOSPaymentInstrumentLauncherTest,
+       PopulatedStringifiedMethodDataDictionary) {
+  web::PaymentRequest web_payment_request;
+  PaymentMethodData method_datum1;
+  method_datum1.supported_methods.push_back("https://jefpay.com");
+  method_datum1.supported_methods.push_back("https://bobpay.com");
+  std::unique_ptr<base::DictionaryValue> data =
+      base::MakeUnique<base::DictionaryValue>();
+  data->SetString("Some data", "Some stringified data");
+  std::string stringified_data;
+  base::JSONWriter::Write(*data, &stringified_data);
+  method_datum1.data = stringified_data;
+  web_payment_request.method_data.push_back(method_datum1);
+  PaymentMethodData method_datum2;
+  method_datum2.supported_methods.push_back("https://alicepay.com");
+  web_payment_request.method_data.push_back(method_datum2);
+  PaymentMethodData method_datum3;
+  method_datum3.supported_methods.push_back("https://jefpay.com");
+  web_payment_request.method_data.push_back(method_datum3);
+  PaymentMethodData method_datum4;
+  method_datum4.supported_methods.push_back("https://alicepay.com");
+  method_datum4.supported_methods.push_back("https://bobpay.com");
+  web_payment_request.method_data.push_back(method_datum4);
+
+  autofill::TestPersonalDataManager personal_data_manager;
+  TestPaymentRequest payment_request(web_payment_request,
+                                     chrome_browser_state_.get(), &web_state_,
+                                     &personal_data_manager);
+
+  base::DictionaryValue expected_dict;
+  std::unique_ptr<base::ListValue> jef_data_list =
+      base::MakeUnique<base::ListValue>();
+  std::unique_ptr<base::DictionaryValue> jef_data =
+      base::MakeUnique<base::DictionaryValue>();
+  jef_data->SetString("Some data", "Some stringified data");
+  std::string jef_stringified_data;
+  base::JSONWriter::Write(*jef_data, &jef_stringified_data);
+  jef_data_list->GetList().emplace_back(jef_stringified_data);
+  expected_dict.SetKey("https://jefpay.com", *jef_data_list);
+  std::unique_ptr<base::ListValue> bob_data_list =
+      base::MakeUnique<base::ListValue>();
+  std::unique_ptr<base::DictionaryValue> bob_data =
+      base::MakeUnique<base::DictionaryValue>();
+  bob_data->SetString("Some data", "Some stringified data");
+  std::string bob_stringified_data;
+  base::JSONWriter::Write(*bob_data, &bob_stringified_data);
+  bob_data_list->GetList().emplace_back(bob_stringified_data);
+  expected_dict.SetListWithoutPathExpansion("https://bobpay.com",
+                                            std::move(bob_data_list));
+  std::unique_ptr<base::ListValue> alice_data_list =
+      base::MakeUnique<base::ListValue>();
+  expected_dict.SetKey("https://alicepay.com", *alice_data_list);
+
+  EXPECT_TRUE(expected_dict.Equals(
+      SerializeMethodDataWrapper(payment_request.stringified_method_data())
+          .get()));
+}
+
+// Tests that attempting to open an invalid universal link calls the
+// OnInstrumentDetailsError() function of the delegate.
+TEST_F(IOSPaymentInstrumentLauncherTest,
+       LaunchIOSPaymentInstrument_MalformedUniversalLink) {
+  if (base::ios::IsRunningOnIOS10OrLater()) {
+    std::unique_ptr<web::TestNavigationManager> navigation_manager =
+        base::MakeUnique<web::TestNavigationManager>();
+    web_state_.SetNavigationManager(std::move(navigation_manager));
+
+    web::PaymentRequest web_payment_request;
+    autofill::TestPersonalDataManager personal_data_manager;
+    TestPaymentRequest payment_request(web_payment_request,
+                                       chrome_browser_state_.get(), &web_state_,
+                                       &personal_data_manager);
+
+    FakePaymentInstrumentDelegate instrument_delegate;
+    IOSPaymentInstrumentLauncher launcher;
+
+    EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+    EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+    GURL malformed_link = GURL("http://bad-link.com");
+    launcher.LaunchIOSPaymentInstrument(&payment_request, &web_state_,
+                                        malformed_link, &instrument_delegate);
+    instrument_delegate.RunLoop();
+
+    EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+    EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+  }
+}
+
+// Tests that if the response from the payment app is not a valid JSON
+// dictionary then the OnInstrumentDetailsError() function of the delegate
+// is called.
+TEST_F(IOSPaymentInstrumentLauncherTest,
+       ReceiveResponseFromIOSPaymentInstrument_ResponseNotDictionary) {
+  FakePaymentInstrumentDelegate instrument_delegate;
+  IOSPaymentInstrumentLauncher launcher;
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  std::string stringified_response = "\"JSON\"";
+  std::string base_64_params;
+  base::Base64Encode(stringified_response, &base_64_params);
+
+  launcher.set_delegate(&instrument_delegate);
+  launcher.ReceiveResponseFromIOSPaymentInstrument(base_64_params);
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+// Tests that if a payment app claims to have not been successful in
+// fulfilling its side of the transaction then OnInstrumentDetailsError()
+// is called.
+TEST_F(IOSPaymentInstrumentLauncherTest,
+       ReceiveResponseFromIOSPaymentInstrument_PaymentAppFailed) {
+  FakePaymentInstrumentDelegate instrument_delegate;
+  IOSPaymentInstrumentLauncher launcher;
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  base::DictionaryValue response;
+  response.SetInteger("success", 0);
+  response.SetString("methodName", "visa");
+  response.SetString("details", "Some details");
+  std::string stringified_response;
+  base::JSONWriter::Write(response, &stringified_response);
+  std::string base_64_params;
+  base::Base64Encode(stringified_response, &base_64_params);
+
+  launcher.set_delegate(&instrument_delegate);
+  launcher.ReceiveResponseFromIOSPaymentInstrument(base_64_params);
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+// Tests that if the response from the payment app does not contain a
+// method name then OnInstrumentDetailsError() of the delegate is
+// called.
+TEST_F(IOSPaymentInstrumentLauncherTest,
+       ReceiveResponseFromIOSPaymentInstrument_NoMethodName) {
+  FakePaymentInstrumentDelegate instrument_delegate;
+  IOSPaymentInstrumentLauncher launcher;
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  base::DictionaryValue response;
+  response.SetInteger("success", 1);
+  response.SetString("details", "Some details");
+  std::string stringified_response;
+  base::JSONWriter::Write(response, &stringified_response);
+  std::string base_64_params;
+  base::Base64Encode(stringified_response, &base_64_params);
+
+  launcher.set_delegate(&instrument_delegate);
+  launcher.ReceiveResponseFromIOSPaymentInstrument(base_64_params);
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+// Tests that if the response from the payment app does not contain any
+// stringified details then OnInstrumentDetailsError() of the delegate is
+// called.
+TEST_F(IOSPaymentInstrumentLauncherTest,
+       ReceiveResponseFromIOSPaymentInstrument_NoDetails) {
+  FakePaymentInstrumentDelegate instrument_delegate;
+  IOSPaymentInstrumentLauncher launcher;
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  base::DictionaryValue response;
+  response.SetInteger("success", 1);
+  response.SetString("methodName", "visa");
+  std::string stringified_response;
+  base::JSONWriter::Write(response, &stringified_response);
+  std::string base_64_params;
+  base::Base64Encode(stringified_response, &base_64_params);
+
+  launcher.set_delegate(&instrument_delegate);
+  launcher.ReceiveResponseFromIOSPaymentInstrument(base_64_params);
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+// Tests that if the response from the payment app has all necessary
+// parameters with valid values then OnInstrumentDetailsReady() of the
+// delegate is called.
+TEST_F(IOSPaymentInstrumentLauncherTest,
+       ReceiveResponseFromIOSPaymentInstrument_WellFormedResponse) {
+  FakePaymentInstrumentDelegate instrument_delegate;
+  IOSPaymentInstrumentLauncher launcher;
+
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+  base::DictionaryValue response;
+  response.SetInteger("success", 1);
+  response.SetString("methodName", "visa");
+  response.SetString("details", "Some details");
+  std::string stringified_response;
+  base::JSONWriter::Write(response, &stringified_response);
+  std::string base_64_params;
+  base::Base64Encode(stringified_response, &base_64_params);
+
+  launcher.set_delegate(&instrument_delegate);
+  launcher.ReceiveResponseFromIOSPaymentInstrument(base_64_params);
+
+  EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+  EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+}  // namespace payments
diff --git a/ios/chrome/browser/payments/payment_request.h b/ios/chrome/browser/payments/payment_request.h
index 75215f9..42323eac 100644
--- a/ios/chrome/browser/payments/payment_request.h
+++ b/ios/chrome/browser/payments/payment_request.h
@@ -63,7 +63,7 @@
 
 // Called when a native iOS payment app should be launched.
 - (void)paymentInstrument:(payments::IOSPaymentInstrument*)paymentInstrument
-    launchAppWithUniversalLink:(std::string)universalLink
+    launchAppWithUniversalLink:(GURL)universalLink
             instrumentDelegate:(payments::PaymentInstrument::Delegate*)delegate;
 
 @end
diff --git a/ios/chrome/browser/payments/payment_response_helper.h b/ios/chrome/browser/payments/payment_response_helper.h
index 42d87b4..5434272 100644
--- a/ios/chrome/browser/payments/payment_response_helper.h
+++ b/ios/chrome/browser/payments/payment_response_helper.h
@@ -20,6 +20,9 @@
 // Called when the payment method details have been successfully received.
 - (void)paymentResponseHelperDidReceivePaymentMethodDetails;
 
+// Called if an error occured when attempting to receive payment method details.
+- (void)paymentResponseHelperDidFailToReceivePaymentMethodDetails;
+
 // Called when the payment method details have been successfully received and
 // the shipping address and the contact info are normalized, if applicable.
 - (void)paymentResponseHelperDidCompleteWithPaymentResponse:
@@ -44,7 +47,7 @@
   void OnInstrumentDetailsReady(
       const std::string& method_name,
       const std::string& stringified_details) override;
-  void OnInstrumentDetailsError() override {}
+  void OnInstrumentDetailsError() override;
 
  private:
   // Called when the AddressNormalizationManager is done, whether any autofill
diff --git a/ios/chrome/browser/payments/payment_response_helper.mm b/ios/chrome/browser/payments/payment_response_helper.mm
index 9535010..4801802 100644
--- a/ios/chrome/browser/payments/payment_response_helper.mm
+++ b/ios/chrome/browser/payments/payment_response_helper.mm
@@ -59,6 +59,10 @@
           &PaymentResponseHelper::AddressNormalizationCompleted, AsWeakPtr()));
 }
 
+void PaymentResponseHelper::OnInstrumentDetailsError() {
+  [consumer_ paymentResponseHelperDidFailToReceivePaymentMethodDetails];
+}
+
 void PaymentResponseHelper::AddressNormalizationCompleted() {
   web::PaymentResponse response;
 
diff --git a/ios/chrome/browser/payments/payment_response_helper_unittest.mm b/ios/chrome/browser/payments/payment_response_helper_unittest.mm
index 1cc2d814..41eeac4f 100644
--- a/ios/chrome/browser/payments/payment_response_helper_unittest.mm
+++ b/ios/chrome/browser/payments/payment_response_helper_unittest.mm
@@ -46,6 +46,9 @@
 - (void)paymentResponseHelperDidReceivePaymentMethodDetails {
 }
 
+- (void)paymentResponseHelperDidFailToReceivePaymentMethodDetails {
+}
+
 - (void)paymentResponseHelperDidCompleteWithPaymentResponse:
     (const web::PaymentResponse&)paymentResponse {
   return static_cast<
diff --git a/ios/chrome/browser/reading_list/OWNERS b/ios/chrome/browser/reading_list/OWNERS
index c6815f7..ccd8b4f 100644
--- a/ios/chrome/browser/reading_list/OWNERS
+++ b/ios/chrome/browser/reading_list/OWNERS
@@ -1,2 +1,5 @@
 noyau@chromium.org
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/services/OWNERS b/ios/chrome/browser/services/OWNERS
index b380428..f0db296d 100644
--- a/ios/chrome/browser/services/OWNERS
+++ b/ios/chrome/browser/services/OWNERS
@@ -1,3 +1,6 @@
 zea@chromium.org
 
 # COMPONENT: Services>CloudMessaging
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/services/gcm/OWNERS b/ios/chrome/browser/services/gcm/OWNERS
index 295d996..2271273 100644
--- a/ios/chrome/browser/services/gcm/OWNERS
+++ b/ios/chrome/browser/services/gcm/OWNERS
@@ -4,3 +4,6 @@
 zea@chromium.org
 
 # COMPONENT: Services>CloudMessaging
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/sessions/OWNERS b/ios/chrome/browser/sessions/OWNERS
index 40a68c7..b127701 100644
--- a/ios/chrome/browser/sessions/OWNERS
+++ b/ios/chrome/browser/sessions/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/share_extension/OWNERS b/ios/chrome/browser/share_extension/OWNERS
index 4a03091..6a63c2b3 100644
--- a/ios/chrome/browser/share_extension/OWNERS
+++ b/ios/chrome/browser/share_extension/OWNERS
@@ -1,2 +1,5 @@
 gambard@chromium.org
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/signin/OWNERS b/ios/chrome/browser/signin/OWNERS
index bdb69b2..a8f0801c 100644
--- a/ios/chrome/browser/signin/OWNERS
+++ b/ios/chrome/browser/signin/OWNERS
@@ -3,3 +3,6 @@
 msarda@chromium.org
 
 # COMPONENT: Services>SignIn
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/snapshots/OWNERS b/ios/chrome/browser/snapshots/OWNERS
index d13827d..f4823b16 100644
--- a/ios/chrome/browser/snapshots/OWNERS
+++ b/ios/chrome/browser/snapshots/OWNERS
@@ -1,2 +1,5 @@
 jif@chromium.org
 justincohen@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ssl/OWNERS b/ios/chrome/browser/ssl/OWNERS
index 115e3a7..b9da33cc 100644
--- a/ios/chrome/browser/ssl/OWNERS
+++ b/ios/chrome/browser/ssl/OWNERS
@@ -2,3 +2,6 @@
 felt@chromium.org
 
 # COMPONENT: Security>UX
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/store_kit/OWNERS b/ios/chrome/browser/store_kit/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/store_kit/OWNERS
+++ b/ios/chrome/browser/store_kit/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/suggestions/OWNERS b/ios/chrome/browser/suggestions/OWNERS
index 1986f2f0..2c6e9e7 100644
--- a/ios/chrome/browser/suggestions/OWNERS
+++ b/ios/chrome/browser/suggestions/OWNERS
@@ -4,3 +4,4 @@
 
 # TEAM: ntp-dev@chromium.org
 # COMPONENT: UI>Browser>NewTabPage
+# OS: iOS
diff --git a/ios/chrome/browser/sync/OWNERS b/ios/chrome/browser/sync/OWNERS
index 261ab18..c1575f5 100644
--- a/ios/chrome/browser/sync/OWNERS
+++ b/ios/chrome/browser/sync/OWNERS
@@ -1 +1,4 @@
 file://components/sync/OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/tabs/OWNERS b/ios/chrome/browser/tabs/OWNERS
index 40a68c7..b127701 100644
--- a/ios/chrome/browser/tabs/OWNERS
+++ b/ios/chrome/browser/tabs/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 044b7bd..6c12045 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -102,6 +102,7 @@
 #import "ios/chrome/browser/web/auto_reload_bridge.h"
 #import "ios/chrome/browser/web/external_app_launcher.h"
 #import "ios/chrome/browser/web/navigation_manager_util.h"
+#import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
 #import "ios/chrome/browser/web/passkit_dialog_provider.h"
 #include "ios/chrome/browser/web/print_observer.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
@@ -231,6 +232,9 @@
   // YES if the Tab needs to be reloaded after the app becomes active.
   BOOL _requireReloadAfterBecomingActive;
 
+  // YES if the Tab needs to be reloaded after displaying.
+  BOOL _requireReloadOnDisplay;
+
   // Last visited timestamp.
   double _lastVisitedTimestamp;
 
@@ -1227,7 +1231,7 @@
     self.navigationManager->Reload(web::ReloadType::NORMAL,
                                    false /* check_for_repost */);
   } else {
-    [self.webController requirePageReload];
+    _requireReloadOnDisplay = YES;
   }
   _requireReloadAfterBecomingActive = NO;
 }
@@ -1719,13 +1723,10 @@
     if (!applicationIsNotActive)
       [_fullScreenController disableFullScreen];
   } else {
-    [self.webController requirePageReload];
+    _requireReloadOnDisplay = YES;
   }
   // Returning to the app (after the renderer crashed in the background) and
   // having the page reload is much less confusing for the user.
-  // Note: Given that the tab is visible, calling |requirePageReload| will not
-  // work when the app becomes active because there is nothing to trigger
-  // a view redisplay in that scenario.
   _requireReloadAfterBecomingActive = _visible && applicationIsNotActive;
   [self.dialogDelegate cancelDialogForTab:self];
 }
@@ -1813,6 +1814,13 @@
   if (self.webState)
     self.webState->WasShown();
   [_inputAccessoryViewController wasShown];
+
+  if (_requireReloadOnDisplay) {
+    PagePlaceholderTabHelper::FromWebState(self.webState)
+        ->AddPlaceholderForNextNavigation();
+    [self.webController loadCurrentURL];
+    _requireReloadOnDisplay = NO;
+  }
 }
 
 - (void)wasHidden {
diff --git a/ios/chrome/browser/translate/OWNERS b/ios/chrome/browser/translate/OWNERS
index ca1bcb6a..51f79e89 100644
--- a/ios/chrome/browser/translate/OWNERS
+++ b/ios/chrome/browser/translate/OWNERS
@@ -1,2 +1,5 @@
 file://components/translate/OWNERS
 droger@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/u2f/OWNERS b/ios/chrome/browser/u2f/OWNERS
index ea2dcf4..bd6fe7d6 100644
--- a/ios/chrome/browser/u2f/OWNERS
+++ b/ios/chrome/browser/u2f/OWNERS
@@ -1 +1,4 @@
 eugenebut@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/OWNERS b/ios/chrome/browser/ui/OWNERS
index 7e77d32..1ccf6c3c 100644
--- a/ios/chrome/browser/ui/OWNERS
+++ b/ios/chrome/browser/ui/OWNERS
@@ -3,3 +3,6 @@
 # like it needs extra review, or is related to the Omnibox.
 marq@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/activity_services/OWNERS b/ios/chrome/browser/ui/activity_services/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/ui/activity_services/OWNERS
+++ b/ios/chrome/browser/ui/activity_services/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/alert_coordinator/OWNERS b/ios/chrome/browser/ui/alert_coordinator/OWNERS
index 0f2892e..8b593c3 100644
--- a/ios/chrome/browser/ui/alert_coordinator/OWNERS
+++ b/ios/chrome/browser/ui/alert_coordinator/OWNERS
@@ -1,2 +1,5 @@
 gambard@chromium.org
 michaeldo@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index b0e8632..5d7f181 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -188,7 +188,6 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/ui",
-    "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/ntp:ntp_controller",
     "//ios/chrome/browser/ui/settings",
diff --git a/ios/chrome/browser/ui/authentication/OWNERS b/ios/chrome/browser/ui/authentication/OWNERS
index bdb69b2..a8f0801c 100644
--- a/ios/chrome/browser/ui/authentication/OWNERS
+++ b/ios/chrome/browser/ui/authentication/OWNERS
@@ -3,3 +3,6 @@
 msarda@chromium.org
 
 # COMPONENT: Services>SignIn
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm b/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
index a4470ba..64a0a75 100644
--- a/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
@@ -10,14 +10,9 @@
 #include "components/signin/core/browser/signin_manager.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
-#import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/commands/open_url_command.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
-#import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
-#import "ios/chrome/browser/ui/settings/import_data_collection_view_controller.h"
-#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
-#import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
@@ -63,21 +58,6 @@
       forConfigKey:kGREYConfigKeySynchronizationEnabled];
 }
 
-// Taps the view with accessibility matcher |accessibility_matcher|.
-void TapViewWithMatcher(id<GREYMatcher> accessibility_matcher) {
-  // grey_sufficientlyVisible() is necessary because reloading a cell in a
-  // collection view might duplicate it (with the old one being hidden but
-  // EarlGrey can find it).
-  id<GREYMatcher> matcher =
-      grey_allOf(accessibility_matcher, grey_sufficientlyVisible(), nil);
-  [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_tap()];
-}
-
-// Taps the view with acessibility identifier |accessibility_id|.
-void TapViewWithAccessibilityId(NSString* accessiblity_id) {
-  TapViewWithMatcher(grey_accessibilityID(accessiblity_id));
-}
-
 // Taps the button with accessibility label |label|.
 void TapButtonWithAccessibilityLabel(NSString* label) {
   id<GREYMatcher> matcher =
@@ -164,13 +144,16 @@
   AssertAuthenticatedIdentityInActiveProfile(identity1);
 
   // Open accounts settings, then sync settings.
-  TapViewWithAccessibilityId(kSettingsAccountCellId);
-  TapViewWithAccessibilityId(kSettingsAccountsSyncCellId);
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::AccountsSyncButton()]
+      performAction:grey_tap()];
 
   // Switch Sync account to |identity2|.
   TapButtonWithAccessibilityLabel(identity2.userEmail);
-
-  TapViewWithAccessibilityId(kImportDataKeepSeparateCellId);
+  [[EarlGrey selectElementWithMatcher:
+                 chrome_test_util::SettingsImportDataKeepSeparateButton()]
+      performAction:grey_tap()];
   TapButtonWithLabelId(IDS_IOS_OPTIONS_IMPORT_DATA_CONTINUE_BUTTON);
 
   // Check the signed-in user did change.
@@ -199,13 +182,16 @@
   AssertAuthenticatedIdentityInActiveProfile(identity1);
 
   // Open accounts settings, then sync settings.
-  TapViewWithAccessibilityId(kSettingsAccountCellId);
-  TapViewWithAccessibilityId(kSettingsAccountsSyncCellId);
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::AccountsSyncButton()]
+      performAction:grey_tap()];
 
   // Switch Sync account to |identity2|.
   TapButtonWithAccessibilityLabel(identity2.userEmail);
-
-  TapViewWithAccessibilityId(kImportDataImportCellId);
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::
+                                          SettingsImportDataImportButton()]
+      performAction:grey_tap()];
   TapButtonWithLabelId(IDS_IOS_OPTIONS_IMPORT_DATA_CONTINUE_BUTTON);
 
   // Check the signed-in user did change.
@@ -246,8 +232,11 @@
   AssertAuthenticatedIdentityInActiveProfile(managed_identity);
 
   // Switch Sync account to |identity|.
-  TapViewWithAccessibilityId(kSettingsAccountCellId);
-  TapViewWithAccessibilityId(kSettingsAccountsSyncCellId);
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::AccountsSyncButton()]
+      performAction:grey_tap()];
+
   TapButtonWithAccessibilityLabel(identity.userEmail);
 
   SetEarlGreySynchronizationEnabled(NO);
@@ -276,16 +265,19 @@
   AssertAuthenticatedIdentityInActiveProfile(identity);
 
   // Go to Accounts Settings and tap the sign out button.
-  TapViewWithAccessibilityId(kSettingsAccountCellId);
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
+      performAction:grey_tap()];
+
   const CGFloat scroll_displacement = 100.0;
-  [[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                           kSettingsAccountsSignoutCellId)]
+  [[[EarlGrey
+      selectElementWithMatcher:grey_allOf(
+                                   chrome_test_util::SignOutAccountsButton(),
+                                   grey_interactable(), nil)]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                   scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kSettingsAccountsId)]
+      onElementWithMatcher:chrome_test_util::SettingsAccountsCollectionView()]
       performAction:grey_tap()];
   TapButtonWithLabelId(IDS_IOS_DISCONNECT_DIALOG_CONTINUE_BUTTON_MOBILE);
-
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
       performAction:grey_tap()];
 
@@ -319,16 +311,19 @@
   AssertAuthenticatedIdentityInActiveProfile(identity);
 
   // Go to Accounts Settings and tap the sign out button.
-  TapViewWithAccessibilityId(kSettingsAccountCellId);
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
+      performAction:grey_tap()];
+
   const CGFloat scroll_displacement = 100.0;
-  [[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                           kSettingsAccountsSignoutCellId)]
+  [[[EarlGrey
+      selectElementWithMatcher:grey_allOf(
+                                   chrome_test_util::SignOutAccountsButton(),
+                                   grey_interactable(), nil)]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                   scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kSettingsAccountsId)]
+      onElementWithMatcher:chrome_test_util::SettingsAccountsCollectionView()]
       performAction:grey_tap()];
   TapButtonWithLabelId(IDS_IOS_MANAGED_DISCONNECT_DIALOG_ACCEPT);
-
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
       performAction:grey_tap()];
 
@@ -463,18 +458,22 @@
   AssertAuthenticatedIdentityInActiveProfile(identity2);
 
   // Go to Accounts Settings and tap the sign out button.
-  TapViewWithAccessibilityId(kSettingsAccountCellId);
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
+      performAction:grey_tap()];
+
   const CGFloat scroll_displacement = 100.0;
-  [[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                           kSettingsAccountsSignoutCellId)]
+  [[[EarlGrey
+      selectElementWithMatcher:grey_allOf(
+                                   chrome_test_util::SignOutAccountsButton(),
+                                   grey_interactable(), nil)]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                   scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kSettingsAccountsId)]
+      onElementWithMatcher:chrome_test_util::SettingsAccountsCollectionView()]
       performAction:grey_tap()];
   TapButtonWithLabelId(IDS_IOS_DISCONNECT_DIALOG_CONTINUE_BUTTON_MOBILE);
   AssertAuthenticatedIdentityInActiveProfile(nil);
-
-  TapViewWithMatcher(chrome_test_util::SignInMenuButton());
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::SignInMenuButton()]
+      performAction:grey_tap()];
   [ChromeEarlGreyUI signInToIdentityByEmail:identity1.userEmail];
 
   // Open new tab to cancel sign-in.
@@ -514,7 +513,7 @@
       selectElementWithMatcher:grey_accessibilityID(kToolsMenuBookmarksId)]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                   scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
+      onElementWithMatcher:chrome_test_util::ToolsMenuView()]
       performAction:grey_tap()];
 
   if (!IsIPadIdiom()) {
@@ -530,8 +529,7 @@
                  grey_descendant(grey_text(topLevelFolderTitle)), nil);
   [[EarlGrey selectElementWithMatcher:all_bookmarks_matcher]
       performAction:grey_tap()];
-
-  TapViewWithAccessibilityId(kSigninPromoSecondaryButtonId);
+  TapButtonWithLabelId(IDS_IOS_BOOKMARK_PROMO_SIGN_IN_BUTTON);
 
   // Assert sign-in screen was shown.
   id<GREYMatcher> signin_matcher =
@@ -552,7 +550,7 @@
       selectElementWithMatcher:grey_accessibilityID(kToolsMenuBookmarksId)]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                   scroll_displacement)
-      onElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
+      onElementWithMatcher:chrome_test_util::ToolsMenuView()]
       performAction:grey_tap()];
   if (!IsIPadIdiom()) {
     [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"Menu")]
@@ -560,7 +558,7 @@
   }
   [[EarlGrey selectElementWithMatcher:all_bookmarks_matcher]
       performAction:grey_tap()];
-  TapViewWithAccessibilityId(kSigninPromoSecondaryButtonId);
+  TapButtonWithLabelId(IDS_IOS_BOOKMARK_PROMO_SIGN_IN_BUTTON);
   [[EarlGrey selectElementWithMatcher:signin_matcher]
       assertWithMatcher:grey_sufficientlyVisible()];
 
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view.h b/ios/chrome/browser/ui/authentication/signin_promo_view.h
index fda056f..d9eaa27 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view.h
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view.h
@@ -20,11 +20,6 @@
   SigninPromoViewModeWarmState,
 };
 
-extern NSString* const kSigninPromoViewId;
-extern NSString* const kSigninPromoPrimaryButtonId;
-extern NSString* const kSigninPromoSecondaryButtonId;
-extern NSString* const kSigninPromoCloseButtonId;
-
 // This class creates an image view, a label and 2 buttons. This view can be
 // configured with 2 modes : "Cold State" and "Warm State".
 // + "Cold State" mode displays the chomium icon in the image view, and only
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view.mm b/ios/chrome/browser/ui/authentication/signin_promo_view.mm
index b67421d..31bb7b38 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view.mm
@@ -32,12 +32,6 @@
 const CGFloat kButtonHeight = 36;
 }
 
-NSString* const kSigninPromoViewId = @"kSigninPromoViewId";
-NSString* const kSigninPromoPrimaryButtonId = @"kSigninPromoPrimaryButtonId";
-NSString* const kSigninPromoSecondaryButtonId =
-    @"kSigninPromoSecondaryButtonId";
-NSString* const kSigninPromoCloseButtonId = @"kSigninPromoCloseButtonId";
-
 @implementation SigninPromoView {
   NSArray<NSLayoutConstraint*>* _coldStateConstraints;
   NSArray<NSLayoutConstraint*>* _warmStateConstraints;
@@ -56,7 +50,6 @@
   self = [super initWithFrame:frame];
   if (self) {
     self.isAccessibilityElement = YES;
-    self.accessibilityIdentifier = kSigninPromoViewId;
 
     // Adding subviews.
     self.clipsToBounds = YES;
@@ -70,7 +63,7 @@
 
     _primaryButton = [[MDCFlatButton alloc] init];
     _primaryButton.translatesAutoresizingMaskIntoConstraints = NO;
-    _primaryButton.accessibilityIdentifier = kSigninPromoPrimaryButtonId;
+    _primaryButton.accessibilityIdentifier = @"signin_promo_primary_button";
     _primaryButton.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
     [_primaryButton addTarget:self
                        action:@selector(onPrimaryButtonAction:)
@@ -79,7 +72,7 @@
 
     _secondaryButton = [[MDCFlatButton alloc] init];
     _secondaryButton.translatesAutoresizingMaskIntoConstraints = NO;
-    _secondaryButton.accessibilityIdentifier = kSigninPromoSecondaryButtonId;
+    _secondaryButton.accessibilityIdentifier = @"signin_promo_secondary_button";
     [_secondaryButton addTarget:self
                          action:@selector(onSecondaryButtonAction:)
                forControlEvents:UIControlEventTouchUpInside];
@@ -87,7 +80,7 @@
 
     _closeButton = [[UIButton alloc] init];
     _closeButton.translatesAutoresizingMaskIntoConstraints = NO;
-    _closeButton.accessibilityIdentifier = kSigninPromoCloseButtonId;
+    _closeButton.accessibilityIdentifier = @"signin_promo_close_button";
     [self addSubview:_closeButton];
 
     // Adding style.
diff --git a/ios/chrome/browser/ui/autofill/OWNERS b/ios/chrome/browser/ui/autofill/OWNERS
index 6a806bd..adccd5d 100644
--- a/ios/chrome/browser/ui/autofill/OWNERS
+++ b/ios/chrome/browser/ui/autofill/OWNERS
@@ -1 +1,4 @@
 mahmadi@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/bookmarks/BUILD.gn b/ios/chrome/browser/ui/bookmarks/BUILD.gn
index 8d4f289..91ce5d6 100644
--- a/ios/chrome/browser/ui/bookmarks/BUILD.gn
+++ b/ios/chrome/browser/ui/bookmarks/BUILD.gn
@@ -187,7 +187,6 @@
     "//ios/chrome/browser/bookmarks:bookmarks_utils",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/ui",
-    "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/ui/tools_menu",
diff --git a/ios/chrome/browser/ui/bookmarks/OWNERS b/ios/chrome/browser/ui/bookmarks/OWNERS
index 3b99155..f6480c4 100644
--- a/ios/chrome/browser/ui/bookmarks/OWNERS
+++ b/ios/chrome/browser/ui/bookmarks/OWNERS
@@ -1,3 +1,6 @@
 noyau@chromium.org
 lpromero@chromium.org
 ramyasharma@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
index 785d4e4..9fd6d95 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
@@ -18,7 +18,6 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #include "ios/chrome/browser/pref_names.h"
-#import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
@@ -1096,16 +1095,16 @@
   }];
   // Check that promo is visible.
   [BookmarksTestCase verifyPromoAlreadySeen:NO];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SignInPromoView()]
-      assertWithMatcher:grey_sufficientlyVisible()];
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"promo_view")]
+      assertWithMatcher:grey_notNil()];
 
   // Tap the dismiss button.
   [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSigninPromoCloseButtonId)]
+      selectElementWithMatcher:grey_accessibilityID(@"promo_no_thanks_button")]
       performAction:grey_tap()];
 
   // Wait until promo is gone.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::SignInPromoView()]
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"promo_view")]
       assertWithMatcher:grey_notVisible()];
 
   // Check that the promo already seen state is updated.
@@ -1127,16 +1126,12 @@
 
   // Check that promo is visible.
   [BookmarksTestCase verifyPromoAlreadySeen:NO];
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(chrome_test_util::SignInPromoView(),
-                                          grey_sufficientlyVisible(), nil)]
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"promo_view")]
       assertWithMatcher:grey_notNil()];
 
   // Tap the Sign in button.
   [[EarlGrey
-      selectElementWithMatcher:grey_allOf(grey_accessibilityID(
-                                              kSigninPromoSecondaryButtonId),
-                                          grey_sufficientlyVisible(), nil)]
+      selectElementWithMatcher:grey_accessibilityID(@"promo_sign_in_button")]
       performAction:grey_tap()];
 
   // Tap the CANCEL button.
@@ -1146,9 +1141,7 @@
                      uppercaseString])] performAction:grey_tap()];
 
   // Check that the bookmarks UI reappeared and the cell is still here.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(chrome_test_util::SignInPromoView(),
-                                          grey_sufficientlyVisible(), nil)]
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"promo_view")]
       assertWithMatcher:grey_notNil()];
 
   [BookmarksTestCase verifyPromoAlreadySeen:NO];
diff --git a/ios/shared/chrome/browser/ui/browser_list/BUILD.gn b/ios/chrome/browser/ui/browser_list/BUILD.gn
similarity index 100%
rename from ios/shared/chrome/browser/ui/browser_list/BUILD.gn
rename to ios/chrome/browser/ui/browser_list/BUILD.gn
diff --git a/ios/shared/chrome/browser/ui/browser_list/DEPS b/ios/chrome/browser/ui/browser_list/DEPS
similarity index 100%
rename from ios/shared/chrome/browser/ui/browser_list/DEPS
rename to ios/chrome/browser/ui/browser_list/DEPS
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser.h b/ios/chrome/browser/ui/browser_list/browser.h
similarity index 87%
rename from ios/shared/chrome/browser/ui/browser_list/browser.h
rename to ios/chrome/browser/ui/browser_list/browser.h
index 213f8d1..665e031 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser.h
+++ b/ios/chrome/browser/ui/browser_list/browser.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_H_
 
 #include <memory>
 
@@ -46,4 +46,4 @@
   DISALLOW_COPY_AND_ASSIGN(Browser);
 };
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser.mm b/ios/chrome/browser/ui/browser_list/browser.mm
similarity index 86%
rename from ios/shared/chrome/browser/ui/browser_list/browser.mm
rename to ios/chrome/browser/ui/browser_list/browser.mm
index b80b383..c4f8b380 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser.mm
+++ b/ios/chrome/browser/ui/browser_list/browser.mm
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 
+#import "ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list.h b/ios/chrome/browser/ui/browser_list/browser_list.h
similarity index 86%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list.h
rename to ios/chrome/browser/ui/browser_list/browser_list.h
index 11edf98f..a2a71e5 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list.h
+++ b/ios/chrome/browser/ui/browser_list/browser_list.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
 
 #include <memory>
 #include <vector>
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/supports_user_data.h"
-#include "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#include "ios/chrome/browser/ui/browser_list/browser.h"
 
 class BrowserListObserver;
 
@@ -60,4 +60,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserList);
 };
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list.mm b/ios/chrome/browser/ui/browser_list/browser_list.mm
similarity index 93%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list.mm
rename to ios/chrome/browser/ui/browser_list/browser_list.mm
index 94a039f4..1bac1c2b 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list.mm
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list.h"
 
 #include <stdint.h>
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_observer.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_observer.h b/ios/chrome/browser/ui/browser_list/browser_list_observer.h
similarity index 76%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_observer.h
rename to ios/chrome/browser/ui/browser_list/browser_list_observer.h
index 6afc61e..dc13323 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_observer.h
+++ b/ios/chrome/browser/ui/browser_list/browser_list_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_OBSERVER_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_OBSERVER_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_OBSERVER_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_OBSERVER_H_
 
 #include "base/macros.h"
 
@@ -26,4 +26,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserListObserver);
 };
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_OBSERVER_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_OBSERVER_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_observer.mm b/ios/chrome/browser/ui/browser_list/browser_list_observer.mm
similarity index 89%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_observer.mm
rename to ios/chrome/browser/ui/browser_list/browser_list_observer.mm
index ac6a9e4..f9a80a2 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_observer.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list_observer.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_observer.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service.h b/ios/chrome/browser/ui/browser_list/browser_list_session_service.h
similarity index 81%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_session_service.h
rename to ios/chrome/browser/ui/browser_list/browser_list_session_service.h
index c4803b2..061e05f 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service.h
+++ b/ios/chrome/browser/ui/browser_list/browser_list_session_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_H_
 
 #include "base/macros.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -31,4 +31,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserListSessionService);
 };
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.h b/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h
similarity index 82%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.h
rename to ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h
index f2d20de..3e8f058 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.h
+++ b/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_FACTORY_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_FACTORY_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_FACTORY_H_
 
 #include "base/macros.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
@@ -44,4 +44,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserListSessionServiceFactory);
 };
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_FACTORY_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_FACTORY_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm b/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
similarity index 93%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
rename to ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
index 3276814..877c95a 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 
 #include <memory>
 
@@ -16,8 +16,8 @@
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.h"
 #import "ios/web/public/certificate_policy_cache.h"
 #import "ios/web/public/web_state/session_certificate_policy_cache.h"
 
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.h b/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.h
similarity index 85%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.h
rename to ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.h
index 6a0cb29..cf32fae4 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.h
+++ b/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_IMPL_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_IMPL_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_IMPL_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_IMPL_H_
 
 #import <Foundation/Foundation.h>
 
@@ -12,7 +12,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service.h"
 #import "ios/web/public/web_state/web_state.h"
 
 class BrowserList;
@@ -62,4 +62,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserListSessionServiceImpl);
 };
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_IMPL_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_SESSION_SERVICE_IMPL_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm b/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
similarity index 97%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
rename to ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
index b1e164d..f6841c1 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_impl.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.h"
 
 #include <map>
 
@@ -14,12 +14,12 @@
 #import "ios/chrome/browser/sessions/session_ios.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #import "ios/chrome/browser/sessions/session_window_ios.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_serialization.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_observer.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/web_state/web_state.h"
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_list_unittest.mm b/ios/chrome/browser/ui/browser_list/browser_list_unittest.mm
similarity index 96%
rename from ios/shared/chrome/browser/ui/browser_list/browser_list_unittest.mm
rename to ios/chrome/browser/ui/browser_list/browser_list_unittest.mm
index ece8ca1..fc1e633 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_list_unittest.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list_unittest.mm
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list.h"
 
 #include <memory>
 
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_observer.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_user_data.h b/ios/chrome/browser/ui/browser_list/browser_user_data.h
similarity index 89%
rename from ios/shared/chrome/browser/ui/browser_list/browser_user_data.h
rename to ios/chrome/browser/ui/browser_list/browser_user_data.h
index 15562a3..337956be 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_user_data.h
+++ b/ios/chrome/browser/ui/browser_list/browser_user_data.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_USER_DATA_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_USER_DATA_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_USER_DATA_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_USER_DATA_H_
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/supports_user_data.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 
 // A base class for classes attached to, and scoped to, the lifetime of a
 // Browser. For example:
@@ -71,4 +71,4 @@
   template <>                              \
   int BrowserUserData<TYPE>::kLocatorKey = 0
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_USER_DATA_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_USER_DATA_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h b/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h
similarity index 74%
rename from ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h
rename to ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h
index e09ba1d7..355468d 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h
+++ b/ios/chrome/browser/ui/browser_list/browser_web_state_list_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 IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_WEB_STATE_LIST_DELEGATE_H_
-#define IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_WEB_STATE_LIST_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_WEB_STATE_LIST_DELEGATE_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_WEB_STATE_LIST_DELEGATE_H_
 
 #include "base/macros.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
@@ -26,4 +26,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserWebStateListDelegate);
 };
 
-#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_WEB_STATE_LIST_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_WEB_STATE_LIST_DELEGATE_H_
diff --git a/ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm b/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
similarity index 92%
rename from ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
rename to ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
index b8c92f3..5ebff4d 100644
--- a/ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/shared/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h"
+#import "ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h"
 
 #include "base/logging.h"
 #import "ios/chrome/browser/find_in_page/find_tab_helper.h"
diff --git a/ios/chrome/browser/ui/browser_view_controller.h b/ios/chrome/browser/ui/browser_view_controller.h
index 6d87b4a..88a7947 100644
--- a/ios/chrome/browser/ui/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view_controller.h
@@ -107,6 +107,12 @@
 // Called when the user explicitly opens the tab switcher.
 - (void)userEnteredTabSwitcher;
 
+// Presents either the new tab tip or incognito tab tip in-product help bubbles
+// if the the user is in a valid state to see one of them. At most one bubble
+// will be shown. If the feature engagement tracker determines it is not valid
+// to see one of the bubbles, that bubble will not be shown.
+- (void)presentBubblesIfEligible;
+
 // Called when the browser state provided to this instance is being destroyed.
 // At this point the browser will no longer ever be active, and will likely be
 // deallocated soon.
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 0a40ac3..703859def 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -581,6 +581,10 @@
 @property(nonatomic, strong)
     BubbleViewControllerPresenter* tabTipBubblePresenter;
 
+// Used to display the new incognito tab tip in-product help promotion bubble.
+@property(nonatomic, strong)
+    BubbleViewControllerPresenter* incognitoTabTipBubblePresenter;
+
 // BVC initialization:
 // If the BVC is initialized with a valid browser state & tab model immediately,
 // the path is straightforward: functionality is enabled, and the UI is built
@@ -694,10 +698,22 @@
 // promotion until the feature engagement tracker database is fully initialized.
 // Does not present the bubble if |tabTipBubblePresenter.userEngaged| is |YES|
 // to prevent resetting |tabTipBubblePresenter| and affecting the value of
-// |userEngaged|.
+// |userEngaged|. Does not present the bubble if the feature engagement tracker
+// determines it is not valid to present it.
 - (void)presentNewTabTipBubbleOnInitialized;
-// Presents a bubble associated with the new tab tip in-product help promotion.
+// Optionally presents a bubble associated with the new tab tip in-product help
+// promotion. If the feature engagement tracker determines it is valid to show
+// the new tab tip, then it initializes |tabTipBubblePresenter| and presents
+// the bubble. If it is not valid to show the new tab tip,
+// |tabTipBubblePresenter| is set to |nil| and no bubble is shown.
 - (void)presentNewTabTipBubble;
+// Waits to present a bubble associated with the new incognito tab tip
+// in-product help promotion until the feature engagement tracker database is
+// fully initialized.
+- (void)presentNewIncognitoTabTipBubbleOnInitialized;
+// Presents a bubble associated with the new incognito tab tip in-product help
+// promotion.
+- (void)presentNewIncognitoTabTipBubble;
 
 // Create and show the find bar.
 - (void)initFindBarForTab;
@@ -947,7 +963,8 @@
 @synthesize presenting = _presenting;
 @synthesize foregroundTabWasAddedCompletionBlock =
     _foregroundTabWasAddedCompletionBlock;
-@synthesize tabTipBubblePresenter = tabTipBubblePresenter;
+@synthesize tabTipBubblePresenter = _tabTipBubblePresenter;
+@synthesize incognitoTabTipBubblePresenter = _incognitoTabTipBubblePresenter;
 
 #pragma mark - Object lifecycle
 
@@ -1217,6 +1234,11 @@
   }
 }
 
+- (void)presentBubblesIfEligible {
+  [self presentNewTabTipBubbleOnInitialized];
+  [self presentNewIncognitoTabTipBubble];
+}
+
 #pragma mark - UIViewController methods
 
 // Perform additional set up after loading the view, typically from a nib.
@@ -1263,7 +1285,7 @@
   [super viewDidAppear:animated];
   self.viewVisible = YES;
   [self updateDialogPresenterActiveState];
-  [self presentNewTabTipBubbleOnInitialized];
+  [self presentBubblesIfEligible];
 }
 
 - (void)viewWillAppear:(BOOL)animated {
@@ -2073,6 +2095,9 @@
     tabSwitcherAnchor = [self.toolbarController
         anchorPointForTabSwitcherButton:BubbleArrowDirectionUp];
   }
+  // If the feature engagement tracker does not consider it valid to display
+  // the new tab tip, then |bubblePresenterForFeature| returns |nil| and the
+  // call to |presentInViewController| is a no-op.
   self.tabTipBubblePresenter =
       [self bubblePresenterForFeature:feature_engagement::kIPHNewTabTipFeature
                             direction:BubbleArrowDirectionUp
@@ -2083,6 +2108,46 @@
                                           anchorPoint:tabSwitcherAnchor];
 }
 
+- (void)presentNewIncognitoTabTipBubbleOnInitialized {
+  // Do not override |incognitoTabtipBubblePresenter| or set it to nil if the
+  // user is still considered engaged.
+  if (!self.incognitoTabTipBubblePresenter.isUserEngaged) {
+    __weak BrowserViewController* weakSelf = self;
+    void (^onInitializedBlock)(bool) = ^(bool successfullyLoaded) {
+      [weakSelf presentNewIncognitoTabTipBubble];
+    };
+
+    // Use a callback in case the new incognito tab tip should be shown on
+    // startup. This ensures that the tracker's database will be fully loaded
+    // before checking if the promotion should be displayed.
+    feature_engagement::TrackerFactory::GetForBrowserState(self.browserState)
+        ->AddOnInitializedCallback(base::BindBlockArc(onInitializedBlock));
+  }
+}
+
+- (void)presentNewIncognitoTabTipBubble {
+  DCHECK([self.toolbarController
+      respondsToSelector:@selector(anchorPointForToolsMenuButton:)]);
+  NSString* text = l10n_util::GetNSStringWithFixup(
+      IDS_IOS_NEW_INCOGNITO_TAB_IPH_PROMOTION_TEXT);
+  CGPoint toolsButtonAnchor = [self.toolbarController
+      anchorPointForToolsMenuButton:BubbleArrowDirectionUp];
+  self.incognitoTabTipBubblePresenter =
+      [self bubblePresenterForFeature:feature_engagement::
+                                          kIPHNewIncognitoTabTipFeature
+                            direction:BubbleArrowDirectionUp
+                            alignment:BubbleAlignmentTrailing
+                                 text:text];
+  [self.incognitoTabTipBubblePresenter
+      presentInViewController:self
+                         view:self.view
+                  anchorPoint:toolsButtonAnchor];
+  // Only trigger the tools menu button animation if the bubble is shown.
+  if (self.incognitoTabTipBubblePresenter) {
+    [self.toolbarController triggerToolsMenuButtonAnimation];
+  }
+}
+
 #pragma mark - Tap handling
 
 - (void)setLastTapPoint:(OpenNewTabCommand*)command {
@@ -4097,6 +4162,15 @@
 
   [configuration setUserAgentType:self.userAgentType];
 
+  if (self.incognitoTabTipBubblePresenter.triggerFollowUpAction) {
+    [configuration setHighlightNewIncognitoTabCell:YES];
+    [self.incognitoTabTipBubblePresenter setTriggerFollowUpAction:NO];
+  }
+
+  if (self.incognitoTabTipBubblePresenter.isUserEngaged) {
+    base::RecordAction(UserMetricsAction("NewIncognitoTabTipTargetSelected"));
+  }
+
   [_toolbarController showToolsMenuPopupWithConfiguration:configuration];
 
   ToolsPopupController* toolsPopupController =
diff --git a/ios/chrome/browser/ui/bubble/OWNERS b/ios/chrome/browser/ui/bubble/OWNERS
index d122690d..2c40cf4 100644
--- a/ios/chrome/browser/ui/bubble/OWNERS
+++ b/ios/chrome/browser/ui/bubble/OWNERS
@@ -1,2 +1,4 @@
 edchin@chromium.org
-gchatz@chromium.org
\ No newline at end of file
+gchatz@chromium.org
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm b/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm
index bed713c..2cb7c9b 100644
--- a/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm
@@ -14,7 +14,7 @@
 #endif
 
 namespace {
-const CGFloat kTestBubbleAlignmentOffset = 40.0f;
+const CGFloat kTestBubbleAlignmentOffset = 25.0f;
 }  // namespace
 
 namespace bubble_util {
diff --git a/ios/chrome/browser/ui/bubble/bubble_view.mm b/ios/chrome/browser/ui/bubble/bubble_view.mm
index 5c4f211..b2b8b3c 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view.mm
@@ -12,7 +12,7 @@
 #error "This file requires ARC support."
 #endif
 
-const CGFloat kBubbleAlignmentOffset = 40.0f;
+const CGFloat kBubbleAlignmentOffset = 25.0f;
 
 namespace {
 // The color of the bubble (both circular background and arrow).
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h
index 4db25936..911f1c6 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h
@@ -30,6 +30,13 @@
 // the bubble had an effect on the user's action.
 @property(nonatomic, assign, readonly, getter=isUserEngaged) BOOL userEngaged;
 
+// Determines whether a follow-up action, such as highlighting a UI element,
+// should be triggered. This depends on |userEngaged|, since a follow-up action
+// should only occur if the user is engaged with the bubble. Defaults to |YES|,
+// and is set to |NO| once |userEngaged| is set to |NO| or after the user has
+// triggered the follow-up action.
+@property(nonatomic, assign) BOOL triggerFollowUpAction;
+
 // Initializes the presenter. |text| is the text displayed by the bubble.
 // |arrowDirection| is the direction the bubble's arrow is pointing. |alignment|
 // is the position of the arrow on the bubble. |dismissalCallback| is a block
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm
index cb298b0..21dd759f 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm
@@ -71,6 +71,7 @@
 @synthesize bubbleDismissalTimer = _bubbleDismissalTimer;
 @synthesize engagementTimer = _engagementTimer;
 @synthesize userEngaged = _userEngaged;
+@synthesize triggerFollowUpAction = _triggerFollowUpAction;
 @synthesize arrowDirection = _arrowDirection;
 @synthesize alignment = _alignment;
 @synthesize dismissalCallback = _dismissalCallback;
@@ -96,6 +97,7 @@
     _insideBubbleTapRecognizer.delegate = self;
     _insideBubbleTapRecognizer.cancelsTouchesInView = NO;
     _userEngaged = NO;
+    _triggerFollowUpAction = NO;
     _arrowDirection = arrowDirection;
     _alignment = alignment;
     _dismissalCallback = dismissalCallback;
@@ -132,6 +134,7 @@
                              repeats:NO];
 
   self.userEngaged = YES;
+  self.triggerFollowUpAction = YES;
   self.engagementTimer =
       [NSTimer scheduledTimerWithTimeInterval:kBubbleEngagementDuration
                                        target:self
@@ -206,6 +209,7 @@
 // Marks the user as not engaged when |engagementTimer| fires.
 - (void)engagementTimerFired:(id)sender {
   self.userEngaged = NO;
+  self.triggerFollowUpAction = NO;
   self.engagementTimer = nil;
 }
 
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm b/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm
index c9c64d71..6842465 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm
@@ -46,7 +46,7 @@
   CGSize bubbleSize = [bubble sizeThatFits:maxSize_];
   // Since the label is shorter than the minimum line width, expect the bubble
   // to be the minimum width and accommodate one line of text.
-  EXPECT_NEAR(80.0f, bubbleSize.width, 1.0f);
+  EXPECT_NEAR(56.0f, bubbleSize.width, 1.0f);
   EXPECT_NEAR(61.5f, bubbleSize.height, 1.0f);
 }
 
diff --git a/ios/chrome/browser/ui/collection_view/OWNERS b/ios/chrome/browser/ui/collection_view/OWNERS
index 077a06e..c984591 100644
--- a/ios/chrome/browser/ui/collection_view/OWNERS
+++ b/ios/chrome/browser/ui/collection_view/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/colors/OWNERS b/ios/chrome/browser/ui/colors/OWNERS
index 4bb62eb..a188a478 100644
--- a/ios/chrome/browser/ui/colors/OWNERS
+++ b/ios/chrome/browser/ui/colors/OWNERS
@@ -1 +1,4 @@
 lpromero@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/content_suggestions/OWNERS b/ios/chrome/browser/ui/content_suggestions/OWNERS
index 2d35f0a..f192143 100644
--- a/ios/chrome/browser/ui/content_suggestions/OWNERS
+++ b/ios/chrome/browser/ui/content_suggestions/OWNERS
@@ -1 +1,4 @@
 gambard@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
index 5aa5a63..967abb0 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -343,8 +343,6 @@
   NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
 
   if (suggestions.count == 0) {
-    // No suggestions for this section. Add the item signaling this section is
-    // empty if there is currently no item in it.
     if ([model hasSectionForSectionIdentifier:sectionIdentifier] &&
         [model numberOfItemsInSection:[model sectionForSectionIdentifier:
                                                  sectionIdentifier]] == 0) {
@@ -358,35 +356,6 @@
     return indexPaths;
   }
 
-  if (sectionIdentifier == SectionIdentifierLearnMore) {
-    // The "Learn more" items should only be displayed if there is at least one
-    // ContentSuggestions section.
-    if ((![model hasSectionForSectionIdentifier:SectionIdentifierArticles] &&
-         !
-         [model hasSectionForSectionIdentifier:SectionIdentifierReadingList]) ||
-        [model itemsInSectionWithIdentifier:sectionIdentifier].count > 0) {
-      return @[];
-    }
-  } else if (IsFromContentSuggestions(sectionIdentifier)) {
-    // If the section is a ContentSuggestions section, add the "Learn more"
-    // items if they are not already present.
-    if ([model hasSectionForSectionIdentifier:SectionIdentifierLearnMore] &&
-        [model itemsInSectionWithIdentifier:SectionIdentifierLearnMore].count ==
-            0) {
-      ContentSuggestionsSectionInformation* learnMoreSectionInfo =
-          self.sectionInfoBySectionIdentifier[@(SectionIdentifierLearnMore)];
-      for (CSCollectionViewItem* item in
-           [self.dataSource itemsForSectionInfo:learnMoreSectionInfo]) {
-        item.type = ItemTypeForInfo(learnMoreSectionInfo);
-        NSIndexPath* addedIndexPath = [self addItem:item
-                            toSectionWithIdentifier:SectionIdentifierLearnMore];
-
-        [indexPaths addObject:addedIndexPath];
-      }
-    }
-  }
-
-  // Add the items from this section.
   [suggestions enumerateObjectsUsingBlock:^(CSCollectionViewItem* item,
                                             NSUInteger index, BOOL* stop) {
     NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier];
diff --git a/ios/chrome/browser/ui/context_menu/OWNERS b/ios/chrome/browser/ui/context_menu/OWNERS
index 49ba79e..07726cf 100644
--- a/ios/chrome/browser/ui/context_menu/OWNERS
+++ b/ios/chrome/browser/ui/context_menu/OWNERS
@@ -1 +1,4 @@
 michaeldo@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/contextual_search/OWNERS b/ios/chrome/browser/ui/contextual_search/OWNERS
index 3141303..17f2da6 100644
--- a/ios/chrome/browser/ui/contextual_search/OWNERS
+++ b/ios/chrome/browser/ui/contextual_search/OWNERS
@@ -1,2 +1,5 @@
 marq@chromium.org
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/dialogs/OWNERS b/ios/chrome/browser/ui/dialogs/OWNERS
index 48efb49e..bd85dcf2 100644
--- a/ios/chrome/browser/ui/dialogs/OWNERS
+++ b/ios/chrome/browser/ui/dialogs/OWNERS
@@ -1 +1,4 @@
 kkhorimoto@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/elements/OWNERS b/ios/chrome/browser/ui/elements/OWNERS
index f49ed38..01e8c98 100644
--- a/ios/chrome/browser/ui/elements/OWNERS
+++ b/ios/chrome/browser/ui/elements/OWNERS
@@ -1 +1,4 @@
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/fancy_ui/OWNERS b/ios/chrome/browser/ui/fancy_ui/OWNERS
index 40a68c7..b127701 100644
--- a/ios/chrome/browser/ui/fancy_ui/OWNERS
+++ b/ios/chrome/browser/ui/fancy_ui/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/favicon/OWNERS b/ios/chrome/browser/ui/favicon/OWNERS
index 2d35f0a..f192143 100644
--- a/ios/chrome/browser/ui/favicon/OWNERS
+++ b/ios/chrome/browser/ui/favicon/OWNERS
@@ -1 +1,4 @@
 gambard@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/find_bar/BUILD.gn b/ios/chrome/browser/ui/find_bar/BUILD.gn
index ad487457..9ca15b2 100644
--- a/ios/chrome/browser/ui/find_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/find_bar/BUILD.gn
@@ -55,7 +55,6 @@
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/find_in_page",
-    "//ios/chrome/browser/ui:ui",
     "//ios/chrome/browser/ui/tools_menu",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
diff --git a/ios/chrome/browser/ui/find_bar/OWNERS b/ios/chrome/browser/ui/find_bar/OWNERS
index 8298f43..479c250 100644
--- a/ios/chrome/browser/ui/find_bar/OWNERS
+++ b/ios/chrome/browser/ui/find_bar/OWNERS
@@ -1,2 +1,5 @@
 justincohen@chromium.org
 stkhapugin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
index 36bb7af..ef3dfcd 100644
--- a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
+++ b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
@@ -4,14 +4,12 @@
 
 #import <XCTest/XCTest.h>
 
-#include "base/ios/ios_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
 #import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h"
 #import "ios/chrome/browser/ui/find_bar/find_bar_view.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
-#include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/chrome/test/earl_grey/accessibility_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -96,12 +94,6 @@
 // Tests that find in page allows iteration between search results and displays
 // correct number of results.
 - (void)testFindInPage {
-  // TODO(crbug.com/753098): Re-enable this test on iOS 11 iPad once
-  // grey_typeText works on iOS 11.
-  if (base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 11.");
-  }
-
   // Type "find".
   [self typeFindInPageText:@"find"];
   // Should be highlighting result 1 of 2.
@@ -184,7 +176,7 @@
 
 - (void)typeFindInPageText:(NSString*)text {
   [[EarlGrey selectElementWithMatcher:[self findInPageInputField]]
-      performAction:grey_typeText(text)];
+      performAction:grey_replaceText(text)];
 }
 
 - (id<GREYMatcher>)findInPageInputField {
diff --git a/ios/chrome/browser/ui/first_run/OWNERS b/ios/chrome/browser/ui/first_run/OWNERS
index 3281fe5b..e29ab3e 100644
--- a/ios/chrome/browser/ui/first_run/OWNERS
+++ b/ios/chrome/browser/ui/first_run/OWNERS
@@ -1,2 +1,5 @@
 bzanotti@chromium.org
 kkhorimoto@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/history/BUILD.gn b/ios/chrome/browser/ui/history/BUILD.gn
index 10674d96..ed52dc65c 100644
--- a/ios/chrome/browser/ui/history/BUILD.gn
+++ b/ios/chrome/browser/ui/history/BUILD.gn
@@ -207,7 +207,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
-    "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/tools_menu",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/history/OWNERS b/ios/chrome/browser/ui/history/OWNERS
index 1085212..1c26985 100644
--- a/ios/chrome/browser/ui/history/OWNERS
+++ b/ios/chrome/browser/ui/history/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 sczs@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm
index 590f142f..51b4feed 100644
--- a/ios/chrome/browser/ui/history/history_ui_egtest.mm
+++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -14,7 +14,6 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
-#import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/history/history_entry_item.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
diff --git a/ios/chrome/browser/ui/infobars/OWNERS b/ios/chrome/browser/ui/infobars/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/ui/infobars/OWNERS
+++ b/ios/chrome/browser/ui/infobars/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/keyboard/OWNERS b/ios/chrome/browser/ui/keyboard/OWNERS
index 4bb62eb..a188a478 100644
--- a/ios/chrome/browser/ui/keyboard/OWNERS
+++ b/ios/chrome/browser/ui/keyboard/OWNERS
@@ -1 +1,4 @@
 lpromero@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/keyboard_commands_egtest.mm b/ios/chrome/browser/ui/keyboard_commands_egtest.mm
index 2b72f680..db85067 100644
--- a/ios/chrome/browser/ui/keyboard_commands_egtest.mm
+++ b/ios/chrome/browser/ui/keyboard_commands_egtest.mm
@@ -115,8 +115,7 @@
 - (void)selectToolsMenuItem:(id<GREYMatcher>)toolsMenuItem {
   [ChromeEarlGreyUI openToolsMenu];
 
-  id<GREYMatcher> toolsMenuTableView =
-      grey_accessibilityID(kToolsMenuTableViewId);
+  id<GREYMatcher> toolsMenuTableView = chrome_test_util::ToolsMenuView();
   [[[EarlGrey selectElementWithMatcher:toolsMenuItem]
          usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                   kScrollDisplacement)
diff --git a/ios/chrome/browser/ui/main/OWNERS b/ios/chrome/browser/ui/main/OWNERS
index f49ed38..01e8c98 100644
--- a/ios/chrome/browser/ui/main/OWNERS
+++ b/ios/chrome/browser/ui/main/OWNERS
@@ -1 +1,4 @@
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/ntp/OWNERS b/ios/chrome/browser/ui/ntp/OWNERS
index 3306281..9b78e8b 100644
--- a/ios/chrome/browser/ui/ntp/OWNERS
+++ b/ios/chrome/browser/ui/ntp/OWNERS
@@ -1,2 +1,5 @@
 rohitrao@chromium.org
 justincohen@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/OWNERS b/ios/chrome/browser/ui/ntp/recent_tabs/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/OWNERS
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/omnibox/OWNERS b/ios/chrome/browser/ui/omnibox/OWNERS
index 03b0059..5e5e932a 100644
--- a/ios/chrome/browser/ui/omnibox/OWNERS
+++ b/ios/chrome/browser/ui/omnibox/OWNERS
@@ -2,3 +2,6 @@
 rohitrao@chromium.org
 
 # COMPONENT: UI>Browser>Omnibox
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/overscroll_actions/OWNERS b/ios/chrome/browser/ui/overscroll_actions/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/ui/overscroll_actions/OWNERS
+++ b/ios/chrome/browser/ui/overscroll_actions/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/payments/OWNERS b/ios/chrome/browser/ui/payments/OWNERS
index 4cfb56d..2a262cc2 100644
--- a/ios/chrome/browser/ui/payments/OWNERS
+++ b/ios/chrome/browser/ui/payments/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 mahmadi@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm
index 36ff598d..dddb360 100644
--- a/ios/chrome/browser/ui/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -39,6 +39,8 @@
 #include "ios/chrome/browser/autofill/validation_rules_storage_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/payments/ios_can_make_payment_query_factory.h"
+#include "ios/chrome/browser/payments/ios_payment_instrument_launcher.h"
+#include "ios/chrome/browser/payments/ios_payment_instrument_launcher_factory.h"
 #include "ios/chrome/browser/payments/ios_payment_request_cache_factory.h"
 #include "ios/chrome/browser/payments/origin_security_checker.h"
 #include "ios/chrome/browser/payments/payment_request.h"
@@ -308,12 +310,21 @@
                                            (ProceduralBlockWithBool)callback {
   DCHECK(_pendingPaymentRequest);
   _pendingPaymentRequest = nullptr;
+  [self resetIOSPaymentInstrumentLauncherDelegate];
   [self dismissUI];
   [_paymentRequestJsManager rejectRequestPromiseWithErrorName:kAbortErrorName
                                                  errorMessage:errorMessage
                                             completionHandler:callback];
 }
 
+- (void)resetIOSPaymentInstrumentLauncherDelegate {
+  payments::IOSPaymentInstrumentLauncher* paymentAppLauncher =
+      payments::IOSPaymentInstrumentLauncherFactory::GetInstance()
+          ->GetForBrowserState(_browserState);
+  DCHECK(paymentAppLauncher);
+  paymentAppLauncher->set_delegate(nullptr);
+}
+
 - (void)close {
   [self setActiveWebState:nullptr];
 }
@@ -474,6 +485,30 @@
       paymentRequest->request_payer_phone(),
       paymentRequest->request_payer_name());
 
+  // Log metrics around which payment methods are requested by the merchant.
+  const GURL kGooglePayUrl("https://google.com/pay");
+  const GURL kAndroidPayUrl("https://android.com/pay");
+  // Looking for payment methods that are NOT Google-related as well as the
+  // Google-related ones.
+  bool requestedMethodGoogle = false;
+  bool requestedMethodOther = false;
+  for (const GURL& url_payment_method :
+       paymentRequest->url_payment_method_identifiers()) {
+    if (url_payment_method == kGooglePayUrl ||
+        url_payment_method == kAndroidPayUrl) {
+      requestedMethodGoogle = true;
+    } else {
+      requestedMethodOther = true;
+    }
+  }
+
+  paymentRequest->journey_logger().SetRequestedPaymentMethodTypes(
+      /*requested_basic_card=*/!paymentRequest->supported_card_networks()
+          .empty(),
+      /*requested_method_google=*/
+      requestedMethodGoogle,
+      /*requested_method_other=*/requestedMethodOther);
+
   UIImage* pageFavicon = nil;
   web::NavigationItem* navigationItem =
       _activeWebState->GetNavigationManager()->GetVisibleItem();
@@ -756,12 +791,24 @@
 }
 
 - (void)paymentInstrument:(payments::IOSPaymentInstrument*)paymentInstrument
-    launchAppWithUniversalLink:(std::string)universalLink
+    launchAppWithUniversalLink:(GURL)universalLink
             instrumentDelegate:
                 (payments::PaymentInstrument::Delegate*)delegate {
-  // TODO(crbug.com/748556): Implement this function to use a native app's
-  // universal link to open it from Chrome with several arguments supplied
-  // from the Payment Request object.
+  DCHECK(_pendingPaymentRequest);
+  DCHECK(_activeWebState);
+
+  [_paymentRequestCoordinator setPending:YES];
+  [_paymentRequestCoordinator setCancellable:YES];
+
+  payments::IOSPaymentInstrumentLauncher* paymentAppLauncher =
+      payments::IOSPaymentInstrumentLauncherFactory::GetInstance()
+          ->GetForBrowserState(_browserState);
+  DCHECK(paymentAppLauncher);
+  if (!paymentAppLauncher->LaunchIOSPaymentInstrument(
+          _pendingPaymentRequest, _activeWebState, universalLink, delegate)) {
+    [_paymentRequestCoordinator setPending:NO];
+    [_paymentRequestCoordinator setCancellable:YES];
+  }
 }
 
 #pragma mark - PaymentRequestCoordinatorDelegate methods
@@ -840,6 +887,11 @@
   [_paymentRequestCoordinator setCancellable:NO];
 }
 
+- (void)paymentResponseHelperDidFailToReceivePaymentMethodDetails {
+  [_paymentRequestCoordinator setPending:NO];
+  [_paymentRequestCoordinator setCancellable:YES];
+}
+
 - (void)paymentResponseHelperDidCompleteWithPaymentResponse:
     (const web::PaymentResponse&)paymentResponse {
   [_paymentRequestJsManager
@@ -859,6 +911,7 @@
     _pendingPaymentRequest->journey_logger().SetAborted(
         payments::JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION);
     _pendingPaymentRequest = nullptr;
+    [self resetIOSPaymentInstrumentLauncherDelegate];
   }
 
   [self dismissUI];
diff --git a/ios/chrome/browser/ui/product_tour/OWNERS b/ios/chrome/browser/ui/product_tour/OWNERS
index 48efb49e..bd85dcf2 100644
--- a/ios/chrome/browser/ui/product_tour/OWNERS
+++ b/ios/chrome/browser/ui/product_tour/OWNERS
@@ -1 +1,4 @@
 kkhorimoto@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/promos/OWNERS b/ios/chrome/browser/ui/promos/OWNERS
index f49ed38..01e8c98 100644
--- a/ios/chrome/browser/ui/promos/OWNERS
+++ b/ios/chrome/browser/ui/promos/OWNERS
@@ -1 +1,4 @@
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/qr_scanner/OWNERS b/ios/chrome/browser/ui/qr_scanner/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/ui/qr_scanner/OWNERS
+++ b/ios/chrome/browser/ui/qr_scanner/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/reader_mode/OWNERS b/ios/chrome/browser/ui/reader_mode/OWNERS
index bf1620f..ab525656 100644
--- a/ios/chrome/browser/ui/reader_mode/OWNERS
+++ b/ios/chrome/browser/ui/reader_mode/OWNERS
@@ -1 +1,4 @@
 noyau@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/reading_list/OWNERS b/ios/chrome/browser/ui/reading_list/OWNERS
index ad2440ad..8f4d804 100644
--- a/ios/chrome/browser/ui/reading_list/OWNERS
+++ b/ios/chrome/browser/ui/reading_list/OWNERS
@@ -4,3 +4,6 @@
 
 per-file *badge_view*=edchin@chromium.org
 per-file *badge_view*=gchatz@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/sad_tab/OWNERS b/ios/chrome/browser/ui/sad_tab/OWNERS
index 48efb49e..bd85dcf2 100644
--- a/ios/chrome/browser/ui/sad_tab/OWNERS
+++ b/ios/chrome/browser/ui/sad_tab/OWNERS
@@ -1 +1,4 @@
 kkhorimoto@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 96bcdce..bb4f70b 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -350,7 +350,6 @@
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/ui:ui_internal",
-    "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/settings:test_support",
     "//ios/chrome/browser/ui/tools_menu",
     "//ios/chrome/browser/ui/util:util",
diff --git a/ios/chrome/browser/ui/settings/OWNERS b/ios/chrome/browser/ui/settings/OWNERS
index ccddf8d..126713c7 100644
--- a/ios/chrome/browser/ui/settings/OWNERS
+++ b/ios/chrome/browser/ui/settings/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 sdefresne@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
index 432b587a..9efe103 100644
--- a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
+++ b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
@@ -9,7 +9,6 @@
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
-#import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -32,7 +31,6 @@
 using chrome_test_util::SettingsAccountButton;
 using chrome_test_util::SignOutAccountsButton;
 using chrome_test_util::SignInMenuButton;
-using chrome_test_util::SignInPromoView;
 
 namespace {
 
@@ -122,7 +120,7 @@
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()
       ->ForgetIdentity(identity, nil);
 
-  [[EarlGrey selectElementWithMatcher:SignInPromoView()]
+  [[EarlGrey selectElementWithMatcher:SignInMenuButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
   AssertAuthenticatedIdentityInActiveProfile(nil);
 
@@ -152,7 +150,7 @@
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()
       ->ForgetIdentity(identity, nil);
 
-  [[EarlGrey selectElementWithMatcher:SignInPromoView()]
+  [[EarlGrey selectElementWithMatcher:SignInMenuButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
   AssertAuthenticatedIdentityInActiveProfile(nil);
 
@@ -266,7 +264,7 @@
       performAction:grey_tap()];
 
   // Check that the user is signed out and the Main Settings screen is shown.
-  [[EarlGrey selectElementWithMatcher:SignInPromoView()]
+  [[EarlGrey selectElementWithMatcher:SignInMenuButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
   AssertAuthenticatedIdentityInActiveProfile(nil);
 
@@ -300,7 +298,8 @@
       performAction:grey_tap()];
 
   // Check that Account Settings screen is open and |identity| is signed in.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(kSettingsAccountsId)]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::
+                                          SettingsAccountsCollectionView()]
       assertWithMatcher:grey_sufficientlyVisible()];
   AssertAuthenticatedIdentityInActiveProfile(identity);
 
diff --git a/ios/chrome/browser/ui/settings/settings_egtest.mm b/ios/chrome/browser/ui/settings/settings_egtest.mm
index 6d5ae00..2f1a430 100644
--- a/ios/chrome/browser/ui/settings/settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/settings_egtest.mm
@@ -21,7 +21,6 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "ios/chrome/browser/pref_names.h"
-#import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
@@ -777,9 +776,8 @@
                    @"Settings should register key commands when presented.");
 
   // Present the Sign-in UI.
-  id<GREYMatcher> matcher =
-      grey_allOf(grey_accessibilityID(kSigninPromoPrimaryButtonId),
-                 grey_sufficientlyVisible(), nil);
+  id<GREYMatcher> matcher = grey_allOf(chrome_test_util::SignInMenuButton(),
+                                       grey_sufficientlyVisible(), nil);
   [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_tap()];
   // Wait for UI to finish loading the Sign-in screen.
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
diff --git a/ios/chrome/browser/ui/side_swipe/OWNERS b/ios/chrome/browser/ui/side_swipe/OWNERS
index 330cb31..867f7564 100644
--- a/ios/chrome/browser/ui/side_swipe/OWNERS
+++ b/ios/chrome/browser/ui/side_swipe/OWNERS
@@ -1 +1,4 @@
 justincohen@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/stack_view/OWNERS b/ios/chrome/browser/ui/stack_view/OWNERS
index 48efb49e..bd85dcf2 100644
--- a/ios/chrome/browser/ui/stack_view/OWNERS
+++ b/ios/chrome/browser/ui/stack_view/OWNERS
@@ -1 +1,4 @@
 kkhorimoto@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/sync/BUILD.gn b/ios/chrome/browser/ui/sync/BUILD.gn
index de187ce..164c8669 100644
--- a/ios/chrome/browser/ui/sync/BUILD.gn
+++ b/ios/chrome/browser/ui/sync/BUILD.gn
@@ -52,7 +52,6 @@
     "//ios/chrome/browser/bookmarks:bookmarks_utils",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/sync",
-    "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/tools_menu",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/ui/sync/OWNERS b/ios/chrome/browser/ui/sync/OWNERS
index 3431e1a..3f08195 100644
--- a/ios/chrome/browser/ui/sync/OWNERS
+++ b/ios/chrome/browser/ui/sync/OWNERS
@@ -1,3 +1,6 @@
 msarda@chromium.org
 
 # COMPONENT: Services>Sync
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm b/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm
index 278d2c5..0c0f325e 100644
--- a/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm
+++ b/ios/chrome/browser/ui/sync/sync_fake_server_egtest.mm
@@ -16,7 +16,6 @@
 #include "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
-#import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
diff --git a/ios/chrome/browser/ui/tab_switcher/OWNERS b/ios/chrome/browser/ui/tab_switcher/OWNERS
index f16c1db1..e2b64c4 100644
--- a/ios/chrome/browser/ui/tab_switcher/OWNERS
+++ b/ios/chrome/browser/ui/tab_switcher/OWNERS
@@ -1,2 +1,5 @@
 edchin@chromium.org
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/tabs/OWNERS b/ios/chrome/browser/ui/tabs/OWNERS
index 40a68c7..b127701 100644
--- a/ios/chrome/browser/ui/tabs/OWNERS
+++ b/ios/chrome/browser/ui/tabs/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/toolbar/OWNERS b/ios/chrome/browser/ui/toolbar/OWNERS
index 3306281..9b78e8b 100644
--- a/ios/chrome/browser/ui/toolbar/OWNERS
+++ b/ios/chrome/browser/ui/toolbar/OWNERS
@@ -1,2 +1,5 @@
 rohitrao@chromium.org
 justincohen@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/toolbar/keyboard_assist/OWNERS b/ios/chrome/browser/ui/toolbar/keyboard_assist/OWNERS
index d2e1add..c85e66a 100644
--- a/ios/chrome/browser/ui/toolbar/keyboard_assist/OWNERS
+++ b/ios/chrome/browser/ui/toolbar/keyboard_assist/OWNERS
@@ -1 +1,4 @@
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_controller.h b/ios/chrome/browser/ui/toolbar/toolbar_controller.h
index 01b2b6f..412e40a 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_controller.h
+++ b/ios/chrome/browser/ui/toolbar/toolbar_controller.h
@@ -294,6 +294,9 @@
 // Shows/hides iPhone toolbar views for when the new tab page is displayed.
 - (void)hideViewsForNewTabPage:(BOOL)hide;
 
+// Triggers an animation on the tools menu button to draw the user's attention.
+- (void)triggerToolsMenuButtonAnimation;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_controller.mm b/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
index 5fbf638a9..ba069b311 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_controller.mm
@@ -1040,6 +1040,10 @@
   return hash;
 }
 
+- (void)triggerToolsMenuButtonAnimation {
+  [toolsMenuButton_ triggerAnimation];
+}
+
 #pragma mark -
 #pragma mark PopupMenuDelegate methods.
 
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index bd0d13e..e681448 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -173,6 +173,12 @@
     EARL_GREY_TEST_SKIPPED(@"Test not support on iPhone");
   }
 
+  // TODO(crbug.com/753098): Re-enable this test on iOS 11 iPad once
+  // grey_typeText works on iOS 11.
+  if (base::ios::IsRunningOnIOS11OrLater()) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 11.");
+  }
+
   const GURL URL = web::test::HttpServer::MakeUrl("http://origin");
 
   [ChromeEarlGrey loadURL:URL];
@@ -320,6 +326,12 @@
 
 // Verifies that the clear text button clears any text in the omnibox.
 - (void)testOmniboxClearTextButton {
+  // TODO(crbug.com/753098): Re-enable this test on iOS 11 iPad once
+  // grey_typeText works on iOS 11.
+  if (base::ios::IsRunningOnIOS11OrLater() && IsIPadIdiom()) {
+    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 11.");
+  }
+
   const GURL URL = web::test::HttpServer::MakeUrl("http://origin");
 
   [ChromeEarlGrey loadURL:URL];
diff --git a/ios/chrome/browser/ui/tools_menu/OWNERS b/ios/chrome/browser/ui/tools_menu/OWNERS
index 67c824d2..403665e 100644
--- a/ios/chrome/browser/ui/tools_menu/OWNERS
+++ b/ios/chrome/browser/ui/tools_menu/OWNERS
@@ -1,2 +1,5 @@
 rohitrao@chromium.org
 jif@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h b/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h
index 66e1049..41aaaa8 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h
@@ -32,6 +32,8 @@
 @property(nonatomic, getter=isInNewTabPage) BOOL inNewTabPage;
 // Indicates that the menu should show a "NEW" badge on the reading list item.
 @property(nonatomic, assign) BOOL showReadingListNewBadge;
+// Indicates that the New Incognito Tab cell should be highlighted in blue.
+@property(nonatomic, assign) BOOL highlightNewIncognitoTabCell;
 
 // Indicates that the menu is being shown while user agent is |userAgentType|.
 // If NONE, shows "Request Desktop Site" in disabled state.
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.mm
index 441b491..fdd6309 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_configuration.mm
@@ -18,6 +18,7 @@
 @synthesize noOpenedTabs = _noOpenedTabs;
 @synthesize inIncognito = _inIncognito;
 @synthesize showReadingListNewBadge = _showReadingListNewBadge;
+@synthesize highlightNewIncognitoTabCell = _highlightNewIncognitoTabCell;
 @synthesize userAgentType = _userAgentType;
 @synthesize requestStartTime = _requestStartTime;
 @synthesize inNewTabPage = _inNewTabPage;
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h
index 8e7ff99..fbd9762 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h
@@ -98,6 +98,10 @@
 
 - (void)hideContent;
 
+// Highlight the New Incognito Tab cell in blue. The highlight fades in, pulses
+// once, and fades out.
+- (void)triggerNewIncognitoTabCellHighlight;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TOOLS_MENU_TOOLS_MENU_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
index 920ff87..0a2db26 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm
@@ -15,6 +15,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/ui/animation_util.h"
+#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
@@ -120,6 +121,9 @@
 // Determines if the reading list should display a new feature badge. Defaults
 // to |NO|.
 @property(nonatomic, assign) BOOL showReadingListNewBadge;
+// Indicates whether the New Incognito Tab cell should be highlighted. Defaults
+// to |NO|.
+@property(nonatomic, assign) BOOL highlightNewIncognitoTabCell;
 // Tracks events for the purpose of in-product help. Does not take ownership of
 // tracker. Tracker must not be destroyed during lifetime of
 // ToolsMenuViewController. Defaults to |NULL|.
@@ -139,6 +143,7 @@
 @implementation ToolsMenuViewController
 
 @synthesize showReadingListNewBadge = _showReadingListNewBadge;
+@synthesize highlightNewIncognitoTabCell = _highlightNewIncognitoTabCell;
 @synthesize engagementTracker = _engagementTracker;
 @synthesize menuView = _menuView;
 @synthesize isCurrentPageBookmarked = _isCurrentPageBookmarked;
@@ -232,6 +237,8 @@
   self.requestStartTime = configuration.requestStartTime;
   self.showReadingListNewBadge = configuration.showReadingListNewBadge;
   self.engagementTracker = configuration.engagementTracker;
+  self.highlightNewIncognitoTabCell =
+      configuration.highlightNewIncognitoTabCell;
 
   if (configuration.readingListMenuNotifier) {
     _readingListMenuNotifier = configuration.readingListMenuNotifier;
@@ -473,6 +480,9 @@
   [[self readingListCell]
       updateSeenState:_readingListMenuNotifier.readingListUnseenItemsExist
              animated:YES];
+  if (self.highlightNewIncognitoTabCell) {
+    [self triggerNewIncognitoTabCellHighlight];
+  }
 }
 
 - (void)hideContent {
@@ -678,4 +688,33 @@
   [[self readingListCell] updateSeenState:unseenItemsExist animated:YES];
 }
 
+#pragma mark - New Incognito Tab in-product help promotion
+
+- (void)triggerNewIncognitoTabCellHighlight {
+  for (ToolsMenuViewCell* visibleCell in [_menuView visibleCells]) {
+    if ([visibleCell.accessibilityIdentifier
+            isEqualToString:kToolsMenuNewIncognitoTabId]) {
+      // Set the label's background color to be clear so that the highlight is
+      // is not covered by the label.
+      visibleCell.title.backgroundColor = [UIColor clearColor];
+      [UIView animateWithDuration:ios::material::kDuration5
+          delay:0.0
+          options:UIViewAnimationOptionAllowUserInteraction |
+                  UIViewAnimationOptionRepeat |
+                  UIViewAnimationOptionAutoreverse |
+                  UIViewAnimationOptionCurveEaseInOut
+          animations:^{
+            [UIView setAnimationRepeatCount:2];
+            visibleCell.contentView.backgroundColor =
+                [[MDCPalette cr_bluePalette] tint100];
+          }
+          completion:^(BOOL finished) {
+            visibleCell.contentView.backgroundColor = [UIColor whiteColor];
+          }];
+      self.highlightNewIncognitoTabCell = NO;
+      break;
+    }
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm b/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm
index ba37230..5c655c4 100644
--- a/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm
+++ b/ios/chrome/browser/ui/tools_menu/tools_popup_menu_egtest.mm
@@ -53,8 +53,7 @@
         performAction:grey_tap()];
   }
 
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kToolsMenuTableViewId)]
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::ToolsMenuView()]
       assertWithMatcher:grey_notVisible()];
 }
 
diff --git a/ios/chrome/browser/ui/util/OWNERS b/ios/chrome/browser/ui/util/OWNERS
index 1f7a7b8..37eb1aa 100644
--- a/ios/chrome/browser/ui/util/OWNERS
+++ b/ios/chrome/browser/ui/util/OWNERS
@@ -1,2 +1,5 @@
 kkhorimoto@chromium.org
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/voice/OWNERS b/ios/chrome/browser/ui/voice/OWNERS
index 48efb49e..bd85dcf2 100644
--- a/ios/chrome/browser/ui/voice/OWNERS
+++ b/ios/chrome/browser/ui/voice/OWNERS
@@ -1 +1,4 @@
 kkhorimoto@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/webui/OWNERS b/ios/chrome/browser/ui/webui/OWNERS
index be9def2..11231a36 100644
--- a/ios/chrome/browser/ui/webui/OWNERS
+++ b/ios/chrome/browser/ui/webui/OWNERS
@@ -3,3 +3,6 @@
 
 per-file ntp_tiles_internals_ui.*=file://components/ntp_tiles/OWNERS
 per-file popular_sites_internals_ui.*=file://components/ntp_tiles/OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/webui/gcm/OWNERS b/ios/chrome/browser/ui/webui/gcm/OWNERS
index 295d996..2271273 100644
--- a/ios/chrome/browser/ui/webui/gcm/OWNERS
+++ b/ios/chrome/browser/ui/webui/gcm/OWNERS
@@ -4,3 +4,6 @@
 zea@chromium.org
 
 # COMPONENT: Services>CloudMessaging
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/webui/net_export/OWNERS b/ios/chrome/browser/ui/webui/net_export/OWNERS
index be796293..3d058981 100644
--- a/ios/chrome/browser/ui/webui/net_export/OWNERS
+++ b/ios/chrome/browser/ui/webui/net_export/OWNERS
@@ -1,3 +1,6 @@
 file://net/OWNERS
 
 # COMPONENT: Internals>Network>Logging
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/webui/sync_internals/OWNERS b/ios/chrome/browser/ui/webui/sync_internals/OWNERS
index 261ab18..c1575f5 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/OWNERS
+++ b/ios/chrome/browser/ui/webui/sync_internals/OWNERS
@@ -1 +1,4 @@
 file://components/sync/OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/update_client/OWNERS b/ios/chrome/browser/update_client/OWNERS
index 75e2f4d..8d0e865 100644
--- a/ios/chrome/browser/update_client/OWNERS
+++ b/ios/chrome/browser/update_client/OWNERS
@@ -4,3 +4,6 @@
 waffles@chromium.org
 
 # COMPONENT: Internals>Installer>Components
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/voice/OWNERS b/ios/chrome/browser/voice/OWNERS
index cc50037..2f0d436 100644
--- a/ios/chrome/browser/voice/OWNERS
+++ b/ios/chrome/browser/voice/OWNERS
@@ -1,2 +1,5 @@
 kkhorimoto@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/web/OWNERS b/ios/chrome/browser/web/OWNERS
index 5a2923e..1a6c73c 100644
--- a/ios/chrome/browser/web/OWNERS
+++ b/ios/chrome/browser/web/OWNERS
@@ -1,2 +1,5 @@
 eugenebut@chromium.org
 # This directory needs to be carved into subdirs with more specific OWNERS.
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/web/resources/OWNERS b/ios/chrome/browser/web/resources/OWNERS
index da255e6..7b2fb33 100644
--- a/ios/chrome/browser/web/resources/OWNERS
+++ b/ios/chrome/browser/web/resources/OWNERS
@@ -1 +1,4 @@
 per-file payment_request.js=mahmadi@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/web_resource/OWNERS b/ios/chrome/browser/web_resource/OWNERS
index 50c6bcd4..16d5d98c 100644
--- a/ios/chrome/browser/web_resource/OWNERS
+++ b/ios/chrome/browser/web_resource/OWNERS
@@ -1,2 +1,5 @@
 achuith@chromium.org
 rsesek@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/web_state_list/OWNERS b/ios/chrome/browser/web_state_list/OWNERS
index c5cd5cb..39066e1 100644
--- a/ios/chrome/browser/web_state_list/OWNERS
+++ b/ios/chrome/browser/web_state_list/OWNERS
@@ -1 +1,4 @@
 sdefresne@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/common/physical_web/OWNERS b/ios/chrome/common/physical_web/OWNERS
index 3ebff38d..56360bb 100644
--- a/ios/chrome/common/physical_web/OWNERS
+++ b/ios/chrome/common/physical_web/OWNERS
@@ -1 +1,4 @@
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/content_widget_extension/OWNERS b/ios/chrome/content_widget_extension/OWNERS
index 55fb7e49..2b72d62 100644
--- a/ios/chrome/content_widget_extension/OWNERS
+++ b/ios/chrome/content_widget_extension/OWNERS
@@ -1 +1,3 @@
-lod@chromium.org
\ No newline at end of file
+lod@chromium.org
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/search_widget_extension/OWNERS b/ios/chrome/search_widget_extension/OWNERS
index 55fb7e49..2b72d62 100644
--- a/ios/chrome/search_widget_extension/OWNERS
+++ b/ios/chrome/search_widget_extension/OWNERS
@@ -1 +1,3 @@
-lod@chromium.org
\ No newline at end of file
+lod@chromium.org
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/share_extension/OWNERS b/ios/chrome/share_extension/OWNERS
index 3ebff38d..56360bb 100644
--- a/ios/chrome/share_extension/OWNERS
+++ b/ios/chrome/share_extension/OWNERS
@@ -1 +1,4 @@
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 7a7cbe3..cce1532 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -166,6 +166,7 @@
     "//ios/chrome/browser/ui/autofill/cells:unit_tests",
     "//ios/chrome/browser/ui/bookmarks:unit_tests",
     "//ios/chrome/browser/ui/bookmarks/cells:unit_tests",
+    "//ios/chrome/browser/ui/browser_list:unit_tests",
     "//ios/chrome/browser/ui/bubble:unit_tests",
     "//ios/chrome/browser/ui/collection_view:unit_tests",
     "//ios/chrome/browser/ui/collection_view/cells:unit_tests",
@@ -215,7 +216,6 @@
     "//ios/chrome/search_widget_extension:unit_tests",
     "//ios/chrome/test/base:unit_tests",
     "//ios/shared/chrome/browser/ui/broadcaster:unit_tests",
-    "//ios/shared/chrome/browser/ui/browser_list:unit_tests",
     "//ios/shared/chrome/browser/ui/commands:unit_tests",
     "//ios/shared/chrome/browser/ui/coordinators:unit_tests",
     "//ios/shared/chrome/browser/ui/dialogs:unit_tests",
diff --git a/ios/chrome/test/OWNERS b/ios/chrome/test/OWNERS
index 1df316e..6f764fe 100644
--- a/ios/chrome/test/OWNERS
+++ b/ios/chrome/test/OWNERS
@@ -1 +1,4 @@
 baxley@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index bdd90add..c689139e 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -67,7 +67,6 @@
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui:ui_internal",
-    "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/main",
     "//ios/chrome/browser/ui/ntp:ntp_controller",
diff --git a/ios/chrome/test/app/signin_test_util.h b/ios/chrome/test/app/signin_test_util.h
index b60ada3..1f9fcfa3 100644
--- a/ios/chrome/test/app/signin_test_util.h
+++ b/ios/chrome/test/app/signin_test_util.h
@@ -25,10 +25,6 @@
 // the accounts were correctly removed from the keychain.
 bool SignOutAndClearAccounts();
 
-// Resets Sign-in promo impression preferences for bookmarks and settings view,
-// and resets kIosBookmarkPromoAlreadySeen flag for bookmarks.
-void ResetSigninPromoPreferences();
-
 }  // namespace chrome_test_util
 
 #endif  // IOS_CHROME_TEST_APP_SIGNIN_TEST_UTIL_H_
diff --git a/ios/chrome/test/app/signin_test_util.mm b/ios/chrome/test/app/signin_test_util.mm
index 096390c..8fc529e4 100644
--- a/ios/chrome/test/app/signin_test_util.mm
+++ b/ios/chrome/test/app/signin_test_util.mm
@@ -12,12 +12,10 @@
 #include "components/signin/core/common/signin_pref_names.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/signin/account_tracker_service_factory.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/signin/gaia_auth_fetcher_ios.h"
-#import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h"
@@ -174,12 +172,4 @@
   return !identity_service->HasIdentities();
 }
 
-void ResetSigninPromoPreferences() {
-  ios::ChromeBrowserState* browser_state = GetOriginalBrowserState();
-  PrefService* prefs = browser_state->GetPrefs();
-  prefs->SetInteger(prefs::kIosBookmarkSigninPromoDisplayedCount, 0);
-  prefs->SetBoolean(prefs::kIosBookmarkPromoAlreadySeen, false);
-  prefs->SetInteger(prefs::kIosSettingsSigninPromoDisplayedCount, 0);
-}
-
 }  // namespace chrome_test_util
diff --git a/ios/chrome/test/data/OWNERS b/ios/chrome/test/data/OWNERS
index 72e8ffc..d03d28b 100644
--- a/ios/chrome/test/data/OWNERS
+++ b/ios/chrome/test/data/OWNERS
@@ -1 +1,4 @@
 *
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index ceb5f1e..bee6b5d 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -179,12 +179,10 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//components/signin/core/common",
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/authentication:authentication",
-    "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/collection_view/cells",
     "//ios/chrome/browser/ui/commands:commands",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
diff --git a/ios/chrome/test/earl_grey/OWNERS b/ios/chrome/test/earl_grey/OWNERS
index 86cd262..b69f0d0 100644
--- a/ios/chrome/test/earl_grey/OWNERS
+++ b/ios/chrome/test/earl_grey/OWNERS
@@ -1,2 +1,5 @@
 baxley@chromium.org
 liaoyuke@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 0b4e542..adb7a10 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -101,12 +101,12 @@
 // Returns matcher for the settings button in the tools menu.
 id<GREYMatcher> SettingsMenuButton();
 
+// Returns matcher for the tools menu table view.
+id<GREYMatcher> ToolsMenuView();
+
 // Returns matcher for the OK button.
 id<GREYMatcher> OKButton();
 
-// Returns matcher for the sign-in promo view in the settings menu.
-id<GREYMatcher> SignInPromoView();
-
 // Returns matcher for the signin button in the settings menu.
 id<GREYMatcher> SignInMenuButton();
 
@@ -114,6 +114,15 @@
 // settings menu.
 id<GREYMatcher> SettingsAccountButton();
 
+// Returns matcher for the accounts collection view.
+id<GREYMatcher> SettingsAccountsCollectionView();
+
+// Returns matcher for the Import Data cell in switch sync account view.
+id<GREYMatcher> SettingsImportDataImportButton();
+
+// Returns matcher for the Keep Data Separate cell in switch sync account view.
+id<GREYMatcher> SettingsImportDataKeepSeparateButton();
+
 // Returns matcher for the menu button to sync accounts.
 id<GREYMatcher> AccountsSyncButton();
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index addbce3..618018a1 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -12,11 +12,11 @@
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
 #import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/import_data_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #import "ios/chrome/browser/ui/static_content/static_html_view_controller.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_controller.h"
@@ -190,22 +190,34 @@
   return grey_accessibilityID(kToolsMenuSettingsId);
 }
 
+id<GREYMatcher> ToolsMenuView() {
+  return grey_accessibilityID(kToolsMenuTableViewId);
+}
+
 id<GREYMatcher> OKButton() {
   return ButtonWithAccessibilityLabelId(IDS_OK);
 }
 
-id<GREYMatcher> SignInPromoView() {
-  return grey_accessibilityID(kSigninPromoViewId);
-}
-
 id<GREYMatcher> SignInMenuButton() {
-  return grey_accessibilityID(kSigninPromoSecondaryButtonId);
+  return grey_accessibilityID(kSettingsSignInCellId);
 }
 
 id<GREYMatcher> SettingsAccountButton() {
   return grey_accessibilityID(kSettingsAccountCellId);
 }
 
+id<GREYMatcher> SettingsAccountsCollectionView() {
+  return grey_accessibilityID(kSettingsAccountsId);
+}
+
+id<GREYMatcher> SettingsImportDataImportButton() {
+  return grey_accessibilityID(kImportDataImportCellId);
+}
+
+id<GREYMatcher> SettingsImportDataKeepSeparateButton() {
+  return grey_accessibilityID(kImportDataKeepSeparateCellId);
+}
+
 id<GREYMatcher> AccountsSyncButton() {
   return grey_accessibilityID(kSettingsAccountsSyncCellId);
 }
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm
index 4aaf058..6da92b8 100644
--- a/ios/chrome/test/earl_grey/chrome_test_case.mm
+++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -8,10 +8,8 @@
 
 #import <EarlGrey/EarlGrey.h>
 
-#include "base/command_line.h"
 #include "base/mac/scoped_block.h"
 #include "base/strings/sys_string_conversions.h"
-#include "components/signin/core/common/signin_switches.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #include "ios/chrome/test/app/settings_test_util.h"
 #include "ios/chrome/test/app/signin_test_util.h"
@@ -165,7 +163,6 @@
   _isMockAuthenticationDisabled = NO;
   _tearDownHandler = nil;
 
-  chrome_test_util::ResetSigninPromoPreferences();
   chrome_test_util::OpenNewTab();
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
 }
@@ -250,11 +247,6 @@
 }
 
 + (void)enableMockAuthentication {
-  // Enable sign-in promo for all tests.
-  // TODO(crbug.com/739910): Remove this line when the sign-in promo is enabled
-  // by default.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableSigninPromo);
   chrome_test_util::SetUpMockAuthentication();
   chrome_test_util::SetUpMockAccountReconcilor();
   chrome_test_util::SetUpFakeSyncServer();
diff --git a/ios/chrome/today_extension/OWNERS b/ios/chrome/today_extension/OWNERS
index 3ebff38d..56360bb 100644
--- a/ios/chrome/today_extension/OWNERS
+++ b/ios/chrome/today_extension/OWNERS
@@ -1 +1,4 @@
 olivierrobin@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/clean/OWNERS b/ios/clean/OWNERS
index 6d63668..d846ad4fa 100644
--- a/ios/clean/OWNERS
+++ b/ios/clean/OWNERS
@@ -1,3 +1,6 @@
 edchin@chromium.org
 lpromero@chromium.org
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/clean/chrome/app/steps/BUILD.gn b/ios/clean/chrome/app/steps/BUILD.gn
index a9a6b5e..c6640336 100644
--- a/ios/clean/chrome/app/steps/BUILD.gn
+++ b/ios/clean/chrome/app/steps/BUILD.gn
@@ -55,11 +55,11 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/browser_state:browser_state_impl",
     "//ios/chrome/browser/content_settings",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/web:web_internal",
     "//ios/chrome/browser/web_state_list",
     "//ios/clean/chrome/browser/ui/root",
     "//ios/net",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web:web_arc",
   ]
diff --git a/ios/clean/chrome/app/steps/root_coordinator+application_step.mm b/ios/clean/chrome/app/steps/root_coordinator+application_step.mm
index ac306df..29ce960 100644
--- a/ios/clean/chrome/app/steps/root_coordinator+application_step.mm
+++ b/ios/clean/chrome/app/steps/root_coordinator+application_step.mm
@@ -7,13 +7,13 @@
 #include "base/memory/ptr_util.h"
 #include "base/supports_user_data.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
 #import "ios/clean/chrome/app/application_state.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/web_state/web_state.h"
diff --git a/ios/clean/chrome/app/steps/root_coordinator_initializer.mm b/ios/clean/chrome/app/steps/root_coordinator_initializer.mm
index d098a546..0feba6b 100644
--- a/ios/clean/chrome/app/steps/root_coordinator_initializer.mm
+++ b/ios/clean/chrome/app/steps/root_coordinator_initializer.mm
@@ -7,14 +7,14 @@
 #import "ios/chrome/app/startup/provider_registration.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
 #import "ios/clean/chrome/app/steps/step_context.h"
 #import "ios/clean/chrome/app/steps/step_features.h"
 #import "ios/clean/chrome/browser/ui/root/root_coordinator.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #include "ios/web/public/web_state/web_state.h"
 
diff --git a/ios/clean/chrome/browser/ui/bookmarks/BUILD.gn b/ios/clean/chrome/browser/ui/bookmarks/BUILD.gn
index dec4e7694..591d83f 100644
--- a/ios/clean/chrome/browser/ui/bookmarks/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/bookmarks/BUILD.gn
@@ -13,7 +13,7 @@
   deps = [
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/bookmarks",
-    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
   ]
 }
diff --git a/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm
index 2a3a6f0..63b16bc 100644
--- a/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm
@@ -7,8 +7,8 @@
 #import "ios/chrome/browser/ui/bookmarks/bookmark_controller_factory.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_home_handset_view_controller.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #include "ios/chrome/browser/ui/ui_util.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/clean/chrome/browser/ui/context_menu/BUILD.gn b/ios/clean/chrome/browser/ui/context_menu/BUILD.gn
index 75ed85b0..ec87bff 100644
--- a/ios/clean/chrome/browser/ui/context_menu/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/context_menu/BUILD.gn
@@ -16,9 +16,9 @@
 
   deps = [
     ":context_menu_ui",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/web_state_list:web_state_list",
     "//ios/clean/chrome/browser/ui/commands:commands",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
diff --git a/ios/clean/chrome/browser/ui/context_menu/context_menu_mediator.mm b/ios/clean/chrome/browser/ui/context_menu/context_menu_mediator.mm
index fe17c57..2c5d24b 100644
--- a/ios/clean/chrome/browser/ui/context_menu/context_menu_mediator.mm
+++ b/ios/clean/chrome/browser/ui/context_menu/context_menu_mediator.mm
@@ -4,10 +4,10 @@
 
 #import "ios/clean/chrome/browser/ui/context_menu/context_menu_mediator.h"
 
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/clean/chrome/browser/ui/commands/context_menu_commands.h"
 #import "ios/clean/chrome/browser/ui/context_menu/context_menu_consumer.h"
 #import "ios/clean/chrome/browser/ui/context_menu/context_menu_context_impl.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/web/public/url_scheme_util.h"
 #include "url/gurl.h"
 
diff --git a/ios/clean/chrome/browser/ui/context_menu/web_context_menu_coordinator.mm b/ios/clean/chrome/browser/ui/context_menu/web_context_menu_coordinator.mm
index a86ea59..253777b 100644
--- a/ios/clean/chrome/browser/ui/context_menu/web_context_menu_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/context_menu/web_context_menu_coordinator.mm
@@ -5,11 +5,11 @@
 #import "ios/clean/chrome/browser/ui/context_menu/web_context_menu_coordinator.h"
 
 #include "base/logging.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/clean/chrome/browser/ui/commands/context_menu_commands.h"
 #import "ios/clean/chrome/browser/ui/context_menu/context_menu_context_impl.h"
 #import "ios/clean/chrome/browser/ui/context_menu/context_menu_mediator.h"
 #import "ios/clean/chrome/browser/ui/context_menu/context_menu_view_controller.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
diff --git a/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn b/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn
index dccbfe0..918c16d 100644
--- a/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/find_in_page/BUILD.gn
@@ -16,11 +16,11 @@
     ":find_in_page_ui",
     "//base",
     "//ios/chrome/browser/find_in_page",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/web_state_list",
     "//ios/clean/chrome/browser",
     "//ios/clean/chrome/browser/ui/actions",
     "//ios/clean/chrome/browser/ui/commands",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
diff --git a/ios/clean/chrome/browser/ui/find_in_page/find_in_page_coordinator.mm b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_coordinator.mm
index 14acfc1..01ec12e 100644
--- a/ios/clean/chrome/browser/ui/find_in_page/find_in_page_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/find_in_page/find_in_page_coordinator.mm
@@ -8,11 +8,11 @@
 
 #include "base/logging.h"
 #import "ios/chrome/browser/find_in_page/find_tab_helper.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/clean/chrome/browser/ui/commands/find_in_page_visibility_commands.h"
 #import "ios/clean/chrome/browser/ui/find_in_page/find_in_page_mediator.h"
 #import "ios/clean/chrome/browser/ui/find_in_page/find_in_page_view_controller.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
diff --git a/ios/clean/chrome/browser/ui/ntp/BUILD.gn b/ios/clean/chrome/browser/ui/ntp/BUILD.gn
index a774196f..d1972ca 100644
--- a/ios/clean/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/ntp/BUILD.gn
@@ -32,6 +32,7 @@
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/alert_coordinator",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/content_suggestions",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_util",
     "//ios/chrome/browser/ui/content_suggestions/cells",
@@ -42,7 +43,6 @@
     "//ios/clean/chrome/browser/ui/bookmarks",
     "//ios/clean/chrome/browser/ui/commands",
     "//ios/clean/chrome/browser/ui/recent_tabs",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web:web_arc",
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.mm
index 3380b4e..365ab19 100644
--- a/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.mm
@@ -5,6 +5,7 @@
 #import "ios/clean/chrome/browser/ui/ntp/ntp_coordinator.h"
 
 #include "base/logging.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.h"
 #import "ios/clean/chrome/browser/ui/commands/ntp_commands.h"
@@ -12,7 +13,6 @@
 #import "ios/clean/chrome/browser/ui/ntp/ntp_mediator.h"
 #import "ios/clean/chrome/browser/ui/ntp/ntp_view_controller.h"
 #import "ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
index ce336ed..19a27c76 100644
--- a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
@@ -20,6 +20,7 @@
 #include "ios/chrome/browser/ntp_tiles/ios_most_visited_sites_factory.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_gesture_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
@@ -38,7 +39,6 @@
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/clean/chrome/browser/ui/ntp/ntp_home_header_coordinator.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h"
diff --git a/ios/clean/chrome/browser/ui/omnibox/BUILD.gn b/ios/clean/chrome/browser/ui/omnibox/BUILD.gn
index 44ddf1a96..1b75b9f 100644
--- a/ios/clean/chrome/browser/ui/omnibox/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/omnibox/BUILD.gn
@@ -14,11 +14,11 @@
     "//base",
     "//components/toolbar",
     "//ios/chrome/browser/ssl",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/web_state_list",
     "//ios/clean/chrome/browser",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/shared/chrome/browser/ui/omnibox",
     "//ios/web",
diff --git a/ios/clean/chrome/browser/ui/omnibox/location_bar_coordinator.mm b/ios/clean/chrome/browser/ui/omnibox/location_bar_coordinator.mm
index 2880d51..4e5ab50 100644
--- a/ios/clean/chrome/browser/ui/omnibox/location_bar_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/omnibox/location_bar_coordinator.mm
@@ -5,11 +5,11 @@
 #import "ios/clean/chrome/browser/ui/omnibox/location_bar_coordinator.h"
 
 #include "base/memory/ptr_util.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/omnibox/location_bar_controller_impl.h"
 #include "ios/chrome/browser/ui/toolbar/toolbar_model_ios.h"
 #import "ios/clean/chrome/browser/ui/omnibox/location_bar_mediator.h"
 #import "ios/clean/chrome/browser/ui/omnibox/location_bar_view_controller.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/clean/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/clean/chrome/browser/ui/recent_tabs/BUILD.gn
index 0ebc0e4..0fc25c9 100644
--- a/ios/clean/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -12,8 +12,8 @@
 
   deps = [
     "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/ntp/recent_tabs",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
   ]
 }
diff --git a/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm
index 12ca148..d7bd8ceb 100644
--- a/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm
@@ -4,10 +4,10 @@
 
 #import "ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
 
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.h"
 #import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_view_controller.h"
 #include "ios/chrome/browser/ui/ui_util.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/clean/chrome/browser/ui/root/BUILD.gn b/ios/clean/chrome/browser/ui/root/BUILD.gn
index d499ecd..c9c6460 100644
--- a/ios/clean/chrome/browser/ui/root/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/root/BUILD.gn
@@ -9,10 +9,10 @@
   ]
   deps = [
     ":root_ui",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/clean/chrome/browser",
     "//ios/clean/chrome/browser/ui/commands",
     "//ios/clean/chrome/browser/ui/tab_grid",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
   ]
diff --git a/ios/clean/chrome/browser/ui/root/root_coordinator.mm b/ios/clean/chrome/browser/ui/root/root_coordinator.mm
index 289e2700..12de61a 100644
--- a/ios/clean/chrome/browser/ui/root/root_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/root/root_coordinator.mm
@@ -8,9 +8,9 @@
 #error "This file requires ARC support."
 #endif
 
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/clean/chrome/browser/ui/root/root_container_view_controller.h"
 #import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/clean/chrome/browser/ui/settings/BUILD.gn b/ios/clean/chrome/browser/ui/settings/BUILD.gn
index fae4fa0..ddace90 100644
--- a/ios/clean/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/settings/BUILD.gn
@@ -17,10 +17,10 @@
   deps = [
     "//base:base",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/settings",
     "//ios/clean/chrome/browser/ui/actions",
     "//ios/clean/chrome/browser/ui/commands",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
   ]
@@ -37,8 +37,8 @@
   deps = [
     ":settings",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/settings",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators:test_support",
     "//testing/gtest",
   ]
diff --git a/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm b/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
index 8194051..26958214 100644
--- a/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
@@ -6,10 +6,10 @@
 
 #include "base/logging.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/clean/chrome/browser/ui/commands/settings_commands.h"
 #import "ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 
diff --git a/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.mm b/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.mm
index e89125d..f8cb709f 100644
--- a/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.mm
@@ -6,9 +6,9 @@
 
 #include "base/logging.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #import "ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/shared/chrome/browser/ui/settings/settings_main_page_commands.h"
diff --git a/ios/clean/chrome/browser/ui/tab/BUILD.gn b/ios/clean/chrome/browser/ui/tab/BUILD.gn
index 5bc2d72..d04dfb4 100644
--- a/ios/clean/chrome/browser/ui/tab/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tab/BUILD.gn
@@ -16,6 +16,7 @@
     ":tab_ui",
     "//base",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/web_state_list",
     "//ios/clean/chrome/browser/ui/actions",
     "//ios/clean/chrome/browser/ui/commands",
@@ -26,7 +27,6 @@
     "//ios/clean/chrome/browser/ui/transitions",
     "//ios/clean/chrome/browser/ui/web_contents",
     "//ios/shared/chrome/browser/ui/broadcaster",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
@@ -64,8 +64,8 @@
     ":tab",
     ":tab_ui",
     "//base",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/toolbar/test",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/shared/chrome/browser/ui/coordinators:test_support",
     "//ios/shared/chrome/browser/ui/tab:test_support",
diff --git a/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm b/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
index a29ecb7b..01e9139c 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/scoped_observer.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
 #import "ios/clean/chrome/browser/ui/commands/tab_commands.h"
@@ -23,7 +24,6 @@
 #import "ios/clean/chrome/browser/ui/transitions/zoom_transition_controller.h"
 #import "ios/clean/chrome/browser/ui/web_contents/web_coordinator.h"
 #import "ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/web/public/web_state/web_state.h"
diff --git a/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm
index 5ac4ca8a..5b4f1fe 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm
+++ b/ios/clean/chrome/browser/ui/tab/tab_coordinator_unittest.mm
@@ -7,9 +7,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/toolbar/test/toolbar_test_web_state.h"
 #import "ios/clean/chrome/browser/ui/tab/tab_container_view_controller.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator_test.h"
 #import "ios/shared/chrome/browser/ui/tab/tab_test_util.h"
diff --git a/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn b/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn
index cb12b1a..9d2b93cf9 100644
--- a/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn
@@ -17,6 +17,7 @@
     "//base",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/snapshots",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/tools_menu:configuration",
     "//ios/chrome/browser/web_state_list",
     "//ios/clean/chrome/browser",
@@ -27,7 +28,6 @@
     "//ios/clean/chrome/browser/ui/tab_collection",
     "//ios/clean/chrome/browser/ui/tab_collection:tab_collection_ui",
     "//ios/clean/chrome/browser/ui/tools",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
diff --git a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
index fe0a209..cb9f9e77 100644
--- a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -10,6 +10,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
@@ -23,7 +24,6 @@
 #import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator.h"
 #import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_view_controller.h"
 #import "ios/clean/chrome/browser/ui/tools/tools_coordinator.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/web/public/navigation_manager.h"
diff --git a/ios/clean/chrome/browser/ui/tab_strip/BUILD.gn b/ios/clean/chrome/browser/ui/tab_strip/BUILD.gn
index e75f5f6..074ebaf 100644
--- a/ios/clean/chrome/browser/ui/tab_strip/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tab_strip/BUILD.gn
@@ -10,10 +10,10 @@
   deps = [
     ":tab_strip_ui",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/web_state_list",
     "//ios/clean/chrome/browser/ui/commands",
     "//ios/clean/chrome/browser/ui/tab_collection",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
diff --git a/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm b/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm
index 8478ce5..0f7eb69 100644
--- a/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.mm
@@ -4,12 +4,12 @@
 
 #import "ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.h"
 
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/clean/chrome/browser/ui/commands/tab_grid_commands.h"
 #import "ios/clean/chrome/browser/ui/commands/tab_strip_commands.h"
 #import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_mediator.h"
 #import "ios/clean/chrome/browser/ui/tab_strip/tab_strip_view_controller.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #include "ios/web/public/web_state/web_state.h"
 
diff --git a/ios/clean/chrome/browser/ui/toolbar/BUILD.gn b/ios/clean/chrome/browser/ui/toolbar/BUILD.gn
index 0184951..e517ef7 100644
--- a/ios/clean/chrome/browser/ui/toolbar/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/toolbar/BUILD.gn
@@ -16,13 +16,13 @@
     ":toolbar_ui",
     "//base",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/tools_menu:configuration",
     "//ios/chrome/browser/web_state_list",
     "//ios/clean/chrome/browser/ui/commands",
     "//ios/clean/chrome/browser/ui/omnibox",
     "//ios/clean/chrome/browser/ui/tools",
     "//ios/shared/chrome/browser/ui/broadcaster",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
diff --git a/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm b/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
index e48a074..50079c6 100644
--- a/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.mm
@@ -5,6 +5,7 @@
 #import "ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.h"
 
 #include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_configuration.h"
 #import "ios/clean/chrome/browser/ui/commands/tools_menu_commands.h"
 #import "ios/clean/chrome/browser/ui/omnibox/location_bar_coordinator.h"
@@ -12,7 +13,6 @@
 #import "ios/clean/chrome/browser/ui/toolbar/toolbar_view_controller.h"
 #import "ios/clean/chrome/browser/ui/tools/tools_coordinator.h"
 #import "ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/web/public/web_state/web_state.h"
diff --git a/ios/clean/chrome/browser/ui/tools/BUILD.gn b/ios/clean/chrome/browser/ui/tools/BUILD.gn
index c9ddf772..d9c569bf 100644
--- a/ios/clean/chrome/browser/ui/tools/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tools/BUILD.gn
@@ -16,10 +16,10 @@
   deps = [
     ":tools_ui",
     "//base",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/chrome/browser/ui/tools_menu:configuration",
     "//ios/clean/chrome/browser/ui/commands",
     "//ios/clean/chrome/browser/ui/transitions",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
     "//ui/base",
diff --git a/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm b/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
index 0251ceba..7610df3 100644
--- a/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tools/tools_coordinator.mm
@@ -4,10 +4,10 @@
 
 #import "ios/clean/chrome/browser/ui/tools/tools_coordinator.h"
 
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/clean/chrome/browser/ui/tools/menu_view_controller.h"
 #import "ios/clean/chrome/browser/ui/tools/tools_mediator.h"
 #import "ios/clean/chrome/browser/ui/transitions/zooming_menu_transition_controller.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/clean/chrome/browser/ui/web_contents/BUILD.gn b/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
index 7cc6f98..36ae90d 100644
--- a/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
@@ -15,9 +15,9 @@
   deps = [
     ":web_contents_ui",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/ui/browser_list",
     "//ios/clean/chrome/browser/ui/commands",
     "//ios/clean/chrome/browser/ui/context_menu",
-    "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
@@ -40,6 +40,7 @@
   testonly = true
   sources = [
     "web_contents_mediator_unittest.mm",
+    "web_coordinator_unittest.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -50,6 +51,7 @@
     "//base",
     "//base/test:test_support",
     "//ios/chrome/test/base",
+    "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/shared/chrome/browser/ui/tab:test_support",
     "//ios/web/public/test/fakes",
     "//testing/gtest",
diff --git a/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm b/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm
index db3ac94..a7ea49c 100644
--- a/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/web_contents/web_coordinator.mm
@@ -6,12 +6,12 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/memory/ptr_util.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/clean/chrome/browser/ui/commands/context_menu_commands.h"
 #import "ios/clean/chrome/browser/ui/context_menu/context_menu_context_impl.h"
 #import "ios/clean/chrome/browser/ui/context_menu/web_context_menu_coordinator.h"
 #import "ios/clean/chrome/browser/ui/web_contents/web_contents_mediator.h"
 #import "ios/clean/chrome/browser/ui/web_contents/web_contents_view_controller.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #include "ios/web/public/navigation_manager.h"
diff --git a/ios/clean/chrome/browser/ui/web_contents/web_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/web_contents/web_coordinator_unittest.mm
new file mode 100644
index 0000000..2abeebc
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/web_contents/web_coordinator_unittest.mm
@@ -0,0 +1,63 @@
+// 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.
+
+#import "ios/clean/chrome/browser/ui/web_contents/web_coordinator.h"
+
+#include "base/memory/ptr_util.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+#import "ios/shared/chrome/browser/ui/tab/tab_test_util.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+class WebCoordinatorTest : public PlatformTest {
+ public:
+  WebCoordinatorTest() {
+    auto navigation_manager = base::MakeUnique<TabNavigationManager>();
+    navigation_manager->SetItemCount(0);
+    test_web_state_.SetView([[UIView alloc] init]);
+    test_web_state_.SetNavigationManager(std::move(navigation_manager));
+
+    coordinator_ = [[WebCoordinator alloc] init];
+  }
+
+ protected:
+  WebCoordinator* coordinator_;
+  web::TestWebState test_web_state_;
+};
+
+// Tests that starting without the webstate still creates a view controller.
+TEST_F(WebCoordinatorTest, TestStartWithoutWebState) {
+  EXPECT_EQ(coordinator_.viewController, nil);
+  [coordinator_ start];
+  EXPECT_NE(coordinator_.viewController, nil);
+}
+
+// Tests that starting with the webstate puts the webstate's view in the VC.
+TEST_F(WebCoordinatorTest, TestStartWithWebState) {
+  coordinator_.webState = &test_web_state_;
+  [coordinator_ start];
+  EXPECT_TRUE([test_web_state_.GetView()
+      isDescendantOfView:coordinator_.viewController.view]);
+}
+
+// Tests that starting and then setting the webstate puts the webstate's view in
+// the VC.
+TEST_F(WebCoordinatorTest, TestStartThenSetWebState) {
+  [coordinator_ start];
+  EXPECT_FALSE([test_web_state_.GetView()
+      isDescendantOfView:coordinator_.viewController.view]);
+  coordinator_.webState = &test_web_state_;
+  EXPECT_TRUE([test_web_state_.GetView()
+      isDescendantOfView:coordinator_.viewController.view]);
+}
+
+}  // namespace
diff --git a/ios/net/OWNERS b/ios/net/OWNERS
index 13444820..706c5f3 100644
--- a/ios/net/OWNERS
+++ b/ios/net/OWNERS
@@ -1,3 +1,6 @@
 droger@chromium.org
 ellyjones@chromium.org
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/public/consumer/OWNERS b/ios/public/consumer/OWNERS
index ae82009..ba9c65db 100644
--- a/ios/public/consumer/OWNERS
+++ b/ios/public/consumer/OWNERS
@@ -1,2 +1,5 @@
 rohitrao@chromium.org
 sdefresne@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/public/provider/OWNERS b/ios/public/provider/OWNERS
index ae82009..ba9c65db 100644
--- a/ios/public/provider/OWNERS
+++ b/ios/public/provider/OWNERS
@@ -1,2 +1,5 @@
 rohitrao@chromium.org
 sdefresne@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/public/provider/chrome/browser/signin/OWNERS b/ios/public/provider/chrome/browser/signin/OWNERS
index d45210b..6068057 100644
--- a/ios/public/provider/chrome/browser/signin/OWNERS
+++ b/ios/public/provider/chrome/browser/signin/OWNERS
@@ -2,3 +2,6 @@
 msarda@chromium.org
 
 # COMPONENT: Services>SignIn
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/shared/OWNERS b/ios/shared/OWNERS
index b8d3d41f..290f78a 100644
--- a/ios/shared/OWNERS
+++ b/ios/shared/OWNERS
@@ -1,3 +1,6 @@
 lpromero@chromium.org
 marq@chromium.org
 sdefresne@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/shared/chrome/browser/ui/DEPS b/ios/shared/chrome/browser/ui/DEPS
index eb4f1fa..661df6b 100644
--- a/ios/shared/chrome/browser/ui/DEPS
+++ b/ios/shared/chrome/browser/ui/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
-  # Allow ios/chrome/browser but not ios/chrome/browser/ui.
+  # Allow ios/chrome/browser.
   "+ios/chrome/browser",
-  "-ios/chrome/browser/ui",
 
   "+components/omnibox/browser",
 ]
diff --git a/ios/shared/chrome/browser/ui/coordinators/BUILD.gn b/ios/shared/chrome/browser/ui/coordinators/BUILD.gn
index 01805bb..ad36878 100644
--- a/ios/shared/chrome/browser/ui/coordinators/BUILD.gn
+++ b/ios/shared/chrome/browser/ui/coordinators/BUILD.gn
@@ -17,7 +17,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/tabs:tabs_internal",
-    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/ui/browser_list",
   ]
 }
 
@@ -34,7 +34,7 @@
     ":coordinators",
     "//base",
     "//ios/chrome/browser/browser_state:test_support",
-    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/ui/browser_list",
     "//testing/gtest",
   ]
 }
diff --git a/ios/shared/chrome/browser/ui/coordinators/browser_coordinator_test.mm b/ios/shared/chrome/browser/ui/coordinators/browser_coordinator_test.mm
index 5f4cb62a..4d54d95 100644
--- a/ios/shared/chrome/browser/ui/coordinators/browser_coordinator_test.mm
+++ b/ios/shared/chrome/browser/ui/coordinators/browser_coordinator_test.mm
@@ -6,7 +6,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/shared/chrome/browser/ui/dialogs/OWNERS b/ios/shared/chrome/browser/ui/dialogs/OWNERS
index 48efb49e..bd85dcf2 100644
--- a/ios/shared/chrome/browser/ui/dialogs/OWNERS
+++ b/ios/shared/chrome/browser/ui/dialogs/OWNERS
@@ -1 +1,4 @@
 kkhorimoto@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/shared/chrome/browser/ui/settings/BUILD.gn b/ios/shared/chrome/browser/ui/settings/BUILD.gn
index ac535bc..4d5db7a 100644
--- a/ios/shared/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/shared/chrome/browser/ui/settings/BUILD.gn
@@ -8,13 +8,4 @@
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
-
-  deps = [
-    # "//base",
-    # "//ios/chrome/app:tests_fake_hook",
-    # "//ios/chrome/browser",
-    # "//ios/chrome/browser/browser_state",
-    # "//ios/chrome/browser/tabs:tabs_internal",
-    # "//ios/shared/chrome/browser/ui/browser_list",
-  ]
 }
diff --git a/ios/showcase/OWNERS b/ios/showcase/OWNERS
index 411d967..aa8e854 100644
--- a/ios/showcase/OWNERS
+++ b/ios/showcase/OWNERS
@@ -1,2 +1,5 @@
 edchin@chromium.org
 lpromero@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/showcase/content_suggestions/OWNERS b/ios/showcase/content_suggestions/OWNERS
index 2d35f0a..f192143 100644
--- a/ios/showcase/content_suggestions/OWNERS
+++ b/ios/showcase/content_suggestions/OWNERS
@@ -1 +1,4 @@
 gambard@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/testing/earl_grey/OWNERS b/ios/testing/earl_grey/OWNERS
index 1df316e..6f764fe 100644
--- a/ios/testing/earl_grey/OWNERS
+++ b/ios/testing/earl_grey/OWNERS
@@ -1 +1,4 @@
 baxley@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/blink/OWNERS b/ios/third_party/blink/OWNERS
index 40a68c7..b127701 100644
--- a/ios/third_party/blink/OWNERS
+++ b/ios/third_party/blink/OWNERS
@@ -1 +1,4 @@
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/earl_grey/OWNERS b/ios/third_party/earl_grey/OWNERS
index 3bb1f30..f1cc9a0 100644
--- a/ios/third_party/earl_grey/OWNERS
+++ b/ios/third_party/earl_grey/OWNERS
@@ -1,3 +1,6 @@
 baxley@chromium.org
 justincohen@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/fishhook/OWNERS b/ios/third_party/fishhook/OWNERS
index 3bb1f30..f1cc9a0 100644
--- a/ios/third_party/fishhook/OWNERS
+++ b/ios/third_party/fishhook/OWNERS
@@ -1,3 +1,6 @@
 baxley@chromium.org
 justincohen@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/material_components_ios/OWNERS b/ios/third_party/material_components_ios/OWNERS
index 2b842fe..1e909408 100644
--- a/ios/third_party/material_components_ios/OWNERS
+++ b/ios/third_party/material_components_ios/OWNERS
@@ -4,3 +4,6 @@
 # These are for the common case of adding or renaming files. If you're doing
 # structural changes, please get a review from an OWNER.
 per-file BUILD.gn=*
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index 5c68a80..69d4294 100644
--- a/ios/third_party/material_components_ios/README.chromium
+++ b/ios/third_party/material_components_ios/README.chromium
@@ -1,7 +1,7 @@
 Name: Material Components for iOS
 URL: https://github.com/material-components/material-components-ios
 Version: 0
-Revision: c2a357fb67321e9660190c55b49836c51fe04821
+Revision: a0466df249c593ba65007440d7a84e4665ac2631
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/ios/third_party/material_font_disk_loader_ios/OWNERS b/ios/third_party/material_font_disk_loader_ios/OWNERS
index 077a06e..c984591 100644
--- a/ios/third_party/material_font_disk_loader_ios/OWNERS
+++ b/ios/third_party/material_font_disk_loader_ios/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/material_roboto_font_loader_ios/OWNERS b/ios/third_party/material_roboto_font_loader_ios/OWNERS
index 077a06e..c984591 100644
--- a/ios/third_party/material_roboto_font_loader_ios/OWNERS
+++ b/ios/third_party/material_roboto_font_loader_ios/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/material_sprited_animation_view_ios/OWNERS b/ios/third_party/material_sprited_animation_view_ios/OWNERS
index 077a06e..c984591 100644
--- a/ios/third_party/material_sprited_animation_view_ios/OWNERS
+++ b/ios/third_party/material_sprited_animation_view_ios/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/material_text_accessibility_ios/OWNERS b/ios/third_party/material_text_accessibility_ios/OWNERS
index 077a06e..c984591 100644
--- a/ios/third_party/material_text_accessibility_ios/OWNERS
+++ b/ios/third_party/material_text_accessibility_ios/OWNERS
@@ -1,2 +1,5 @@
 lpromero@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/third_party/ochamcrest/OWNERS b/ios/third_party/ochamcrest/OWNERS
index 3bb1f30..f1cc9a0 100644
--- a/ios/third_party/ochamcrest/OWNERS
+++ b/ios/third_party/ochamcrest/OWNERS
@@ -1,3 +1,6 @@
 baxley@chromium.org
 justincohen@chromium.org
 rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/OWNERS b/ios/web/OWNERS
index 2710e7a..a7206ed 100644
--- a/ios/web/OWNERS
+++ b/ios/web/OWNERS
@@ -1,3 +1,6 @@
 eugenebut@chromium.org
 kkhorimoto@chromium.org
 marq@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/payments/OWNERS b/ios/web/payments/OWNERS
index f189e142..6e417fc 100644
--- a/ios/web/payments/OWNERS
+++ b/ios/web/payments/OWNERS
@@ -1,2 +1,5 @@
 mahmadi@chromium.org
 lpromero@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/payments/payment_request.cc b/ios/web/payments/payment_request.cc
index c535f9d7..63ff82d 100644
--- a/ios/web/payments/payment_request.cc
+++ b/ios/web/payments/payment_request.cc
@@ -59,6 +59,9 @@
 
 namespace web {
 
+const char kPaymentRequestIDExternal[] = "payment-request-id";
+const char kPaymentRequestDataExternal[] = "payment-request-data";
+
 PaymentCurrencyAmount::PaymentCurrencyAmount()
     // By default, the currency is defined by [ISO4217]. For example, USD for
     // US Dollars.
diff --git a/ios/web/public/OWNERS b/ios/web/public/OWNERS
index 08850f4..75983fd 100644
--- a/ios/web/public/OWNERS
+++ b/ios/web/public/OWNERS
@@ -1,2 +1,5 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/public/app/mojo/OWNERS b/ios/web/public/app/mojo/OWNERS
index 1c9493c3..77df058 100644
--- a/ios/web/public/app/mojo/OWNERS
+++ b/ios/web/public/app/mojo/OWNERS
@@ -1,2 +1,5 @@
 per-file *.json=set noparent
 per-file *.json=file://ipc/SECURITY_OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/public/payments/OWNERS b/ios/web/public/payments/OWNERS
index f189e142..6e417fc 100644
--- a/ios/web/public/payments/OWNERS
+++ b/ios/web/public/payments/OWNERS
@@ -1,2 +1,5 @@
 mahmadi@chromium.org
 lpromero@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/public/payments/payment_request.h b/ios/web/public/payments/payment_request.h
index 62c3ab6..a5353b1c 100644
--- a/ios/web/public/payments/payment_request.h
+++ b/ios/web/public/payments/payment_request.h
@@ -25,6 +25,11 @@
 
 namespace web {
 
+// These constants are for Univesral Link query parameters when receiving
+// payment response data from an external application.
+extern const char kPaymentRequestIDExternal[];
+extern const char kPaymentRequestDataExternal[];
+
 // Supplies monetary amounts.
 class PaymentCurrencyAmount {
  public:
diff --git a/ios/web/public/test/OWNERS b/ios/web/public/test/OWNERS
index c3cd7d2..a4af3ca 100644
--- a/ios/web/public/test/OWNERS
+++ b/ios/web/public/test/OWNERS
@@ -1,2 +1,5 @@
 baxley@chromium.org
 eugenebut@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/public/user_agent.mm b/ios/web/public/user_agent.mm
index 10b16fb..ac2a4dd 100644
--- a/ios/web/public/user_agent.mm
+++ b/ios/web/public/user_agent.mm
@@ -45,6 +45,7 @@
   // Safari version can't be, so a lookup table is used instead (for both, since
   // the reported versions should stay in sync).
   static const OSVersionMap version_map[] = {
+      {11, 0, {"604.1", "604.1.34"}},
       {10, 3, {"602.1", "603.1.30"}},
       {10, 0, {"602.1", "602.1.50"}},
       {9, 0, {"601.1.46", "601.1"}},
diff --git a/ios/web/public/webui/OWNERS b/ios/web/public/webui/OWNERS
index a01ab6c..f75309d 100644
--- a/ios/web/public/webui/OWNERS
+++ b/ios/web/public/webui/OWNERS
@@ -1,2 +1,5 @@
 eugenebut@chromium.org
 michaeldo@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/shell/OWNERS b/ios/web/shell/OWNERS
index d0f3b7e..fd32f2c 100644
--- a/ios/web/shell/OWNERS
+++ b/ios/web/shell/OWNERS
@@ -3,3 +3,6 @@
 
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/shell/test/OWNERS b/ios/web/shell/test/OWNERS
index 1df316e..6f764fe 100644
--- a/ios/web/shell/test/OWNERS
+++ b/ios/web/shell/test/OWNERS
@@ -1 +1,4 @@
 baxley@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index 2493e20..8619a60 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -196,12 +196,6 @@
 // only checks on creation, such that the whole object needs to be rebuilt.
 - (void)requirePageReconstruction;
 
-// Requires that the next display reload the page, using a placeholder while
-// loading. This could be used, e.g., to handle a crash in a WebController that
-// is not currently visible.
-// TODO(stuartmorgan): When revisiting the methods above, revisit this as well.
-- (void)requirePageReload;
-
 // Show overlay, don't reload web page. Used when the view will be
 // visible only briefly (e.g., tablet side swipe).
 - (void)setOverlayPreviewMode:(BOOL)overlayPreviewMode;
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index e9617da..19335f89 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -320,9 +320,6 @@
   base::scoped_nsobject<NSMutableArray> _webViewToolbars;
   // Flag to say if browsing is enabled.
   BOOL _webUsageEnabled;
-  // The next time the view is requested, reload the page (using the placeholder
-  // overlay until it's loaded).
-  BOOL _requireReloadOnDisplay;
   // Overlay view used instead of webView.
   base::scoped_nsobject<UIImageView> _placeholderOverlayView;
   // The touch tracking recognizer allowing us to decide if a navigation is
@@ -1111,10 +1108,6 @@
   [self removeWebView];
 }
 
-- (void)requirePageReload {
-  _requireReloadOnDisplay = YES;
-}
-
 - (void)resetContainerView {
   [_containerView removeFromSuperview];
   _containerView.reset();
@@ -1967,10 +1960,6 @@
     // then transition away.
     if (_overlayPreviewMode && !isChromeScheme)
       [self addPlaceholderOverlay];
-  } else if (_requireReloadOnDisplay && _webView) {
-    _requireReloadOnDisplay = NO;
-    [self addPlaceholderOverlay];
-    [self loadCurrentURL];
   }
 }
 
diff --git a/ios/web/webui/web_ui_mojo_inttest.mm b/ios/web/webui/web_ui_mojo_inttest.mm
index d90e2333..0c4dc8f 100644
--- a/ios/web/webui/web_ui_mojo_inttest.mm
+++ b/ios/web/webui/web_ui_mojo_inttest.mm
@@ -174,13 +174,7 @@
 // Tests that JS can send messages to the native code and vice versa.
 // TestUIHandler is used for communication and test succeeds only when
 // |TestUIHandler| successfully receives "ack" message from WebUI page.
-#if TARGET_IPHONE_SIMULATOR
-#define MAYBE_MessageExchange MessageExchange
-#else
-#define MAYBE_MessageExchange FLAKY_MessageExchange
-#endif
-// TODO(crbug.com/720098): Enable this test on device.
-TEST_F(WebUIMojoTest, MAYBE_MessageExchange) {
+TEST_F(WebUIMojoTest, MessageExchange) {
   @autoreleasepool {
     web_state()->SetWebUsageEnabled(true);
     web_state()->GetView();  // WebState won't load URL without view.
diff --git a/ios/web_view/OWNERS b/ios/web_view/OWNERS
index d008751..c3456e9 100644
--- a/ios/web_view/OWNERS
+++ b/ios/web_view/OWNERS
@@ -7,4 +7,6 @@
 # structural changes, please get a review from an OWNER.
 per-file BUILD.gn=*
 
-# COMPONENT: Mobile>iOSWebView
\ No newline at end of file
+# COMPONENT: Mobile>iOSWebView
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index afd0e3c..4ea67bbd 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -172,6 +172,15 @@
     if (!mojo::IsValidInterfaceId(id))
       return mojo::ScopedInterfaceEndpointHandle();
 
+    // Unless it is the master ID, |id| is from the remote side and therefore
+    // its namespace bit is supposed to be different than the value that this
+    // router would use.
+    if (!mojo::IsMasterInterfaceId(id) &&
+        set_interface_id_namespace_bit_ ==
+            mojo::HasInterfaceIdNamespaceBitSet(id)) {
+      return mojo::ScopedInterfaceEndpointHandle();
+    }
+
     base::AutoLock locker(lock_);
     bool inserted = false;
     Endpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index 1423afa..b39ae20 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -34,6 +34,7 @@
     // Executed whenever an error occurs except when the error occurs during
     // Start/Seek/Resume or Suspend. Those errors are reported via |seek_cb|
     // and |suspend_cb| respectively.
+    // NOTE: The client is responsible for calling Pipeline::Stop().
     virtual void OnError(PipelineStatus status) = 0;
 
     // Executed whenever the media reaches the end.
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index ecf1574..2028019 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -222,8 +222,8 @@
     std::unique_ptr<TextRenderer> text_renderer,
     base::WeakPtr<PipelineImpl> weak_pipeline) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
-  DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: "
-                              << state_;
+  DCHECK(state_ == kCreated || state_ == kStopped)
+      << "Received start in unexpected state: " << state_;
 
   SetState(kStarting);
 
@@ -232,7 +232,6 @@
   DCHECK(!text_renderer_);
   DCHECK(!renderer_ended_);
   DCHECK(!text_renderer_ended_);
-  DCHECK(!weak_pipeline_);
   demuxer_ = demuxer;
   {
     base::AutoLock auto_lock(shared_state_lock_);
@@ -1275,18 +1274,16 @@
   // Else report error via the client interface.
   if (!seek_cb_.is_null()) {
     base::ResetAndReturn(&seek_cb_).Run(error);
-  } else if (!suspend_cb_.is_null()) {
-    base::ResetAndReturn(&suspend_cb_).Run(error);
-  } else {
-    DCHECK(client_);
-    client_->OnError(error);
+    return;
   }
 
-  // Any kind of error stops the pipeline.
-  //
-  // TODO (tguilbert): Move this out to PipelineController to make the state
-  // changes more consistent. See crbug.com/695734.
-  Stop();
+  if (!suspend_cb_.is_null()) {
+    base::ResetAndReturn(&suspend_cb_).Run(error);
+    return;
+  }
+
+  DCHECK(client_);
+  client_->OnError(error);
 }
 
 void PipelineImpl::OnEnded() {
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index e5c9de3..73ee91f 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -29,7 +29,7 @@
 // Here's a state diagram that describes the lifetime of this object.
 //
 //   [ *Created ]                       [ Any State ]
-//         | Start()                         | Stop() / SetError()
+//         | Start()                         | Stop()
 //         V                                 V
 //   [ Starting ]                       [ Stopping ]
 //         |                                 |
@@ -55,9 +55,9 @@
 // a chance to preroll. From then on the normal Seek() transitions are carried
 // out and we start playing the media.
 //
-// If any error ever happens, this object will transition to the "Error" state
-// from any state. If Stop() is ever called, this object will transition to
-// "Stopped" state.
+// If Stop() is ever called, this object will transition to "Stopped" state.
+// Pipeline::Stop() is never called from withing PipelineImpl. It's |client_|'s
+// responsibility to call stop when appropriate.
 //
 // TODO(sandersd): It should be possible to pass through Suspended when going
 // from InitDemuxer to InitRenderer, thereby eliminating the Resuming state.
diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc
index 8e81bd2d..9db941b9 100644
--- a/media/base/pipeline_impl_unittest.cc
+++ b/media/base/pipeline_impl_unittest.cc
@@ -37,8 +37,6 @@
 using ::testing::CreateFunctor;
 using ::testing::DeleteArg;
 using ::testing::DoAll;
-// TODO(scherkus): Remove InSequence after refactoring Pipeline.
-using ::testing::InSequence;
 using ::testing::Invoke;
 using ::testing::InvokeWithoutArgs;
 using ::testing::Mock;
@@ -424,7 +422,6 @@
 TEST_F(PipelineImplTest, NoStreams) {
   EXPECT_CALL(*demuxer_, Initialize(_, _, _))
       .WillOnce(PostCallback<1>(PIPELINE_OK));
-  EXPECT_CALL(*demuxer_, Stop());
   EXPECT_CALL(callbacks_, OnMetadata(_));
 
   StartPipelineAndExpect(PIPELINE_ERROR_COULD_NOT_RENDER);
@@ -536,8 +533,9 @@
   // Initialize then seek!
   StartPipelineAndExpect(PIPELINE_OK);
 
+  // Pipeline::Client is supposed to call Pipeline::Stop() after errors.
+  EXPECT_CALL(callbacks_, OnError(_)).WillOnce(Stop(pipeline_.get()));
   EXPECT_CALL(*demuxer_, Stop());
-  EXPECT_CALL(callbacks_, OnError(_));
   OnDemuxerError();
   base::RunLoop().RunUntilIdle();
 
@@ -701,7 +699,8 @@
 
   pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek,
                                         base::Unretained(&callbacks_)));
-  EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ));
+  EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ))
+      .WillOnce(Stop(pipeline_.get()));
   base::RunLoop().RunUntilIdle();
 }
 
@@ -754,7 +753,8 @@
 
   pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek,
                                         base::Unretained(&callbacks_)));
-  EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ));
+  EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ))
+      .WillOnce(Stop(pipeline_.get()));
   base::RunLoop().RunUntilIdle();
 }
 
@@ -934,7 +934,8 @@
       } else {
         EXPECT_CALL(*demuxer_, Initialize(_, _, _))
             .WillOnce(PostCallback<1>(DEMUXER_ERROR_COULD_NOT_OPEN));
-        EXPECT_CALL(callbacks_, OnStart(DEMUXER_ERROR_COULD_NOT_OPEN));
+        EXPECT_CALL(callbacks_, OnStart(DEMUXER_ERROR_COULD_NOT_OPEN))
+            .WillOnce(Stop(pipeline_.get()));
       }
 
       EXPECT_CALL(*demuxer_, Stop());
@@ -957,7 +958,8 @@
       } else {
         EXPECT_CALL(*renderer_, Initialize(_, _, _))
             .WillOnce(PostCallback<2>(PIPELINE_ERROR_INITIALIZATION_FAILED));
-        EXPECT_CALL(callbacks_, OnStart(PIPELINE_ERROR_INITIALIZATION_FAILED));
+        EXPECT_CALL(callbacks_, OnStart(PIPELINE_ERROR_INITIALIZATION_FAILED))
+            .WillOnce(Stop(pipeline_.get()));
       }
 
       EXPECT_CALL(callbacks_, OnMetadata(_));
@@ -1008,7 +1010,8 @@
                 SetError(&renderer_client_, PIPELINE_ERROR_READ),
                 RunClosure<0>()));
         EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
-        EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ));
+        EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ))
+            .WillOnce(Stop(pipeline_.get()));
       }
       return;
     }
@@ -1028,7 +1031,8 @@
       } else {
         EXPECT_CALL(*demuxer_, Seek(_, _))
             .WillOnce(RunCallback<1>(PIPELINE_ERROR_READ));
-        EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ));
+        EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ))
+            .WillOnce(Stop(pipeline_.get()));
       }
       return;
     }
@@ -1068,7 +1072,8 @@
       } else {
         EXPECT_CALL(*demuxer_, Seek(_, _))
             .WillOnce(RunCallback<1>(PIPELINE_ERROR_READ));
-        EXPECT_CALL(callbacks_, OnResume(PIPELINE_ERROR_READ));
+        EXPECT_CALL(callbacks_, OnResume(PIPELINE_ERROR_READ))
+            .WillOnce(Stop(pipeline_.get()));
       }
     } else if (state != kSuspended && state != kSuspending) {
       NOTREACHED() << "State not supported: " << state;
@@ -1085,7 +1090,8 @@
       case kError:
         if (expect_errors) {
           EXPECT_CALL(*demuxer_, Stop());
-          EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ));
+          EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ))
+              .WillOnce(Stop(pipeline_.get()));
         }
         renderer_client_->OnError(PIPELINE_ERROR_READ);
         break;
diff --git a/media/blink/buffered_data_source_host_impl.cc b/media/blink/buffered_data_source_host_impl.cc
index 8569558..1b4f872 100644
--- a/media/blink/buffered_data_source_host_impl.cc
+++ b/media/blink/buffered_data_source_host_impl.cc
@@ -57,9 +57,12 @@
 void BufferedDataSourceHostImpl::AddBufferedByteRange(int64_t start,
                                                       int64_t end) {
   int64_t new_bytes = UnloadedBytesInInterval(Interval<int64_t>(start, end));
-  if (new_bytes > 0)
-    did_loading_progress_ = true;
+  if (new_bytes == 0) {
+    // No change
+    return;
+  }
   buffered_byte_ranges_.SetInterval(start, end, 1);
+  did_loading_progress_ = true;
 
   base::TimeTicks now = tick_clock_->NowTicks();
   int64_t bytes_so_far = 0;
diff --git a/media/blink/multibuffer_data_source.cc b/media/blink/multibuffer_data_source.cc
index 3f29a1c..d793c69 100644
--- a/media/blink/multibuffer_data_source.cc
+++ b/media/blink/multibuffer_data_source.cc
@@ -116,7 +116,7 @@
       stop_signal_received_(false),
       media_has_played_(false),
       single_origin_(true),
-      cancel_on_defer_(true),
+      cancel_on_defer_(false),
       preload_(AUTO),
       bitrate_(0),
       playback_rate_(0.0),
@@ -414,7 +414,6 @@
 }
 
 void MultibufferDataSource::StopInternal_Locked() {
-  DVLOG(1) << __func__;
   lock_.AssertAcquired();
   if (stop_signal_received_)
     return;
@@ -444,7 +443,6 @@
 /////////////////////////////////////////////////////////////////////////////
 // BufferedResourceLoader callback methods.
 void MultibufferDataSource::StartCallback() {
-  DVLOG(1) << __func__;
   DCHECK(render_task_runner_->BelongsToCurrentThread());
 
   if (init_cb_.is_null()) {
@@ -495,8 +493,6 @@
     media_log_->SetBooleanProperty("range_header_supported",
                                    url_data_->range_supported());
   }
-  if (!url_data_->range_supported())
-    cancel_on_defer_ = false;
 
   render_task_runner_->PostTask(
       FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success));
@@ -540,8 +536,7 @@
     bool loading = is_loading || force_loading;
 
     if (!loading && cancel_on_defer_) {
-      DVLOG(2) << "Cancel on defer";
-      if (read_op_ || !init_cb_.is_null()) {
+      if (read_op_) {
         // We can't destroy the reader if a read operation is pending.
         // UpdateLoadingState_Locked will be called again when the read
         // operation is done.
diff --git a/media/blink/multibuffer_data_source_unittest.cc b/media/blink/multibuffer_data_source_unittest.cc
index a7c2b9c..b71b1f2 100644
--- a/media/blink/multibuffer_data_source_unittest.cc
+++ b/media/blink/multibuffer_data_source_unittest.cc
@@ -390,21 +390,18 @@
   }
 
   void CheckCapacityDefer() {
-    if (loader()) {
-      EXPECT_EQ(2 << 20, preload_low());
-      EXPECT_EQ(3 << 20, preload_high());
-    } else {
-      EXPECT_EQ(preload(), MultibufferDataSource::AUTO);
-    }
+    EXPECT_EQ(2 << 20, preload_low());
+    EXPECT_EQ(3 << 20, preload_high());
   }
 
   void CheckReadThenDefer() {
-    if (loader()) {
-      EXPECT_EQ(0, preload_low());
-      EXPECT_EQ(0, preload_high());
-    } else {
-      EXPECT_EQ(preload(), MultibufferDataSource::METADATA);
-    }
+    EXPECT_EQ(0, preload_low());
+    EXPECT_EQ(0, preload_high());
+  }
+
+  void CheckNeverDefer() {
+    EXPECT_EQ(1LL << 40, preload_low());
+    EXPECT_EQ(1LL << 40, preload_high());
   }
 
   // Accessors for private variables on |data_source_|.
@@ -1189,20 +1186,15 @@
   set_preload(MultibufferDataSource::METADATA);
   InitializeWith206Response();
 
-  data_source_->MediaIsPlaying();
-  data_source_->SetPreload(MultibufferDataSource::METADATA);
+  EXPECT_EQ(MultibufferDataSource::METADATA, preload());
   EXPECT_FALSE(is_local_source());
   EXPECT_TRUE(data_source_->range_supported());
   CheckReadThenDefer();
 
-  // Read next block. (Needed to start up the loader again.)
+  // Read a bit from the beginning.
   EXPECT_CALL(*this, ReadCallback(kDataSize));
-  ReadAt(kDataSize);
-  Respond(response_generator_->Generate206(kDataSize));
-  EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize * 2));
-  ReceiveData(kDataSize);
+  ReadAt(0);
 
-  // After reading, we should be in a deferred state.
   ASSERT_TRUE(active_loader());
   EXPECT_TRUE(data_provider()->deferred());
 }
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 68b41bc..545b5c7 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -938,16 +938,6 @@
   if (highest_ready_state_ >= ReadyState::kReadyStateHaveFutureData)
     return false;
 
-  // To suspend before we reach kReadyStateHaveCurrentData is only ok
-  // if we know we're going to get woken up when we get more data, which
-  // will only happen if the network is in the "Loading" state.
-  // This happens when the network is fast, but multiple videos are loading
-  // and multiplexing gets held up waiting for available threads.
-  if (highest_ready_state_ <= ReadyState::kReadyStateHaveMetadata &&
-      network_state_ != WebMediaPlayer::kNetworkStateLoading) {
-    return true;
-  }
-
   if (preroll_attempt_pending_)
     return true;
 
@@ -1333,6 +1323,9 @@
     SetNetworkState(PipelineErrorToNetworkState(status));
   }
 
+  // PipelineController::Stop() is idempotent.
+  pipeline_controller_.Stop();
+
   UpdatePlayState();
 }
 
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 014a2d3..116f8f1e 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -270,9 +270,6 @@
   }
 
  protected:
-  void SetNetworkState(blink::WebMediaPlayer::NetworkState state) {
-    wmpi_->SetNetworkState(state);
-  }
   void SetReadyState(blink::WebMediaPlayer::ReadyState state) {
     wmpi_->SetReadyState(state);
   }
@@ -424,9 +421,11 @@
   EXPECT_FALSE(IsSuspended());
 }
 
-TEST_F(WebMediaPlayerImplTest, IdleSuspendBeforeLoadingBegins) {
+TEST_F(WebMediaPlayerImplTest, IdleSuspendIsEnabledBeforeLoadingBegins) {
   InitializeWebMediaPlayerImpl();
-  EXPECT_FALSE(delegate_.ExpireForTesting());
+  EXPECT_TRUE(delegate_.ExpireForTesting());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(IsSuspended());
 }
 
 TEST_F(WebMediaPlayerImplTest,
@@ -446,7 +445,6 @@
 
 TEST_F(WebMediaPlayerImplTest, IdleSuspendIsEnabledIfLoadingHasStalled) {
   InitializeWebMediaPlayerImpl();
-  SetNetworkState(blink::WebMediaPlayer::kNetworkStateLoading);
   base::SimpleTestTickClock* clock = new base::SimpleTestTickClock();
   clock->Advance(base::TimeDelta::FromSeconds(1));
   SetTickClock(clock);
@@ -462,7 +460,6 @@
 TEST_F(WebMediaPlayerImplTest, DidLoadingProgressTriggersResume) {
   // Same setup as IdleSuspendIsEnabledBeforeLoadingBegins.
   InitializeWebMediaPlayerImpl();
-  SetNetworkState(blink::WebMediaPlayer::kNetworkStateLoading);
   EXPECT_TRUE(delegate_.ExpireForTesting());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(IsSuspended());
diff --git a/media/capture/video/video_capture_system_impl.cc b/media/capture/video/video_capture_system_impl.cc
index da8f119d..8e18420 100644
--- a/media/capture/video/video_capture_system_impl.cc
+++ b/media/capture/video/video_capture_system_impl.cc
@@ -9,41 +9,36 @@
 namespace {
 
 // Compares two VideoCaptureFormat by checking smallest frame_size area, then
-// by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
-// the first entry for a given resolution has the largest frame rate, as needed
-// by the ConsolidateCaptureFormats() method.
+// by width, and then by _largest_ frame_rate. Used to order a
+// VideoCaptureFormats vector so that the first entry for a given resolution has
+// the largest frame rate.
 bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
                             const media::VideoCaptureFormat& format2) {
   DCHECK(format1.frame_size.GetCheckedArea().IsValid());
   DCHECK(format2.frame_size.GetCheckedArea().IsValid());
   if (format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
       format2.frame_size.GetCheckedArea().ValueOrDefault(0)) {
-    return format1.frame_rate > format2.frame_rate;
+    if (format1.frame_size.width() == format2.frame_size.width()) {
+      return format1.frame_rate > format2.frame_rate;
+    }
+    return format1.frame_size.width() < format2.frame_size.width();
   }
   return format1.frame_size.GetCheckedArea().ValueOrDefault(0) <
          format2.frame_size.GetCheckedArea().ValueOrDefault(0);
 }
 
-bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
-                              const media::VideoCaptureFormat& format2) {
-  DCHECK(format1.frame_size.GetCheckedArea().IsValid());
-  DCHECK(format2.frame_size.GetCheckedArea().IsValid());
-  return format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
-         format2.frame_size.GetCheckedArea().ValueOrDefault(0);
+bool IsCaptureFormatEqual(const media::VideoCaptureFormat& format1,
+                          const media::VideoCaptureFormat& format2) {
+  return format1.frame_size == format2.frame_size &&
+         format1.frame_rate == format2.frame_rate &&
+         format1.pixel_format == format2.pixel_format;
 }
 
-// This function receives a list of capture formats, removes duplicated
-// resolutions while keeping the highest frame rate for each, and forcing I420
-// pixel format.
+// This function receives a list of capture formats, sets all of them to I420
+// (while keeping Y16 as is), and then removes duplicates.
 void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
   if (formats->empty())
     return;
-  std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
-  // Due to the ordering imposed, the largest frame_rate is kept while removing
-  // duplicated resolutions.
-  media::VideoCaptureFormats::iterator last =
-      std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
-  formats->erase(last, formats->end());
   // Mark all formats as I420, since this is what the renderer side will get
   // anyhow: the actual pixel format is decided at the device level.
   // Don't do this for Y16 format as it is handled separatelly.
@@ -51,6 +46,11 @@
     if (format.pixel_format != media::PIXEL_FORMAT_Y16)
       format.pixel_format = media::PIXEL_FORMAT_I420;
   }
+  std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
+  // Remove duplicates
+  media::VideoCaptureFormats::iterator last =
+      std::unique(formats->begin(), formats->end(), IsCaptureFormatEqual);
+  formats->erase(last, formats->end());
 }
 
 }  // anonymous namespace
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index c044c52..18fa3681 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -245,88 +245,13 @@
                                                 VideoCaptureFormats* formats) {
   DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for "
            << descriptor.display_name;
-  ScopedComPtr<ICreateDevEnum> dev_enum;
-  HRESULT hr = ::CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
-                                  IID_PPV_ARGS(&dev_enum));
-  if (FAILED(hr))
-    return;
-
-  ScopedComPtr<IEnumMoniker> enum_moniker;
-  hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
-                                       enum_moniker.GetAddressOf(), 0);
-  // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera
-  // exists. Therefore the FAILED macro can't be used.
-  if (hr != S_OK)
-    return;
-
-  // Walk the capture devices. No need to check for device presence again since
-  // that is anyway needed in GetDeviceFilter(). "google camera adapter" and old
-  // VFW devices are already skipped previously in GetDeviceNames() enumeration.
-  base::win::ScopedComPtr<IBaseFilter> capture_filter;
-  hr = VideoCaptureDeviceWin::GetDeviceFilter(descriptor.device_id,
-                                              capture_filter.GetAddressOf());
-  if (!capture_filter.Get()) {
-    DLOG(ERROR) << "Failed to create capture filter: "
-                << logging::SystemErrorCodeToString(hr);
-    return;
-  }
-
-  base::win::ScopedComPtr<IPin> output_capture_pin(
-      VideoCaptureDeviceWin::GetPin(capture_filter.Get(), PINDIR_OUTPUT,
-                                    PIN_CATEGORY_CAPTURE, GUID_NULL));
-  if (!output_capture_pin.Get()) {
-    DLOG(ERROR) << "Failed to get capture output pin";
-    return;
-  }
-
-  ScopedComPtr<IAMStreamConfig> stream_config;
-  hr = output_capture_pin.CopyTo(stream_config.GetAddressOf());
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "Failed to get IAMStreamConfig interface from "
-                   "capture device: " << logging::SystemErrorCodeToString(hr);
-    return;
-  }
-
-  int count = 0, size = 0;
-  hr = stream_config->GetNumberOfCapabilities(&count, &size);
-  if (FAILED(hr)) {
-    DLOG(ERROR) << "GetNumberOfCapabilities failed: "
-                << logging::SystemErrorCodeToString(hr);
-    return;
-  }
-
-  std::unique_ptr<BYTE[]> caps(new BYTE[size]);
-  for (int i = 0; i < count; ++i) {
-    VideoCaptureDeviceWin::ScopedMediaType media_type;
-    hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get());
-    // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
-    // macros here since they'll trigger incorrectly.
-    if (hr != S_OK || !media_type.get()) {
-      DLOG(ERROR) << "GetStreamCaps failed: "
-                  << logging::SystemErrorCodeToString(hr);
-      return;
-    }
-
-    if (media_type->majortype == MEDIATYPE_Video &&
-        media_type->formattype == FORMAT_VideoInfo) {
-      VideoCaptureFormat format;
-      format.pixel_format =
-          VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat(
-              media_type->subtype);
-      if (format.pixel_format == PIXEL_FORMAT_UNKNOWN)
-        continue;
-      VIDEOINFOHEADER* h =
-          reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
-      format.frame_size.SetSize(h->bmiHeader.biWidth, h->bmiHeader.biHeight);
-      // Trust the frame rate from the VIDEOINFOHEADER.
-      format.frame_rate =
-          (h->AvgTimePerFrame > 0)
-              ? kSecondsToReferenceTime / static_cast<float>(h->AvgTimePerFrame)
-              : 0.0f;
-      formats->push_back(format);
-      DVLOG(1) << descriptor.display_name << " "
-               << VideoCaptureFormat::ToString(format);
-    }
+  CapabilityList capability_list;
+  VideoCaptureDeviceWin::GetDeviceCapabilityList(descriptor.device_id,
+                                                 &capability_list);
+  for (const auto& entry : capability_list) {
+    formats->emplace_back(entry.supported_format);
+    DVLOG(1) << descriptor.display_name << " "
+             << VideoCaptureFormat::ToString(entry.supported_format);
   }
 }
 
diff --git a/media/capture/video/win/video_capture_device_win.cc b/media/capture/video/win/video_capture_device_win.cc
index b1c90bc..1c010fe 100644
--- a/media/capture/video/win/video_capture_device_win.cc
+++ b/media/capture/video/win/video_capture_device_win.cc
@@ -101,6 +101,120 @@
   return control_range;
 }
 
+// static
+void VideoCaptureDeviceWin::GetDeviceCapabilityList(
+    const std::string& device_id,
+    CapabilityList* out_capability_list) {
+  base::win::ScopedComPtr<IBaseFilter> capture_filter;
+  HRESULT hr = VideoCaptureDeviceWin::GetDeviceFilter(
+      device_id, capture_filter.GetAddressOf());
+  if (!capture_filter.Get()) {
+    DLOG(ERROR) << "Failed to create capture filter: "
+                << logging::SystemErrorCodeToString(hr);
+    return;
+  }
+
+  base::win::ScopedComPtr<IPin> output_capture_pin(
+      VideoCaptureDeviceWin::GetPin(capture_filter.Get(), PINDIR_OUTPUT,
+                                    PIN_CATEGORY_CAPTURE, GUID_NULL));
+  if (!output_capture_pin.Get()) {
+    DLOG(ERROR) << "Failed to get capture output pin";
+    return;
+  }
+
+  GetPinCapabilityList(capture_filter, output_capture_pin, out_capability_list);
+}
+
+// static
+void VideoCaptureDeviceWin::GetPinCapabilityList(
+    base::win::ScopedComPtr<IBaseFilter> capture_filter,
+    base::win::ScopedComPtr<IPin> output_capture_pin,
+    CapabilityList* out_capability_list) {
+  ScopedComPtr<IAMStreamConfig> stream_config;
+  HRESULT hr = output_capture_pin.CopyTo(stream_config.GetAddressOf());
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "Failed to get IAMStreamConfig interface from "
+                   "capture device: "
+                << logging::SystemErrorCodeToString(hr);
+    return;
+  }
+
+  // Get interface used for getting the frame rate.
+  ScopedComPtr<IAMVideoControl> video_control;
+  hr = capture_filter.CopyTo(video_control.GetAddressOf());
+
+  int count = 0, size = 0;
+  hr = stream_config->GetNumberOfCapabilities(&count, &size);
+  if (FAILED(hr)) {
+    DLOG(ERROR) << "GetNumberOfCapabilities failed: "
+                << logging::SystemErrorCodeToString(hr);
+    return;
+  }
+
+  std::unique_ptr<BYTE[]> caps(new BYTE[size]);
+  for (int i = 0; i < count; ++i) {
+    VideoCaptureDeviceWin::ScopedMediaType media_type;
+    hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get());
+    // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
+    // macros here since they'll trigger incorrectly.
+    if (hr != S_OK || !media_type.get()) {
+      DLOG(ERROR) << "GetStreamCaps failed: "
+                  << logging::SystemErrorCodeToString(hr);
+      return;
+    }
+
+    if (media_type->majortype == MEDIATYPE_Video &&
+        media_type->formattype == FORMAT_VideoInfo) {
+      VideoCaptureFormat format;
+      format.pixel_format =
+          VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat(
+              media_type->subtype);
+      if (format.pixel_format == PIXEL_FORMAT_UNKNOWN)
+        continue;
+      VIDEOINFOHEADER* h =
+          reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
+      format.frame_size.SetSize(h->bmiHeader.biWidth, h->bmiHeader.biHeight);
+
+      // Try to get a better |time_per_frame| from IAMVideoControl. If not, use
+      // the value from VIDEOINFOHEADER.
+      std::vector<float> frame_rates;
+      if (video_control.Get()) {
+        ScopedCoMem<LONGLONG> time_per_frame_list;
+        LONG list_size = 0;
+        const SIZE size = {format.frame_size.width(),
+                           format.frame_size.height()};
+        hr = video_control->GetFrameRateList(output_capture_pin.Get(), i, size,
+                                             &list_size, &time_per_frame_list);
+        // Sometimes |list_size| will be > 0, but time_per_frame_list will be
+        // NULL. Some drivers may return an HRESULT of S_FALSE which SUCCEEDED()
+        // translates into success, so explicitly check S_OK.
+        // See http://crbug.com/306237.
+        if (hr == S_OK && list_size > 0 && time_per_frame_list) {
+          for (int k = 0; k < list_size; k++) {
+            LONGLONG time_per_frame = *(time_per_frame_list + k);
+            if (time_per_frame <= 0)
+              continue;
+            frame_rates.push_back(kSecondsToReferenceTime /
+                                  static_cast<float>(time_per_frame));
+          }
+        }
+      }
+
+      if (frame_rates.empty() && h->AvgTimePerFrame > 0) {
+        frame_rates.push_back(kSecondsToReferenceTime /
+                              static_cast<float>(h->AvgTimePerFrame));
+      }
+      if (frame_rates.empty())
+        frame_rates.push_back(0.0f);
+
+      for (const auto& frame_rate : frame_rates) {
+        format.frame_rate = frame_rate;
+        out_capability_list->emplace_back(i, format, h->bmiHeader);
+      }
+    }
+  }
+}
+
 // Finds and creates a DirectShow Video Capture filter matching the |device_id|.
 // static
 HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id,
@@ -746,77 +860,7 @@
 
 bool VideoCaptureDeviceWin::CreateCapabilityMap() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  ScopedComPtr<IAMStreamConfig> stream_config;
-  HRESULT hr = output_capture_pin_.CopyTo(stream_config.GetAddressOf());
-  DLOG_IF_FAILED_WITH_HRESULT(
-      "Failed to get IAMStreamConfig from capture device", hr);
-  if (FAILED(hr))
-    return false;
-
-  // Get interface used for getting the frame rate.
-  ScopedComPtr<IAMVideoControl> video_control;
-  hr = capture_filter_.CopyTo(video_control.GetAddressOf());
-
-  int count = 0, size = 0;
-  hr = stream_config->GetNumberOfCapabilities(&count, &size);
-  DLOG_IF_FAILED_WITH_HRESULT("Failed to GetNumberOfCapabilities", hr);
-  if (FAILED(hr))
-    return false;
-
-  std::unique_ptr<BYTE[]> caps(new BYTE[size]);
-  for (int stream_index = 0; stream_index < count; ++stream_index) {
-    ScopedMediaType media_type;
-    hr = stream_config->GetStreamCaps(stream_index, media_type.Receive(),
-                                      caps.get());
-    // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
-    // macros here since they'll trigger incorrectly.
-    if (hr != S_OK) {
-      DLOG(ERROR) << "Failed to GetStreamCaps";
-      return false;
-    }
-
-    if (media_type->majortype == MEDIATYPE_Video &&
-        media_type->formattype == FORMAT_VideoInfo) {
-      VideoCaptureFormat format;
-      format.pixel_format =
-          TranslateMediaSubtypeToPixelFormat(media_type->subtype);
-      if (format.pixel_format == PIXEL_FORMAT_UNKNOWN)
-        continue;
-
-      VIDEOINFOHEADER* h =
-          reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
-      format.frame_size.SetSize(h->bmiHeader.biWidth, h->bmiHeader.biHeight);
-
-      // Try to get a better |time_per_frame| from IAMVideoControl. If not, use
-      // the value from VIDEOINFOHEADER.
-      REFERENCE_TIME time_per_frame = h->AvgTimePerFrame;
-      if (video_control.Get()) {
-        ScopedCoMem<LONGLONG> max_fps;
-        LONG list_size = 0;
-        const SIZE size = {format.frame_size.width(),
-                           format.frame_size.height()};
-        hr = video_control->GetFrameRateList(output_capture_pin_.Get(),
-                                             stream_index, size, &list_size,
-                                             &max_fps);
-        // Can't assume the first value will return the max fps.
-        // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some
-        // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates
-        // into success, so explicitly check S_OK. See http://crbug.com/306237.
-        if (hr == S_OK && list_size > 0 && max_fps) {
-          time_per_frame =
-              *std::min_element(max_fps.get(), max_fps.get() + list_size);
-        }
-      }
-
-      format.frame_rate =
-          (time_per_frame > 0)
-              ? (kSecondsToReferenceTime / static_cast<float>(time_per_frame))
-              : 0.0;
-
-      capabilities_.emplace_back(stream_index, format, h->bmiHeader);
-    }
-  }
-
+  GetPinCapabilityList(capture_filter_, output_capture_pin_, &capabilities_);
   return !capabilities_.empty();
 }
 
diff --git a/media/capture/video/win/video_capture_device_win.h b/media/capture/video/win/video_capture_device_win.h
index 7ae6a710..facee7e3 100644
--- a/media/capture/video/win/video_capture_device_win.h
+++ b/media/capture/video/win/video_capture_device_win.h
@@ -56,6 +56,12 @@
     AM_MEDIA_TYPE* media_type_;
   };
 
+  static void GetDeviceCapabilityList(const std::string& device_id,
+                                      CapabilityList* out_capability_list);
+  static void GetPinCapabilityList(
+      base::win::ScopedComPtr<IBaseFilter> capture_filter,
+      base::win::ScopedComPtr<IPin> output_capture_pin,
+      CapabilityList* out_capability_list);
   static HRESULT GetDeviceFilter(const std::string& device_id,
                                  IBaseFilter** filter);
   static base::win::ScopedComPtr<IPin> GetPin(IBaseFilter* filter,
diff --git a/media/filters/pipeline_controller.cc b/media/filters/pipeline_controller.cc
index e5d0245..aad3e4f 100644
--- a/media/filters/pipeline_controller.cc
+++ b/media/filters/pipeline_controller.cc
@@ -45,7 +45,7 @@
                                bool is_streaming,
                                bool is_static) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(state_ == State::CREATED);
+  DCHECK_EQ(state_, State::STOPPED);
   DCHECK(demuxer);
 
   // Once the pipeline is started, we want to call the seeked callback but
@@ -247,12 +247,18 @@
 }
 
 void PipelineController::Stop() {
-  // For the moment, Stop() is only called on WMPI destruction, and updating the
-  // state of |this| is not relevant. Eventually, Start()/Stop() will be called
-  // in order to swap between demuxer types, and this will need to be adressed.
-  //
-  // TODO(tguilbert): Clarify the appropriate state changes when Stop() is
-  // called. See crbug.com/695734.
+  if (state_ == State::STOPPED)
+    return;
+
+  demuxer_ = nullptr;
+  waiting_for_seek_ = false;
+  pending_seeked_cb_ = false;
+  pending_time_updated_ = false;
+  pending_seek_ = false;
+  pending_suspend_ = false;
+  pending_resume_ = false;
+  state_ = State::STOPPED;
+
   pipeline_->Stop();
 }
 
diff --git a/media/filters/pipeline_controller.h b/media/filters/pipeline_controller.h
index 79cb14b..e8fce79 100644
--- a/media/filters/pipeline_controller.h
+++ b/media/filters/pipeline_controller.h
@@ -30,7 +30,7 @@
 class MEDIA_EXPORT PipelineController {
  public:
   enum class State {
-    CREATED,
+    STOPPED,
     STARTING,
     PLAYING,
     SEEKING,
@@ -169,7 +169,7 @@
   bool is_static_ = true;
 
   // Tracks the current state of |pipeline_|.
-  State state_ = State::CREATED;
+  State state_ = State::STOPPED;
 
   // Indicates that a seek has occurred. When set, a seeked callback will be
   // issued at the next stable state.
diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
index 0394ce47..61c6b2e 100644
--- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -434,53 +434,58 @@
       return;
     }
     for (size_t j = 0; j < textures_per_buffer_; j++) {
-      gpu::gles2::TextureRef* texture_ref =
-          texture_manager->GetTexture(buffer_texture_ids[j]);
-      if (!texture_ref) {
+      GLuint service_id = 0;
+      if (!command_decoder->GetServiceTextureId(buffer_texture_ids[j],
+                                                &service_id)) {
         DLOG(ERROR) << "Failed to find texture id " << buffer_texture_ids[j];
         NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT);
         return;
       }
-      gpu::gles2::Texture* info = texture_ref->texture();
-      if (info->target() != texture_target_) {
-        DLOG(ERROR) << "Texture target mismatch for texture id "
-                    << buffer_texture_ids[j];
-        NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT);
-        return;
-      }
-      if (texture_target_ == GL_TEXTURE_EXTERNAL_OES ||
-          texture_target_ == GL_TEXTURE_RECTANGLE_ARB) {
-        // These textures have their dimensions defined by the underlying
-        // storage.
-        // Use |texture_dimensions_| for this size.
-        texture_manager->SetLevelInfo(texture_ref, texture_target_, 0, GL_RGBA,
-                                      texture_dimensions_.width(),
-                                      texture_dimensions_.height(), 1, 0,
-                                      GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
-      } else {
-        // For other targets, texture dimensions should already be defined.
-        GLsizei width = 0, height = 0;
-        info->GetLevelSize(texture_target_, 0, &width, &height, nullptr);
-        if (width != texture_dimensions_.width() ||
-            height != texture_dimensions_.height()) {
-          DLOG(ERROR) << "Size mismatch for texture id "
+
+      gpu::gles2::TextureRef* texture_ref =
+          texture_manager->GetTexture(buffer_texture_ids[j]);
+      if (texture_ref) {
+        gpu::gles2::Texture* info = texture_ref->texture();
+        if (info->target() != texture_target_) {
+          DLOG(ERROR) << "Texture target mismatch for texture id "
                       << buffer_texture_ids[j];
           NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT);
           return;
         }
+        if (texture_target_ == GL_TEXTURE_EXTERNAL_OES ||
+            texture_target_ == GL_TEXTURE_RECTANGLE_ARB) {
+          // These textures have their dimensions defined by the underlying
+          // storage.
+          // Use |texture_dimensions_| for this size.
+          texture_manager->SetLevelInfo(texture_ref, texture_target_, 0,
+                                        GL_RGBA, texture_dimensions_.width(),
+                                        texture_dimensions_.height(), 1, 0,
+                                        GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
+        } else {
+          // For other targets, texture dimensions should already be defined.
+          GLsizei width = 0, height = 0;
+          info->GetLevelSize(texture_target_, 0, &width, &height, nullptr);
+          if (width != texture_dimensions_.width() ||
+              height != texture_dimensions_.height()) {
+            DLOG(ERROR) << "Size mismatch for texture id "
+                        << buffer_texture_ids[j];
+            NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT);
+            return;
+          }
 
-        // TODO(dshwang): after moving to D3D11, remove this. crbug.com/438691
-        GLenum format =
-            video_decode_accelerator_.get()->GetSurfaceInternalFormat();
-        if (format != GL_RGBA) {
-          DCHECK(format == GL_BGRA_EXT);
-          texture_manager->SetLevelInfo(texture_ref, texture_target_, 0, format,
-                                        width, height, 1, 0, format,
-                                        GL_UNSIGNED_BYTE, gfx::Rect());
+          // TODO(dshwang): after moving to D3D11, remove this. crbug.com/438691
+          GLenum format =
+              video_decode_accelerator_.get()->GetSurfaceInternalFormat();
+          if (format != GL_RGBA) {
+            DCHECK(format == GL_BGRA_EXT);
+            texture_manager->SetLevelInfo(texture_ref, texture_target_, 0,
+                                          format, width, height, 1, 0, format,
+                                          GL_UNSIGNED_BYTE, gfx::Rect());
+          }
         }
+        current_textures.push_back(texture_ref);
       }
-      service_ids.push_back(texture_ref->service_id());
-      current_textures.push_back(texture_ref);
+      service_ids.push_back(service_id);
     }
     textures.push_back(current_textures);
     buffers.push_back(PictureBuffer(buffer_ids[i], texture_dimensions_,
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index def6618..3418530 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -557,35 +557,62 @@
 
   // Paint the first frame if possible and necessary. Paint ahead of
   // HAVE_ENOUGH_DATA to ensure the user sees the frame as early as possible.
+  bool just_painted_first_frame = false;
   if (!sink_started_ && algorithm_->frames_queued() && !painted_first_frame_) {
     // We want to paint the first frame under two conditions: Either (1) we have
     // enough frames to know it's definitely the first frame or (2) there may be
     // no more frames coming (sometimes unless we paint one of them).
     //
-    // For the first condition, we need at least two frames or the first frame
-    // must have a timestamp >= |start_timestamp_|, since otherwise we may be
-    // prerolling frames before the actual start time that will be dropped.
-    if (algorithm_->frames_queued() > 1 || received_end_of_stream_ ||
-        frame->timestamp() >= start_timestamp_ || low_delay_ ||
-        !video_frame_stream_->CanReadWithoutStalling()) {
+    // For the first condition, we need at least two effective frames, since
+    // otherwise we may be prerolling frames before the actual start time that
+    // will be dropped.
+    bool should_paint_first_frame =
+        algorithm_->effective_frames_queued() > 1 || received_end_of_stream_ ||
+        !video_frame_stream_->CanReadWithoutStalling();
+
+    // For the very first frame (i.e. not after seeks), we want to paint as fast
+    // as possible to ensure users don't abandon the playback. For live streams
+    // with long duration frames, waiting for a second frame may take seconds.
+    //
+    // Before time starts progressing we may not know if frames are effective or
+    // not, so the first frame must check if timestamp >= |start_timestamp_|.
+    //
+    // We only do this for the very first frame ever painted, since later frames
+    // risk being wrong due to the lack of duration on the first frame. This
+    // avoids any fast-forward or frame-flipping type effects as we try to
+    // resume after a seek.
+    if (!have_renderered_frames_ && !should_paint_first_frame) {
+      should_paint_first_frame =
+          frame->timestamp() >= start_timestamp_ || low_delay_;
+    }
+
+    if (should_paint_first_frame) {
       scoped_refptr<VideoFrame> first_frame =
           algorithm_->Render(base::TimeTicks(), base::TimeTicks(), nullptr);
       CheckForMetadataChanges(first_frame->format(),
                               first_frame->natural_size());
       sink_->PaintSingleFrame(first_frame);
-      painted_first_frame_ = true;
+      just_painted_first_frame = painted_first_frame_ = true;
     }
   }
 
   // Signal buffering state if we've met our conditions.
-  if (buffering_state_ == BUFFERING_HAVE_NOTHING && HaveEnoughData_Locked())
+  //
+  // If we've just painted the first frame, require the standard 1 frame for low
+  // latency playback. If we're resuming after a Flush(), wait until we have two
+  // frames even in low delay mode to avoid any kind of fast-forward or frame
+  // flipping effect while we attempt to find the best frame.
+  if (buffering_state_ == BUFFERING_HAVE_NOTHING &&
+      HaveEnoughData_Locked(just_painted_first_frame ? 1u : 2u)) {
     TransitionToHaveEnough_Locked();
+  }
 
   // Always request more decoded video if we have capacity.
   AttemptRead_Locked();
 }
 
-bool VideoRendererImpl::HaveEnoughData_Locked() {
+bool VideoRendererImpl::HaveEnoughData_Locked(
+    size_t low_latency_frames_required) const {
   DCHECK_EQ(state_, kPlaying);
   lock_.AssertAcquired();
 
@@ -602,10 +629,16 @@
   if (was_background_rendering_ && frames_decoded_)
     return true;
 
-  if (!low_delay_ && video_frame_stream_->CanReadWithoutStalling())
+  // Note: We still require an effective frame in the stalling case since this
+  // method is also used to inform TransitionToHaveNothing_Locked() and thus
+  // would never pause and rebuffer if we always return true here.
+  if (!video_frame_stream_->CanReadWithoutStalling())
+    return algorithm_->effective_frames_queued() > 0u;
+
+  if (!low_delay_)
     return false;
 
-  return algorithm_->effective_frames_queued() > 0;
+  return algorithm_->effective_frames_queued() >= low_latency_frames_required;
 }
 
 void VideoRendererImpl::TransitionToHaveEnough_Locked() {
@@ -725,7 +758,7 @@
   }
 }
 
-bool VideoRendererImpl::HaveReachedBufferingCap() {
+bool VideoRendererImpl::HaveReachedBufferingCap() const {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
   if (use_complexity_based_buffering_)
diff --git a/media/renderers/video_renderer_impl.h b/media/renderers/video_renderer_impl.h
index 2785bed..0cf59f82 100644
--- a/media/renderers/video_renderer_impl.h
+++ b/media/renderers/video_renderer_impl.h
@@ -140,7 +140,12 @@
 
   // Returns true if the renderer has enough data for playback purposes.
   // Note that having enough data may be due to reaching end of stream.
-  bool HaveEnoughData_Locked();
+  //
+  // |low_latency_frames_required| indicates the required number of frame for
+  // have enough with a low latency playback. By default it's one frame, but
+  // during resume after a Flush() we may wait for 2 frames to ensure we have
+  // effective frames.
+  bool HaveEnoughData_Locked(size_t low_latency_frames_required = 1u) const;
   void TransitionToHaveEnough_Locked();
   void TransitionToHaveNothing();
   void TransitionToHaveNothing_Locked();
@@ -150,7 +155,7 @@
   void UpdateStats_Locked();
 
   // Returns true if there is no more room for additional buffered frames.
-  bool HaveReachedBufferingCap();
+  bool HaveReachedBufferingCap() const;
 
   // Starts or stops |sink_| respectively. Do not call while |lock_| is held.
   void StartSink();
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc
index eea5882..486ae88 100644
--- a/media/renderers/video_renderer_impl_unittest.cc
+++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -433,13 +433,14 @@
       EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber());
       EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH))
           .WillOnce(RunClosure(event.GetClosure()));
+
       // Note: In the normal underflow case we queue 5 frames here instead of
       // four since the underflow increases the number of required frames to
       // reach the have enough state.
       if (type == UnderflowTestType::NORMAL)
         QueueFrames("80 100 120 140 160");
       else
-        QueueFrames("40 60 80 90");
+        QueueFrames("40 60 80 90 100");
       SatisfyPendingDecode();
       event.RunAndWait();
     }
diff --git a/media/test/data/load_many_videos.html b/media/test/data/load_many_videos.html
deleted file mode 100644
index 0eacf6f..0000000
--- a/media/test/data/load_many_videos.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!--
-Loads lots of videos to make sure that we don't deadlock somewhere
-while loading lots of videos. We try a variety of video formats and
-containers to try to cover different read patterns.
--->
-<html>
-<body onload="RunTest();">
-<video controls preload=audo src="bear-320x180-hi10p.mp4"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?A"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?B"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?C"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?D"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?E"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?F"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?G"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?H"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?I"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?J"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?K"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?L"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?M"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?N"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?O"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?P"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?Q"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?R"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?S"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?T"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?U"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?V"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?W"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?X"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?Y"></video><br>
-<video controls preload=audo src="bear-320x180-hi10p.mp4?Z"></video><br>
-<video controls preload=audo src="bear-1280x720.mp4"></video><br>
-<video controls preload=audo src="bear-320x240.webm"></video><br>
-<video controls preload=audo src="bear.mp4"></video><br>
-<video controls preload=audo src="bear-vp8a.webm"></video><br>
-<video controls preload=audo src="bear-320x240-video-only.webm"></video><br>
-<video controls preload=audo src="bear-320x240-vp9_profile2.webm"></video><br>
-<video controls preload=audo src="bear-320x180-hi12p-vp9.webm"></video><br>
-<video controls preload=audo src="bbb-320x240-2video-2audio.mp4"></video><br>
-<video controls preload=audo src="bear-1280x720-av_with-aud-nalus_frag.mp4"></video><br>
-<video controls preload=audo src="bear-vp8-webvtt.webm"></video><br>
-</body>
-
-<script>
-  function CheckIfDone() {
-    console.log("Done?");
-    var players = document.getElementsByTagName("video");
-    for (var i = 0; i < players.length; i++) {
-      if (players[i].readyState < 4) return;
-    }
-    document.title = "ENDED";
-  }
-  function RunTest() {
-    var players = document.getElementsByTagName("video");
-    for (var i = 0; i < players.length; i++) {
-      players[i].addEventListener('canplaythrough', function(e) { CheckIfDone(); });
-    }
-    CheckIfDone();
-  }
-</script>
-</html>
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index cae4034..6f08481 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -192,6 +192,7 @@
 void PipelineIntegrationTestBase::OnError(PipelineStatus status) {
   DCHECK_NE(status, PIPELINE_OK);
   pipeline_status_ = status;
+  pipeline_->Stop();
   scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
       FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
 }
diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md
index 99ec23e..2e7c02d 100644
--- a/mojo/edk/embedder/README.md
+++ b/mojo/edk/embedder/README.md
@@ -59,7 +59,7 @@
 
   base::Thread ipc_thread("ipc!");
   ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
 
   // As long as this object is alive, all EDK API surface relevant to IPC
   // connections is usable and message pipes which span a process boundary will
@@ -120,7 +120,7 @@
 
   base::Thread ipc_thread("ipc!");
   ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
 
   mojo::edk::ScopedIPCSupport ipc_support(
       ipc_thread.task_runner(),
@@ -164,7 +164,7 @@
 
   base::Thread ipc_thread("ipc!");
   ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
 
   mojo::edk::ScopedIPCSupport ipc_support(
       ipc_thread.task_runner(),
@@ -212,7 +212,7 @@
 
   base::Thread ipc_thread("ipc!");
   ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO));
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
 
   mojo::edk::ScopedIPCSupport ipc_support(
       ipc_thread.task_runner(),
diff --git a/mojo/public/cpp/bindings/interface_id.h b/mojo/public/cpp/bindings/interface_id.h
index 53475d6..d612853 100644
--- a/mojo/public/cpp/bindings/interface_id.h
+++ b/mojo/public/cpp/bindings/interface_id.h
@@ -30,6 +30,10 @@
   return id != kInvalidInterfaceId;
 }
 
+inline bool HasInterfaceIdNamespaceBitSet(InterfaceId id) {
+  return (id & kInterfaceIdNamespaceMask) != 0;
+}
+
 }  // namespace mojo
 
 #endif  // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ID_H_
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 54fae5c8..031822c 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -442,6 +442,14 @@
   if (!IsValidInterfaceId(id))
     return ScopedInterfaceEndpointHandle();
 
+  // Unless it is the master ID, |id| is from the remote side and therefore its
+  // namespace bit is supposed to be different than the value that this router
+  // would use.
+  if (!IsMasterInterfaceId(id) &&
+      set_interface_id_namespace_bit_ == HasInterfaceIdNamespaceBitSet(id)) {
+    return ScopedInterfaceEndpointHandle();
+  }
+
   MayAutoLock locker(&lock_);
   bool inserted = false;
   InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
@@ -451,13 +459,13 @@
     if (encountered_error_)
       UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
   } else {
-    // If the endpoint already exist, it is because we have received a
-    // notification that the peer endpoint has closed.
-    CHECK(!endpoint->closed());
-    CHECK(endpoint->peer_closed());
-
     if (endpoint->handle_created())
       return ScopedInterfaceEndpointHandle();
+
+    // If the endpoint already exist, it is because we have received a
+    // notification that the peer endpoint has closed.
+    DCHECK(!endpoint->closed());
+    DCHECK(endpoint->peer_closed());
   }
 
   endpoint->set_handle_created();
diff --git a/mojo/public/js/new_bindings/interface_types.js b/mojo/public/js/new_bindings/interface_types.js
index 3d35e8c..b7085e5a 100644
--- a/mojo/public/js/new_bindings/interface_types.js
+++ b/mojo/public/js/new_bindings/interface_types.js
@@ -77,12 +77,20 @@
     return interfaceId !== kInvalidInterfaceId;
   }
 
+  function hasInterfaceIdNamespaceBitSet(interfaceId) {
+    if (interfaceId >= 2 * kInterfaceIdNamespaceMask) {
+      throw new Error("Interface ID should be a 32-bit unsigned integer.");
+    }
+    return interfaceId >= kInterfaceIdNamespaceMask;
+  }
+
   mojo.InterfacePtrInfo = InterfacePtrInfo;
   mojo.InterfaceRequest = InterfaceRequest;
   mojo.AssociatedInterfacePtrInfo = AssociatedInterfacePtrInfo;
   mojo.AssociatedInterfaceRequest = AssociatedInterfaceRequest;
   internal.isMasterInterfaceId = isMasterInterfaceId;
   internal.isValidInterfaceId = isValidInterfaceId;
+  internal.hasInterfaceIdNamespaceBitSet = hasInterfaceIdNamespaceBitSet;
   internal.kInvalidInterfaceId = kInvalidInterfaceId;
   internal.kMasterInterfaceId = kMasterInterfaceId;
   internal.kInterfaceIdNamespaceMask = kInterfaceIdNamespaceMask;
diff --git a/mojo/public/js/new_bindings/router.js b/mojo/public/js/new_bindings/router.js
index c7065dc..9aab9c2 100644
--- a/mojo/public/js/new_bindings/router.js
+++ b/mojo/public/js/new_bindings/router.js
@@ -165,6 +165,15 @@
       return new internal.InterfaceEndpointHandle();
     }
 
+    // Unless it is the master ID, |interfaceId| is from the remote side and
+    // therefore its namespace bit is supposed to be different than the value
+    // that this router would use.
+    if (!internal.isMasterInterfaceId(interfaceId) &&
+        this.setInterfaceIdNamespaceBit_ ===
+            internal.hasInterfaceIdNamespaceBitSet(interfaceId)) {
+      return new internal.InterfaceEndpointHandle();
+    }
+
     var endpoint = this.endpoints_.get(interfaceId);
 
     if (!endpoint) {
diff --git a/mojo/public/js/router.js b/mojo/public/js/router.js
index 5fc0ed56..805f663 100644
--- a/mojo/public/js/router.js
+++ b/mojo/public/js/router.js
@@ -194,14 +194,14 @@
             EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
       }
     } else {
+      if (endpoint.handleCreated) {
+        return new InterfaceEndpointHandle();
+      }
+
       // If the endpoint already exist, it is because we have received a
       // notification that the peer endpoint has closed.
       check(!endpoint.closed);
       check(endpoint.peerClosed);
-
-      if (endpoint.handleCreated) {
-        return new InterfaceEndpointHandle();
-      }
     }
 
     endpoint.handleCreated = true;
diff --git a/net/base/network_change_notifier.h b/net/base/network_change_notifier.h
index 6678979..cf45ab7 100644
--- a/net/base/network_change_notifier.h
+++ b/net/base/network_change_notifier.h
@@ -103,6 +103,7 @@
     SUBTYPE_LAST = SUBTYPE_WIFI_AD
   };
 
+  // DEPRECATED. Please use NetworkChangeObserver instead. crbug.com/754695.
   class NET_EXPORT IPAddressObserver {
    public:
     // Will be called when the IP address of the primary interface changes.
@@ -117,6 +118,7 @@
     DISALLOW_COPY_AND_ASSIGN(IPAddressObserver);
   };
 
+  // DEPRECATED. Please use NetworkChangeObserver instead. crbug.com/754695.
   class NET_EXPORT ConnectionTypeObserver {
    public:
     // Will be called when the connection type of the system has changed.
@@ -374,7 +376,12 @@
   // called back with notifications.  This is safe to call if Create() has not
   // been called (as long as it doesn't race the Create() call on another
   // thread), in which case it will simply do nothing.
+
+  // DEPRECATED. IPAddressObserver is deprecated. Please use
+  // NetworkChangeObserver instead. crbug.com/754695.
   static void AddIPAddressObserver(IPAddressObserver* observer);
+  // DEPRECATED. ConnectionTypeObserver is deprecated. Please use
+  // NetworkChangeObserver instead. crbug.com/754695.
   static void AddConnectionTypeObserver(ConnectionTypeObserver* observer);
   static void AddDNSObserver(DNSObserver* observer);
   static void AddNetworkChangeObserver(NetworkChangeObserver* observer);
@@ -388,7 +395,12 @@
   // nothing.  Technically, it's also safe to call after the notifier object has
   // been destroyed, if the call doesn't race the notifier's destruction, but
   // there's no reason to use the API in this risky way, so don't do it.
+
+  // DEPRECATED. IPAddressObserver is deprecated. Please use
+  // NetworkChangeObserver instead. crbug.com/754695.
   static void RemoveIPAddressObserver(IPAddressObserver* observer);
+  // DEPRECATED. ConnectionTypeObserver is deprecated. Please use
+  // NetworkChangeObserver instead. crbug.com/754695.
   static void RemoveConnectionTypeObserver(ConnectionTypeObserver* observer);
   static void RemoveDNSObserver(DNSObserver* observer);
   static void RemoveNetworkChangeObserver(NetworkChangeObserver* observer);
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index f6b1b8fa..422a8e8 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -2099,6 +2099,14 @@
     MakeNotStale(stale_info);
     return net_error;
   }
+
+  // Special-case localhost names, as per the recommendations in
+  // https://tools.ietf.org/html/draft-west-let-localhost-be-localhost.
+  if (ServeLocalhost(*key, info, addresses)) {
+    MakeNotStale(stale_info);
+    return OK;
+  }
+
   if (ServeFromCache(*key, info, &net_error, addresses, allow_stale,
                      stale_info)) {
     source_net_log.AddEvent(NetLogEventType::HOST_RESOLVER_IMPL_CACHE_HIT,
@@ -2106,6 +2114,7 @@
     // |ServeFromCache()| will set |*stale_info| as needed.
     return net_error;
   }
+
   // TODO(szym): Do not do this if nsswitch.conf instructs not to.
   // http://crbug.com/117655
   if (ServeFromHosts(*key, info, addresses)) {
@@ -2115,11 +2124,6 @@
     return OK;
   }
 
-  if (ServeLocalhost(*key, info, addresses)) {
-    MakeNotStale(stale_info);
-    return OK;
-  }
-
   return ERR_DNS_CACHE_MISS;
 }
 
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index 9b3b3948..249419c 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -641,30 +641,6 @@
   EXPECT_EQ("just.testing", proc_->GetCaptureList()[0].hostname);
 }
 
-// RFC 6761 localhost names should always resolve to loopback.
-TEST_F(HostResolverImplTest, LocalhostLookup) {
-  // Add a rule resolving localhost names to a non-loopback IP and test
-  // that they still resolves to loopback.
-  proc_->AddRuleForAllFamilies("foo.localhost", "192.168.1.42");
-  proc_->AddRuleForAllFamilies("localhost", "192.168.1.42");
-  proc_->AddRuleForAllFamilies("localhost.", "192.168.1.42");
-
-  Request* req0 = CreateRequest("foo.localhost", 80);
-  EXPECT_THAT(req0->Resolve(), IsOk());
-  EXPECT_TRUE(req0->HasAddress("127.0.0.1", 80));
-  EXPECT_TRUE(req0->HasAddress("::1", 80));
-
-  Request* req1 = CreateRequest("localhost", 80);
-  EXPECT_THAT(req1->Resolve(), IsOk());
-  EXPECT_TRUE(req1->HasAddress("127.0.0.1", 80));
-  EXPECT_TRUE(req1->HasAddress("::1", 80));
-
-  Request* req2 = CreateRequest("localhost.", 80);
-  EXPECT_THAT(req2->Resolve(), IsOk());
-  EXPECT_TRUE(req2->HasAddress("127.0.0.1", 80));
-  EXPECT_TRUE(req2->HasAddress("::1", 80));
-}
-
 TEST_F(HostResolverImplTest, LocalhostIPV4IPV6Lookup) {
   Request* req1 = CreateRequest("localhost6", 80, MEDIUM, ADDRESS_FAMILY_IPV4);
   EXPECT_THAT(req1->Resolve(), IsOk());
@@ -1713,6 +1689,56 @@
 
 // TODO(cbentzel): Test a mix of requests with different HostResolverFlags.
 
+// RFC 6761 localhost names should always resolve to loopback.
+TEST_F(HostResolverImplDnsTest, LocalhostLookup) {
+  // Add a rule resolving localhost names to a non-loopback IP and test
+  // that they still resolves to loopback.
+  proc_->AddRuleForAllFamilies("foo.localhost", "192.168.1.42");
+  proc_->AddRuleForAllFamilies("localhost", "192.168.1.42");
+  proc_->AddRuleForAllFamilies("localhost.", "192.168.1.42");
+
+  Request* req0 = CreateRequest("foo.localhost", 80);
+  EXPECT_THAT(req0->Resolve(), IsOk());
+  EXPECT_TRUE(req0->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(req0->HasAddress("::1", 80));
+
+  Request* req1 = CreateRequest("localhost", 80);
+  EXPECT_THAT(req1->Resolve(), IsOk());
+  EXPECT_TRUE(req1->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(req1->HasAddress("::1", 80));
+
+  Request* req2 = CreateRequest("localhost.", 80);
+  EXPECT_THAT(req2->Resolve(), IsOk());
+  EXPECT_TRUE(req2->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(req2->HasAddress("::1", 80));
+}
+
+// RFC 6761 localhost names should always resolve to loopback, even if a HOSTS
+// file is active.
+TEST_F(HostResolverImplDnsTest, LocalhostLookupWithHosts) {
+  DnsHosts hosts;
+  hosts[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4)] =
+      IPAddress({192, 168, 1, 1});
+  hosts[DnsHostsKey("foo.localhost", ADDRESS_FAMILY_IPV4)] =
+      IPAddress({192, 168, 1, 2});
+
+  DnsConfig config = CreateValidDnsConfig();
+  config.hosts = hosts;
+  ChangeDnsConfig(config);
+
+  Request* req1 = CreateRequest("localhost", 80);
+  EXPECT_THAT(req1->Resolve(), IsOk());
+  EXPECT_TRUE(req1->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(req1->HasAddress("::1", 80));
+  EXPECT_FALSE(req1->HasAddress("192.168.1.1", 80));
+
+  Request* req2 = CreateRequest("foo.localhost", 80);
+  EXPECT_THAT(req2->Resolve(), IsOk());
+  EXPECT_TRUE(req2->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(req2->HasAddress("::1", 80));
+  EXPECT_FALSE(req2->HasAddress("192.168.1.2", 80));
+}
+
 // Test successful and fallback resolutions in HostResolverImpl::DnsTask.
 TEST_F(HostResolverImplDnsTest, DnsTask) {
   proc_->AddRuleForAllFamilies("nx_succeed", "192.168.1.102");
@@ -2102,8 +2128,11 @@
   // Expect synchronous resolution from DnsHosts.
   EXPECT_THAT(req->Resolve(), IsOk());
 
-  EXPECT_EQ(saw_ipv4, req->HasAddress("127.0.0.1", 80));
-  EXPECT_EQ(saw_ipv6, req->HasAddress("::1", 80));
+  // Localhost names always resolve to IPv4 and IPv6, regardless of the content
+  // written into the HOSTS file above based on the results of the
+  // SystemHostResolverCall at the top of this test.
+  EXPECT_TRUE(req->HasAddress("127.0.0.1", 80));
+  EXPECT_TRUE(req->HasAddress("::1", 80));
 }
 
 // Cancel a request with a single DNS transaction active.
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h
index a443e4a09..73d3f05 100644
--- a/net/quic/chromium/quic_chromium_client_session.h
+++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -475,6 +475,8 @@
   // TODO(xunjieli): It only tracks |packet_readers_|. Write a better estimate.
   size_t EstimateMemoryUsage() const;
 
+  bool require_confirmation() const { return require_confirmation_; }
+
  protected:
   // QuicSession methods:
   bool ShouldCreateIncomingDynamicStream(QuicStreamId id) override;
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index cee86b8..9572d01 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -508,8 +508,7 @@
 int QuicStreamFactory::Job::DoConnect() {
   io_state_ = STATE_CONNECT_COMPLETE;
 
-  bool require_confirmation = factory_->require_confirmation() ||
-                              was_alternative_service_recently_broken_;
+  bool require_confirmation = was_alternative_service_recently_broken_;
   net_log_.BeginEvent(
       NetLogEventType::QUIC_STREAM_FACTORY_JOB_CONNECT,
       NetLog::BoolCallback("require_confirmation", require_confirmation));
@@ -1533,6 +1532,11 @@
             SocketPerformanceWatcherFactory::PROTOCOL_QUIC, address_list);
   }
 
+  // Wait for handshake confirmation before allowing streams to be created if
+  // either this session or the factory require confirmation.
+  if (require_confirmation_)
+    require_confirmation = true;
+
   *session = new QuicChromiumClientSession(
       connection, std::move(socket), this, quic_crypto_client_stream_factory_,
       clock_, transport_security_state_, std::move(server_info), server_id,
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc
index 1f474bfb..2fa53972 100644
--- a/net/quic/chromium/quic_stream_factory_test.cc
+++ b/net/quic/chromium/quic_stream_factory_test.cc
@@ -222,7 +222,6 @@
         allow_server_migration_, force_hol_blocking_, race_cert_verification_,
         estimate_initial_rtt_, connection_options_, client_connection_options_,
         /*enable_token_binding*/ false));
-    factory_->set_require_confirmation(false);
   }
 
   void InitializeConnectionMigrationTest(
@@ -469,6 +468,7 @@
     store_server_configs_in_properties_ = true;
     idle_connection_timeout_seconds_ = 500;
     Initialize();
+    factory_->set_require_confirmation(false);
     ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
     crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
     crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
@@ -781,8 +781,8 @@
   EXPECT_EQ(OK, request3.Request(host_port_pair_, version_, privacy_mode_,
                                  /*cert_verify_flags=*/0, url_, "GET", net_log_,
                                  callback_.callback()));
-  stream = request3.CreateStream();   // Will reset stream 5.
-  stream.reset();                     // Will reset stream 7.
+  stream = request3.CreateStream();  // Will reset stream 5.
+  stream.reset();                    // Will reset stream 7.
 
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
@@ -790,6 +790,7 @@
 
 TEST_P(QuicStreamFactoryTest, CreateZeroRtt) {
   Initialize();
+  factory_->set_require_confirmation(false);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
@@ -816,6 +817,7 @@
 
 TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) {
   Initialize();
+  factory_->set_require_confirmation(false);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
@@ -861,10 +863,75 @@
   EXPECT_TRUE(stream.get());
 
   QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(session->require_confirmation());
   EXPECT_EQ(100000u, session->connection()->GetStats().srtt_us);
   ASSERT_FALSE(session->config()->HasInitialRoundTripTimeUsToSend());
 }
 
+TEST_P(QuicStreamFactoryTest, RequireConfirmation) {
+  crypto_client_stream_factory_.set_handshake_mode(
+      MockCryptoClientStream::ZERO_RTT);
+  host_resolver_.set_synchronous_mode(true);
+  host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+                                           "192.168.0.1", "");
+  Initialize();
+  factory_->set_require_confirmation(true);
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data;
+  socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(&socket_factory_);
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(host_port_pair_, version_, privacy_mode_,
+                            /*cert_verify_flags=*/0, url_, "GET", net_log_,
+                            callback_.callback()));
+
+  crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
+      QuicSession::HANDSHAKE_CONFIRMED);
+
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
+  EXPECT_TRUE(stream.get());
+
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(session->require_confirmation());
+}
+
+TEST_P(QuicStreamFactoryTest, DontRequireConfirmationFromSameIP) {
+  crypto_client_stream_factory_.set_handshake_mode(
+      MockCryptoClientStream::ZERO_RTT);
+  host_resolver_.set_synchronous_mode(true);
+  host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+                                           "192.168.0.1", "");
+  Initialize();
+  factory_->set_require_confirmation(true);
+  http_server_properties_.SetSupportsQuic(IPAddress(192, 0, 2, 33));
+
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data;
+  socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(&socket_factory_);
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_THAT(request.Request(host_port_pair_, version_, privacy_mode_,
+                              /*cert_verify_flags=*/0, url_, "GET", net_log_,
+                              callback_.callback()),
+              IsOk());
+
+  std::unique_ptr<HttpStream> stream = request.CreateStream();
+  EXPECT_TRUE(stream.get());
+
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_FALSE(session->require_confirmation());
+}
+
 TEST_P(QuicStreamFactoryTest, CachedInitialRtt) {
   ServerNetworkStats stats;
   stats.srtt = base::TimeDelta::FromMilliseconds(10);
@@ -4269,6 +4336,7 @@
 
 TEST_P(QuicStreamFactoryTest, EnableNotLoadFromDiskCache) {
   Initialize();
+  factory_->set_require_confirmation(false);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
@@ -4453,6 +4521,7 @@
 
 TEST_P(QuicStreamFactoryTest, YieldAfterPackets) {
   Initialize();
+  factory_->set_require_confirmation(false);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   QuicStreamFactoryPeer::SetYieldAfterPackets(factory_.get(), 0);
@@ -4497,6 +4566,7 @@
 
 TEST_P(QuicStreamFactoryTest, YieldAfterDuration) {
   Initialize();
+  factory_->set_require_confirmation(false);
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
   QuicStreamFactoryPeer::SetYieldAfterDuration(
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 1bcad96..f14db57 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -137,6 +137,8 @@
     "dns_blackhole_checker.h",
     "evaluate_capability.cc",
     "evaluate_capability.h",
+    "file_proxy_wrapper.cc",
+    "file_proxy_wrapper.h",
     "file_transfer_message_handler.cc",
     "file_transfer_message_handler.h",
     "file_transfer_message_handler_factory.cc",
diff --git a/remoting/host/file_proxy_wrapper.cc b/remoting/host/file_proxy_wrapper.cc
new file mode 100644
index 0000000..6e1aabc
--- /dev/null
+++ b/remoting/host/file_proxy_wrapper.cc
@@ -0,0 +1,13 @@
+// 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 "remoting/host/file_proxy_wrapper.h"
+
+namespace remoting {
+
+FileProxyWrapper::FileProxyWrapper() = default;
+
+FileProxyWrapper::~FileProxyWrapper() = default;
+
+}  // namespace remoting
diff --git a/remoting/host/file_proxy_wrapper.h b/remoting/host/file_proxy_wrapper.h
new file mode 100644
index 0000000..3065b3f
--- /dev/null
+++ b/remoting/host/file_proxy_wrapper.h
@@ -0,0 +1,39 @@
+// 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 REMOTING_HOST_FILE_PROXY_WRAPPER_H_
+#define REMOTING_HOST_FILE_PROXY_WRAPPER_H_
+
+#include "base/callback.h"
+#include "base/files/file_proxy.h"
+#include "remoting/proto/file_transfer.pb.h"
+
+namespace remoting {
+
+class CompoundBuffer;
+
+// FileProxyWrapper is an interface for implementing platform-specific file
+// writers for file transfers. Each operation is posted to a separate file IO
+// thread, and possibly a different process depending on the platform.
+class FileProxyWrapper {
+ public:
+  typedef base::Callback<void(protocol::FileTransferResponse_ErrorCode)>
+      ErrorCallback;
+  typedef base::Callback<void()> SuccessCallback;
+
+  FileProxyWrapper();
+  virtual ~FileProxyWrapper();
+
+  virtual void Init(const ErrorCallback& error_callback) = 0;
+  virtual void CreateFile(const std::string& filename,
+                          uint64_t filesize,
+                          const SuccessCallback& success_callback) = 0;
+  virtual void WriteChunk(std::unique_ptr<CompoundBuffer> buffer) = 0;
+  virtual void Close(const SuccessCallback& success_callback) = 0;
+  virtual void Cancel() = 0;
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_FILE_PROXY_WRAPPER_H_
diff --git a/services/device/generic_sensor/absolute_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_magnetometer.cc b/services/device/generic_sensor/absolute_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_magnetometer.cc
index c6f2866..7d6b45c 100644
--- a/services/device/generic_sensor/absolute_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_magnetometer.cc
+++ b/services/device/generic_sensor/absolute_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_magnetometer.cc
@@ -118,10 +118,8 @@
   // angles.
   DCHECK(fusion_sensor_);
 
-  // Not generating a new sensor value when the |fusion_sensor_|'s reporting
-  // mode is ON_CHANGE and the accelerometer reading doesn't change.
-  if (fusion_sensor_->GetReportingMode() == mojom::ReportingMode::ON_CHANGE &&
-      which_sensor_changed != mojom::SensorType::ACCELEROMETER) {
+  // Only generate a new sensor value when the accelerometer reading changes.
+  if (which_sensor_changed != mojom::SensorType::ACCELEROMETER) {
     return false;
   }
 
diff --git a/services/device/generic_sensor/generic_sensor_service_unittest.cc b/services/device/generic_sensor/generic_sensor_service_unittest.cc
index 79147698..f3ae3d98 100644
--- a/services/device/generic_sensor/generic_sensor_service_unittest.cc
+++ b/services/device/generic_sensor/generic_sensor_service_unittest.cc
@@ -53,7 +53,7 @@
     if (GetType() == SensorType::AMBIENT_LIGHT) {
       // Set the shared buffer value as frequency for testing purpose.
       reading.als.value = configuration.frequency();
-      UpdateSensorReading(reading, true);
+      UpdateSensorReading(reading);
     }
     return true;
   }
diff --git a/services/device/generic_sensor/platform_sensor.cc b/services/device/generic_sensor/platform_sensor.cc
index 7bdf40c..c77be98 100644
--- a/services/device/generic_sensor/platform_sensor.cc
+++ b/services/device/generic_sensor/platform_sensor.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "services/device/generic_sensor/platform_sensor_provider.h"
@@ -103,8 +104,7 @@
   return shared_buffer_reader_->GetReading(result);
 }
 
-void PlatformSensor::UpdateSensorReading(const SensorReading& reading,
-                                         bool notify_clients) {
+void PlatformSensor::UpdateSensorReading(const SensorReading& reading) {
   ReadingBuffer* buffer =
       static_cast<ReadingBuffer*>(shared_buffer_mapping_.get());
   auto& seqlock = buffer->seqlock.value();
@@ -112,10 +112,9 @@
   buffer->reading = reading;
   seqlock.WriteEnd();
 
-  if (notify_clients)
-    task_runner_->PostTask(
-        FROM_HERE, base::Bind(&PlatformSensor::NotifySensorReadingChanged,
-                              weak_factory_.GetWeakPtr()));
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&PlatformSensor::NotifySensorReadingChanged,
+                                    weak_factory_.GetWeakPtr()));
 }
 
 void PlatformSensor::NotifySensorReadingChanged() {
diff --git a/services/device/generic_sensor/platform_sensor.h b/services/device/generic_sensor/platform_sensor.h
index 2bcf963c04..9e466ff 100644
--- a/services/device/generic_sensor/platform_sensor.h
+++ b/services/device/generic_sensor/platform_sensor.h
@@ -84,7 +84,7 @@
 
   // Updates shared buffer with new sensor reading data.
   // Note: this method is thread-safe.
-  void UpdateSensorReading(const SensorReading& reading, bool notify_clients);
+  void UpdateSensorReading(const SensorReading& reading);
 
   void NotifySensorReadingChanged();
   void NotifySensorError();
diff --git a/services/device/generic_sensor/platform_sensor_accelerometer_mac.cc b/services/device/generic_sensor/platform_sensor_accelerometer_mac.cc
index bc0700e7..2eaa9c9 100644
--- a/services/device/generic_sensor/platform_sensor_accelerometer_mac.cc
+++ b/services/device/generic_sensor/platform_sensor_accelerometer_mac.cc
@@ -98,7 +98,7 @@
 
   if (IsSignificantlyDifferent(reading_, reading)) {
     reading_ = reading;
-    UpdateSensorReading(reading, true);
+    UpdateSensorReading(reading);
   }
 }
 
diff --git a/services/device/generic_sensor/platform_sensor_ambient_light_mac.cc b/services/device/generic_sensor/platform_sensor_ambient_light_mac.cc
index bb9208f..bd44766 100644
--- a/services/device/generic_sensor/platform_sensor_ambient_light_mac.cc
+++ b/services/device/generic_sensor/platform_sensor_ambient_light_mac.cc
@@ -171,7 +171,7 @@
   reading.als.timestamp =
       (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
   reading.als.value = current_lux_;
-  UpdateSensorReading(reading, true);
+  UpdateSensorReading(reading);
   return true;
 }
 
diff --git a/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc b/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc
index 1668e8e..871316c 100644
--- a/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc
+++ b/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc
@@ -35,6 +35,8 @@
 // Zero value can mean whether value is not being not used or zero value.
 constexpr double kZero = 0.0;
 
+constexpr double kAmbientLightFrequencyValue = 10.0;
+
 constexpr double kAccelerometerFrequencyValue = 10.0;
 constexpr double kAccelerometerOffsetValue = 1.0;
 constexpr double kAccelerometerScalingValue = 0.009806;
@@ -655,4 +657,43 @@
               scaling * (sensor_values[2] + kMagnetometerOffsetValue));
 }
 
+// Tests that Ambient Light sensor client's OnSensorReadingChanged() is called
+// when the Ambient Light sensor's reporting mode is
+// mojom::ReportingMode::CONTINUOUS.
+TEST_F(PlatformSensorAndProviderLinuxTest,
+       SensorClientGetReadingChangedNotificationWhenSensorIsInContinuousMode) {
+  mojo::ScopedSharedBufferHandle handle = provider_->CloneSharedBufferHandle();
+  mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset(
+      sizeof(SensorReadingSharedBuffer),
+      SensorReadingSharedBuffer::GetOffset(SensorType::AMBIENT_LIGHT));
+
+  double sensor_value[3] = {22};
+  // Set a non-zero frequency here and sensor's reporting mode will be
+  // mojom::ReportingMode::CONTINUOUS.
+  InitializeSupportedSensor(SensorType::AMBIENT_LIGHT,
+                            kAmbientLightFrequencyValue, kZero, kZero,
+                            sensor_value);
+
+  InitializeMockUdevMethods(sensors_dir_.GetPath());
+  SetServiceStart();
+
+  auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT);
+  EXPECT_TRUE(sensor);
+  EXPECT_EQ(mojom::ReportingMode::CONTINUOUS, sensor->GetReportingMode());
+
+  auto client = base::MakeUnique<NiceMock<MockPlatformSensorClient>>(sensor);
+
+  PlatformSensorConfiguration configuration(
+      sensor->GetMaximumSupportedFrequency());
+  EXPECT_TRUE(sensor->StartListening(client.get(), configuration));
+
+  WaitOnSensorReadingChangedEvent(client.get(), sensor->GetType());
+
+  EXPECT_TRUE(sensor->StopListening(client.get(), configuration));
+
+  SensorReadingSharedBuffer* buffer =
+      static_cast<SensorReadingSharedBuffer*>(mapping.get());
+  EXPECT_THAT(buffer->reading.als.value, sensor_value[0]);
+}
+
 }  // namespace device
diff --git a/services/device/generic_sensor/platform_sensor_android.cc b/services/device/generic_sensor/platform_sensor_android.cc
index 5c61176..daf4d2e 100644
--- a/services/device/generic_sensor/platform_sensor_android.cc
+++ b/services/device/generic_sensor/platform_sensor_android.cc
@@ -93,8 +93,7 @@
   reading.raw.values[2] = value3;
   reading.raw.values[3] = value4;
 
-  bool needNotify = (GetReportingMode() == mojom::ReportingMode::ON_CHANGE);
-  UpdateSensorReading(reading, needNotify);
+  UpdateSensorReading(reading);
 }
 
 }  // namespace device
diff --git a/services/device/generic_sensor/platform_sensor_fusion.cc b/services/device/generic_sensor/platform_sensor_fusion.cc
index b4d49ab..15b5173 100644
--- a/services/device/generic_sensor/platform_sensor_fusion.cc
+++ b/services/device/generic_sensor/platform_sensor_fusion.cc
@@ -55,17 +55,7 @@
       return false;
   }
 
-  if (GetReportingMode() == mojom::ReportingMode::CONTINUOUS) {
-    timer_.Start(
-        FROM_HERE,
-        base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond /
-                                          configuration.frequency()),
-        base::Bind(&PlatformSensorFusion::OnSensorReadingChanged,
-                   base::Unretained(this), GetType()));
-  }
-
   fusion_algorithm_->SetFrequency(configuration.frequency());
-
   return true;
 }
 
@@ -73,9 +63,6 @@
   for (const auto& sensor : source_sensors_)
     sensor->StopSensor();
 
-  if (timer_.IsRunning())
-    timer_.Stop();
-
   fusion_algorithm_->Reset();
 }
 
@@ -102,8 +89,7 @@
   }
 
   reading_ = reading;
-  UpdateSensorReading(reading_,
-                      GetReportingMode() == mojom::ReportingMode::ON_CHANGE);
+  UpdateSensorReading(reading_);
 }
 
 void PlatformSensorFusion::OnSensorError() {
diff --git a/services/device/generic_sensor/platform_sensor_fusion.h b/services/device/generic_sensor/platform_sensor_fusion.h
index fe9ea8d5..7d3331c 100644
--- a/services/device/generic_sensor/platform_sensor_fusion.h
+++ b/services/device/generic_sensor/platform_sensor_fusion.h
@@ -70,9 +70,6 @@
   SensorReading reading_;
   std::vector<scoped_refptr<PlatformSensor>> source_sensors_;
   std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm_;
-  // Repeating timer for data polling if all source sensors are CONTINUOUS
-  // reporting mode.
-  base::RepeatingTimer timer_;
   mojom::ReportingMode reporting_mode_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformSensorFusion);
diff --git a/services/device/generic_sensor/platform_sensor_linux.cc b/services/device/generic_sensor/platform_sensor_linux.cc
index 27907348..f501ba6 100644
--- a/services/device/generic_sensor/platform_sensor_linux.cc
+++ b/services/device/generic_sensor/platform_sensor_linux.cc
@@ -51,16 +51,14 @@
 
 void PlatformSensorLinux::UpdatePlatformSensorReading(SensorReading reading) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  bool notifyNeeded = false;
-  if (GetReportingMode() == mojom::ReportingMode::ON_CHANGE) {
-    if (!HaveValuesChanged(reading, old_values_))
-      return;
-    notifyNeeded = true;
+  if (GetReportingMode() == mojom::ReportingMode::ON_CHANGE &&
+      !HaveValuesChanged(reading, old_values_)) {
+    return;
   }
   old_values_ = reading;
   reading.raw.timestamp =
       (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
-  UpdateSensorReading(reading, notifyNeeded);
+  UpdateSensorReading(reading);
 }
 
 void PlatformSensorLinux::NotifyPlatformSensorError() {
diff --git a/services/device/generic_sensor/platform_sensor_win.cc b/services/device/generic_sensor/platform_sensor_win.cc
index fd521ddd..48dac1b 100644
--- a/services/device/generic_sensor/platform_sensor_win.cc
+++ b/services/device/generic_sensor/platform_sensor_win.cc
@@ -45,9 +45,7 @@
 }
 
 void PlatformSensorWin::OnReadingUpdated(const SensorReading& reading) {
-  // Default reporting mode is ON_CHANGE, thus, set notify_clients parameter
-  // to true.
-  UpdateSensorReading(reading, true);
+  UpdateSensorReading(reading);
 }
 
 void PlatformSensorWin::OnSensorError() {
diff --git a/services/device/generic_sensor/sensor_impl.cc b/services/device/generic_sensor/sensor_impl.cc
index 97f5800..1b91f12 100644
--- a/services/device/generic_sensor/sensor_impl.cc
+++ b/services/device/generic_sensor/sensor_impl.cc
@@ -60,8 +60,10 @@
 
 void SensorImpl::OnSensorReadingChanged(mojom::SensorType type) {
   DCHECK(!suspended_);
-  if (client_ && reading_notification_enabled_)
+  if (client_ && reading_notification_enabled_ &&
+      sensor_->GetReportingMode() == mojom::ReportingMode::ON_CHANGE) {
     client_->SensorReadingChanged();
+  }
 }
 
 void SensorImpl::OnSensorError() {
diff --git a/services/network/DEPS b/services/network/DEPS
new file mode 100644
index 0000000..8fa9d48
--- /dev/null
+++ b/services/network/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+net",
+]
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
new file mode 100644
index 0000000..cc13936
--- /dev/null
+++ b/services/network/public/cpp/BUILD.gn
@@ -0,0 +1,16 @@
+# 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.
+
+static_library("cpp") {
+  sources = [
+    "net_adapters.cc",
+    "net_adapters.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/common",
+    "//net",
+  ]
+}
diff --git a/content/common/net_adapters.cc b/services/network/public/cpp/net_adapters.cc
similarity index 96%
rename from content/common/net_adapters.cc
rename to services/network/public/cpp/net_adapters.cc
index 504f389..da516f2 100644
--- a/content/common/net_adapters.cc
+++ b/services/network/public/cpp/net_adapters.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/net_adapters.h"
+#include "services/network/public/cpp/net_adapters.h"
 
 #include "net/base/net_errors.h"
 
-namespace content {
+namespace network {
 
 namespace {
 const uint32_t kMaxBufSize = 64 * 1024;
@@ -99,4 +99,4 @@
   pending_buffer_->CompleteRead(bytes_to_be_read_);
 }
 
-}  // namespace content
+}  // namespace network
diff --git a/content/common/net_adapters.h b/services/network/public/cpp/net_adapters.h
similarity index 95%
rename from content/common/net_adapters.h
rename to services/network/public/cpp/net_adapters.h
index f9955c13..1008a64 100644
--- a/content/common/net_adapters.h
+++ b/services/network/public/cpp/net_adapters.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_COMMON_NET_ADAPTERS_
-#define CONTENT_COMMON_NET_ADAPTERS_
+#ifndef SERVICES_NETWORK_PUBLIC_CPP_NET_ADAPTERS_
+#define SERVICES_NETWORK_PUBLIC_CPP_NET_ADAPTERS_
 
 #include <stdint.h>
 
@@ -11,7 +11,7 @@
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/io_buffer.h"
 
-namespace content {
+namespace network {
 
 // These adapters are used to transfer data between a Mojo pipe and the net
 // library.
@@ -131,6 +131,6 @@
   int bytes_to_be_read_;
 };
 
-}  // namespace content
+}  // namespace network
 
-#endif  // CONTENT_COMMON_NET_ADAPTERS_
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_NET_ADAPTERS_
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
index 95d7e2d6..804c8697 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h
@@ -4,12 +4,17 @@
 #ifndef SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_OS_METRICS_H_
 #define SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_MEMORY_INSTRUMENTATION_OS_METRICS_H_
 
+#include "base/gtest_prod_util.h"
 #include "base/process/process_handle.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "build/build_config.h"
 #include "services/resource_coordinator/public/cpp/resource_coordinator_export.h"
 #include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 
+namespace profiling {
+FORWARD_DECLARE_TEST(ProfilingJsonExporterTest, MemoryMaps);
+};
+
 namespace memory_instrumentation {
 
 class SERVICES_RESOURCE_COORDINATOR_PUBLIC_CPP_EXPORT OSMetrics {
@@ -21,6 +26,7 @@
   FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, ParseProcSmaps);
   FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, TestWinModuleReading);
   FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, TestMachOReading);
+  FRIEND_TEST_ALL_PREFIXES(profiling::ProfilingJsonExporterTest, MemoryMaps);
   static std::vector<mojom::VmRegionPtr> GetProcessMemoryMaps(base::ProcessId);
 
 #if defined(OS_LINUX) || defined(OS_ANDROID)
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc
index aadb8a7..74941237 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc
@@ -37,48 +37,6 @@
       base::StringPrintf("%" PRIx32, os_dump.private_footprint_kb * 1024));
 }
 
-void MemoryMapsAsValueInto(TracedValue* value,
-                           const std::vector<mojom::VmRegionPtr>& memory_maps) {
-  static const char kHexFmt[] = "%" PRIx64;
-
-  // Refer to the design doc goo.gl/sxfFY8 for the semantics of these fields.
-  value->BeginArray("vm_regions");
-  for (const auto& region : memory_maps) {
-    value->BeginDictionary();
-
-    value->SetString("sa", base::StringPrintf(kHexFmt, region->start_address));
-    value->SetString("sz", base::StringPrintf(kHexFmt, region->size_in_bytes));
-    if (region->module_timestamp)
-      value->SetString("ts",
-                       base::StringPrintf(kHexFmt, region->module_timestamp));
-    value->SetInteger("pf", region->protection_flags);
-    value->SetString("mf", region->mapped_file);
-
-    value->BeginDictionary("bs");  // byte stats
-    value->SetString(
-        "pss",
-        base::StringPrintf(kHexFmt, region->byte_stats_proportional_resident));
-    value->SetString(
-        "pd",
-        base::StringPrintf(kHexFmt, region->byte_stats_private_dirty_resident));
-    value->SetString(
-        "pc",
-        base::StringPrintf(kHexFmt, region->byte_stats_private_clean_resident));
-    value->SetString(
-        "sd",
-        base::StringPrintf(kHexFmt, region->byte_stats_shared_dirty_resident));
-    value->SetString(
-        "sc",
-        base::StringPrintf(kHexFmt, region->byte_stats_shared_clean_resident));
-    value->SetString("sw",
-                     base::StringPrintf(kHexFmt, region->byte_stats_swapped));
-    value->EndDictionary();
-
-    value->EndDictionary();
-  }
-  value->EndArray();
-}
-
 };  // namespace
 
 TracingObserver::TracingObserver(
@@ -202,7 +160,7 @@
 
   if (memory_maps->size()) {
     traced_value->BeginDictionary("process_mmaps");
-    MemoryMapsAsValueInto(traced_value.get(), *memory_maps);
+    MemoryMapsAsValueInto(*memory_maps, traced_value.get());
     traced_value->EndDictionary();
   }
 
@@ -210,6 +168,50 @@
   return true;
 }
 
+// static
+void TracingObserver::MemoryMapsAsValueInto(
+    const std::vector<mojom::VmRegionPtr>& memory_maps,
+    TracedValue* value) {
+  static const char kHexFmt[] = "%" PRIx64;
+
+  // Refer to the design doc goo.gl/sxfFY8 for the semantics of these fields.
+  value->BeginArray("vm_regions");
+  for (const auto& region : memory_maps) {
+    value->BeginDictionary();
+
+    value->SetString("sa", base::StringPrintf(kHexFmt, region->start_address));
+    value->SetString("sz", base::StringPrintf(kHexFmt, region->size_in_bytes));
+    if (region->module_timestamp)
+      value->SetString("ts",
+                       base::StringPrintf(kHexFmt, region->module_timestamp));
+    value->SetInteger("pf", region->protection_flags);
+    value->SetString("mf", region->mapped_file);
+
+    value->BeginDictionary("bs");  // byte stats
+    value->SetString(
+        "pss",
+        base::StringPrintf(kHexFmt, region->byte_stats_proportional_resident));
+    value->SetString(
+        "pd",
+        base::StringPrintf(kHexFmt, region->byte_stats_private_dirty_resident));
+    value->SetString(
+        "pc",
+        base::StringPrintf(kHexFmt, region->byte_stats_private_clean_resident));
+    value->SetString(
+        "sd",
+        base::StringPrintf(kHexFmt, region->byte_stats_shared_dirty_resident));
+    value->SetString(
+        "sc",
+        base::StringPrintf(kHexFmt, region->byte_stats_shared_clean_resident));
+    value->SetString("sw",
+                     base::StringPrintf(kHexFmt, region->byte_stats_swapped));
+    value->EndDictionary();
+
+    value->EndDictionary();
+  }
+  value->EndArray();
+}
+
 bool TracingObserver::IsDumpModeAllowed(
     base::trace_event::MemoryDumpLevelOfDetail dump_mode) const {
   if (!memory_dump_config_)
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h b/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h
index 1533365..b21866f 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h
@@ -36,6 +36,10 @@
       const mojom::OSMemDump*,
       const std::vector<mojom::VmRegionPtr>*);
 
+  static void MemoryMapsAsValueInto(
+      const std::vector<mojom::VmRegionPtr>& memory_maps,
+      base::trace_event::TracedValue* value);
+
  private:
   // Returns true if the dump mode is allowed for current tracing session.
   bool IsDumpModeAllowed(base::trace_event::MemoryDumpLevelOfDetail) const;
diff --git a/services/service_manager/embedder/BUILD.gn b/services/service_manager/embedder/BUILD.gn
index 126bb9a..6511fc7 100644
--- a/services/service_manager/embedder/BUILD.gn
+++ b/services/service_manager/embedder/BUILD.gn
@@ -15,6 +15,7 @@
     "embedded_service_info.cc",
     "embedded_service_runner.cc",
     "manifest_utils.cc",
+    "service_manager_embedder_export.h",
   ]
 
   # iOS embeds the Service Manager but does not use service_manager::Main() (and
@@ -32,7 +33,6 @@
     sources += [
       "main.cc",
       "main_delegate.cc",
-      "service_manager_embedder_export.h",
       "set_process_title.cc",
       "set_process_title_linux.cc",
       "shared_file_util.cc",
@@ -82,12 +82,22 @@
   testonly = true
 
   sources = [
+    "embedded_instance_manager_unittest.cc",
     "manifest_utils_unittest.cc",
   ]
 
+  # These headers are duplicated here so that they can remain private in the
+  # "embedder" target. See http://crbug.com/732993 for a way to make it
+  # unnecessary to do this.
+  sources += [
+    "embedded_instance_manager.h",
+    "service_manager_embedder_export.h",
+  ]
+
   deps = [
     ":embedder",
     "//base",
+    "//base/test:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/services/service_manager/embedder/embedded_instance_manager.cc b/services/service_manager/embedder/embedded_instance_manager.cc
index b5c4104..16d2b7e 100644
--- a/services/service_manager/embedder/embedded_instance_manager.cc
+++ b/services/service_manager/embedder/embedded_instance_manager.cc
@@ -61,6 +61,7 @@
         FROM_HERE,
         base::Bind(&EmbeddedInstanceManager::QuitOnServiceSequence, this));
   }
+  thread_.reset();
 }
 
 EmbeddedInstanceManager::~EmbeddedInstanceManager() {
diff --git a/services/service_manager/embedder/embedded_instance_manager.h b/services/service_manager/embedder/embedded_instance_manager.h
index 4a9519d..c1755a66 100644
--- a/services/service_manager/embedder/embedded_instance_manager.h
+++ b/services/service_manager/embedder/embedded_instance_manager.h
@@ -29,6 +29,8 @@
 
 namespace service_manager {
 
+class EmbeddedInstanceManagerTestApi;
+
 // EmbeddedInstanceManager is an implementation detail of EmbeddedServiceRunner.
 // Outside of tests there is no need to use it directly.
 class SERVICE_MANAGER_EMBEDDER_EXPORT EmbeddedInstanceManager
@@ -44,6 +46,7 @@
 
  private:
   friend class base::RefCountedThreadSafe<EmbeddedInstanceManager>;
+  friend class EmbeddedInstanceManagerTestApi;
 
   ~EmbeddedInstanceManager();
 
diff --git a/services/service_manager/embedder/embedded_instance_manager_unittest.cc b/services/service_manager/embedder/embedded_instance_manager_unittest.cc
new file mode 100644
index 0000000..dc011ed
--- /dev/null
+++ b/services/service_manager/embedder/embedded_instance_manager_unittest.cc
@@ -0,0 +1,68 @@
+// 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 "services/service_manager/embedder/embedded_instance_manager.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread.h"
+#include "services/service_manager/embedder/embedded_service_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace service_manager {
+namespace {
+
+void OnQuit(bool* quit_called) {
+  *quit_called = true;
+}
+
+std::unique_ptr<Service> CreateTestService() {
+  return base::MakeUnique<Service>();
+}
+
+}  // namespace
+
+class EmbeddedInstanceManagerTestApi {
+ public:
+  EmbeddedInstanceManagerTestApi(EmbeddedInstanceManager* instance)
+      : instance_(instance) {}
+
+  base::Thread* GetThread() { return instance_->thread_.get(); }
+
+ private:
+  EmbeddedInstanceManager* instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(EmbeddedInstanceManagerTestApi);
+};
+
+TEST(EmbeddedInstanceManager, ShutdownWaitsForThreadToQuit) {
+  base::test::ScopedTaskEnvironment scoped_task_environment;
+  EmbeddedServiceInfo embedded_service_info;
+  embedded_service_info.use_own_thread = true;
+  embedded_service_info.factory = base::Bind(&CreateTestService);
+  bool quit_called = false;
+  scoped_refptr<EmbeddedInstanceManager> instance_manager =
+      new EmbeddedInstanceManager("test", embedded_service_info,
+                                  base::Bind(&OnQuit, &quit_called));
+  instance_manager->BindServiceRequest(nullptr);
+  EmbeddedInstanceManagerTestApi test_api(instance_manager.get());
+  ASSERT_TRUE(test_api.GetThread());
+  scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner =
+      test_api.GetThread()->task_runner();
+  instance_manager->ShutDown();
+  EXPECT_FALSE(test_api.GetThread());
+  // Further verification the thread was shutdown.
+  EXPECT_FALSE(
+      thread_task_runner->PostTask(FROM_HERE, base::Bind(&base::DoNothing)));
+  // Because Shutdown() was explicitly called with the thread running the
+  // quit closure should not have run.
+  EXPECT_FALSE(quit_called);
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/test/BUILD.gn b/services/service_manager/public/cpp/test/BUILD.gn
index ee7880c..3d231b2 100644
--- a/services/service_manager/public/cpp/test/BUILD.gn
+++ b/services/service_manager/public/cpp/test/BUILD.gn
@@ -46,3 +46,20 @@
     "//base/test:test_support",
   ]
 }
+
+source_set("test_support") {
+  sources = [
+    "test_connector_factory.cc",
+    "test_connector_factory.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//services/service_manager/public/cpp",
+    "//services/service_manager/public/interfaces",
+  ]
+
+  deps = [
+    "//mojo/public/cpp/bindings",
+  ]
+}
diff --git a/services/service_manager/public/cpp/test/test_connector_factory.cc b/services/service_manager/public/cpp/test/test_connector_factory.cc
new file mode 100644
index 0000000..6d3e2b8e
--- /dev/null
+++ b/services/service_manager/public/cpp/test/test_connector_factory.cc
@@ -0,0 +1,79 @@
+// 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 "services/service_manager/public/cpp/test/test_connector_factory.h"
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/interfaces/connector.mojom.h"
+
+namespace service_manager {
+
+namespace {
+
+class TestConnectorImpl : public mojom::Connector {
+ public:
+  TestConnectorImpl(TestConnectorFactory* factory, Service* service)
+      : factory_(factory), service_(service) {}
+
+  ~TestConnectorImpl() override = default;
+
+  void BindInterface(const Identity& target,
+                     const std::string& interface_name,
+                     mojo::ScopedMessagePipeHandle interface_pipe,
+                     BindInterfaceCallback callback) override {
+    service_->OnBindInterface(
+        BindSourceInfo(factory_->source_identity(), CapabilitySet()),
+        interface_name, std::move(interface_pipe));
+    std::move(callback).Run(mojom::ConnectResult::SUCCEEDED, Identity());
+  }
+
+  void StartService(const Identity& target,
+                    StartServiceCallback callback) override {
+    NOTREACHED();
+  }
+
+  void StartServiceWithProcess(
+      const Identity& identity,
+      mojo::ScopedMessagePipeHandle service,
+      mojom::PIDReceiverRequest pid_receiver_request,
+      StartServiceWithProcessCallback callback) override {
+    NOTREACHED();
+  }
+
+  void Clone(mojom::ConnectorRequest request) override {
+    bindings_.AddBinding(this, std::move(request));
+  }
+
+  void FilterInterfaces(const std::string& spec,
+                        const Identity& source,
+                        mojom::InterfaceProviderRequest source_request,
+                        mojom::InterfaceProviderPtr target) override {
+    NOTREACHED();
+  }
+
+ private:
+  TestConnectorFactory* const factory_;
+  Service* const service_;
+  mojo::BindingSet<mojom::Connector> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestConnectorImpl);
+};
+
+}  // namespace
+
+TestConnectorFactory::TestConnectorFactory(Service* service)
+    : source_identity_("TestConnectorFactory"),
+      impl_(base::MakeUnique<TestConnectorImpl>(this, service)) {}
+
+TestConnectorFactory::~TestConnectorFactory() = default;
+
+std::unique_ptr<Connector> TestConnectorFactory::CreateConnector() {
+  mojom::ConnectorPtr proxy;
+  impl_->Clone(mojo::MakeRequest(&proxy));
+  return base::MakeUnique<Connector>(std::move(proxy));
+}
+
+}  // namespace service_manager
diff --git a/services/service_manager/public/cpp/test/test_connector_factory.h b/services/service_manager/public/cpp/test/test_connector_factory.h
new file mode 100644
index 0000000..6573893
--- /dev/null
+++ b/services/service_manager/public/cpp/test/test_connector_factory.h
@@ -0,0 +1,71 @@
+// 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 SERVICES_SERVICE_MANAGER_PUBLIC_CPP_TEST_TEST_CONNECTOR_FACTORY_H_
+#define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_TEST_TEST_CONNECTOR_FACTORY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/interfaces/connector.mojom.h"
+
+namespace service_manager {
+
+class Service;
+
+// Creates Connector instances which route BindInterface requests directly to
+// a given Service implementation. Useful for testing production code which is
+// parameterized over a Connector, while bypassing all the Service Manager
+// machinery. Typical usage should look something like:
+//
+//     TEST(MyTest, Foo) {
+//       MyServiceImpl impl;  // Your implementation of service_manager::Service
+//       TestConnectorFactory connector_factory(&impl);
+//       std::unique_ptr<service_manager::Connector> connector =
+//           connector_factory.CreateConnector();
+//       RunSomeClientCode(connector.get());
+//     }
+//
+// Where |RunSomeClientCode()| would typically be some production code that
+// expects a functioning Connector and uses it to connect to the service you're
+// testing.
+//
+// Note that Connectors created by this factory ignore the target service name
+// in BindInterface calls: interface requests are always routed to a single
+// target Service instance.
+class TestConnectorFactory {
+ public:
+  // Constructs a new TestConnectorFactory which creates Connectors whose
+  // requests are routed directly to |service|. |service| is not owned and must
+  // outlive this TestConnectorFactory instance.
+  explicit TestConnectorFactory(Service* service);
+  ~TestConnectorFactory();
+
+  // Allows a test to override the default Identity seen by the target service
+  // when it receives OnBindInterface requests from this factory's Connectors.
+  //
+  // This is useful if a Service implementation cares about the source identity
+  // services making incoming interface requests. Otherwise it can be ignored.
+  void set_source_identity(const Identity& identity) {
+    source_identity_ = identity;
+  }
+
+  const Identity& source_identity() const { return source_identity_; }
+
+  // Creates a new connector which routes BindInterfaces requests directly to
+  // the Service instance associated with this factory.
+  std::unique_ptr<Connector> CreateConnector();
+
+ private:
+  Identity source_identity_;
+
+  std::unique_ptr<mojom::Connector> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestConnectorFactory);
+};
+
+}  // namespace service_manager
+
+#endif  // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_TEST_TEST_CONNECTOR_FACTORY_H_
diff --git a/services/service_manager/tests/BUILD.gn b/services/service_manager/tests/BUILD.gn
index ebd0dcd..9a3ea075 100644
--- a/services/service_manager/tests/BUILD.gn
+++ b/services/service_manager/tests/BUILD.gn
@@ -18,6 +18,10 @@
 service_test("service_manager_unittests") {
   catalog = ":service_manager_unittests_catalog"
 
+  sources = [
+    "test_support_unittest.cc",
+  ]
+
   deps = [
     ":interfaces",
     "//base",
@@ -32,6 +36,7 @@
     "//services/service_manager/background/tests:unittests",
     "//services/service_manager/embedder:unittests",
     "//services/service_manager/public/cpp",
+    "//services/service_manager/public/cpp/test:test_support",
     "//services/service_manager/public/interfaces",
     "//services/service_manager/runner/host:unittests",
     "//services/service_manager/tests/connect",
diff --git a/services/service_manager/tests/test_support_unittest.cc b/services/service_manager/tests/test_support_unittest.cc
new file mode 100644
index 0000000..add4038
--- /dev/null
+++ b/services/service_manager/tests/test_support_unittest.cc
@@ -0,0 +1,108 @@
+// 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/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/test/test_connector_factory.h"
+#include "services/service_manager/tests/test.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace service_manager {
+
+namespace {
+
+// This is a test service used to demonstrate usage of TestConnectorFactory.
+// See documentation on TestConnectorFactory for more details about usage.
+class TestServiceImpl : public Service {
+ public:
+  using OnBindInterfaceCallback = base::Callback<void(const Identity&)>;
+
+  TestServiceImpl() = default;
+  ~TestServiceImpl() override = default;
+
+  void set_on_bind_interface_callback(const OnBindInterfaceCallback& callback) {
+    on_bind_interface_callback_ = callback;
+  }
+
+  // Service:
+  void OnBindInterface(const BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override {
+    if (interface_name == TestC::Name_)
+      c_bindings_.AddBinding(&c_impl_, TestCRequest(std::move(interface_pipe)));
+
+    if (on_bind_interface_callback_)
+      on_bind_interface_callback_.Run(source_info.identity);
+  }
+
+ private:
+  // A simple test interface on the service, which can be pinged by test code
+  // to verify a working service connection.
+  class TestCImpl : public TestC {
+   public:
+    TestCImpl() = default;
+    ~TestCImpl() override = default;
+
+    // TestC:
+    void C(CCallback callback) override { std::move(callback).Run(); }
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(TestCImpl);
+  };
+
+  TestCImpl c_impl_;
+  mojo::BindingSet<TestC> c_bindings_;
+  OnBindInterfaceCallback on_bind_interface_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestServiceImpl);
+};
+
+}  // namespace
+
+TEST(ServiceManagerTestSupport, TestConnectorFactory) {
+  base::test::ScopedTaskEnvironment task_environment;
+
+  TestServiceImpl service;
+  TestConnectorFactory factory(&service);
+  std::unique_ptr<Connector> connector = factory.CreateConnector();
+
+  TestCPtr c;
+  connector->BindInterface("ignored", &c);
+
+  base::RunLoop loop;
+  c->C(loop.QuitClosure());
+  loop.Run();
+}
+
+TEST(ServiceManagerTestSupport, TestConnectorFactoryOverrideSourceIdentity) {
+  base::test::ScopedTaskEnvironment task_environment;
+
+  TestServiceImpl service;
+
+  static const std::string kTestSourceIdentity = "worst. service. ever.";
+  TestConnectorFactory factory(&service);
+  factory.set_source_identity(Identity(kTestSourceIdentity));
+  std::unique_ptr<Connector> connector = factory.CreateConnector();
+
+  // Verify that the test service sees our overridden source identity.
+  service.set_on_bind_interface_callback(
+      base::Bind([](const Identity& source_identity) {
+        EXPECT_EQ(kTestSourceIdentity, source_identity.name());
+      }));
+
+  TestCPtr c;
+  connector->BindInterface("ignored", &c);
+
+  base::RunLoop loop;
+  c->C(loop.QuitClosure());
+  loop.Run();
+}
+
+}  // namespace service_manager
diff --git a/services/ui/public/interfaces/ime/ime.mojom b/services/ui/public/interfaces/ime/ime.mojom
index 8251ac6..e9383ea5 100644
--- a/services/ui/public/interfaces/ime/ime.mojom
+++ b/services/ui/public/interfaces/ime/ime.mojom
@@ -57,8 +57,8 @@
 };
 
 // Represents an underlined segment of text currently composed by IME.
-// Corresponds to ui::CompositionUnderline.
-struct CompositionUnderline {
+// Corresponds to ui::ImeTextSpan.
+struct ImeTextSpan {
   uint32 start_offset;
   uint32 end_offset;
   bool thick;
@@ -70,7 +70,7 @@
 // ui::CompositionText.
 struct CompositionText {
   string text;
-  array<CompositionUnderline> underlines;
+  array<ImeTextSpan> ime_text_spans;
   gfx.mojom.Range selection;
 };
 
@@ -167,7 +167,7 @@
   // Sets composition text and attributes. See comments for
   // ui::TextInputClient::SetCompositionText() for more details.
   SetCompositionText(CompositionText composition);
-  
+
   // Converts current composition text into final content.
   ConfirmCompositionText();
 
diff --git a/services/ui/public/interfaces/ime/ime.typemap b/services/ui/public/interfaces/ime/ime.typemap
index 0327e43..51ee8a4 100644
--- a/services/ui/public/interfaces/ime/ime.typemap
+++ b/services/ui/public/interfaces/ime/ime.typemap
@@ -6,7 +6,7 @@
 public_headers = [
   "//ui/base/ime/candidate_window.h",
   "//ui/base/ime/composition_text.h",
-  "//ui/base/ime/composition_underline.h",
+  "//ui/base/ime/ime_text_span.h",
   "//ui/base/ime/text_input_mode.h",
   "//ui/base/ime/text_input_type.h",
 ]
@@ -25,7 +25,7 @@
   "ui.mojom.CandidateWindowEntry=ui::CandidateWindow::Entry",
   "ui.mojom.CandidateWindowProperties=ui::CandidateWindow::CandidateWindowProperty",
   "ui.mojom.CompositionText=ui::CompositionText",
-  "ui.mojom.CompositionUnderline=ui::CompositionUnderline",
+  "ui.mojom.ImeTextSpan=ui::ImeTextSpan",
   "ui.mojom.TextInputMode=ui::TextInputMode",
   "ui.mojom.TextInputType=ui::TextInputType",
 ]
diff --git a/services/ui/public/interfaces/ime/ime_struct_traits.cc b/services/ui/public/interfaces/ime/ime_struct_traits.cc
index a184652..1e729dd 100644
--- a/services/ui/public/interfaces/ime/ime_struct_traits.cc
+++ b/services/ui/public/interfaces/ime/ime_struct_traits.cc
@@ -40,10 +40,9 @@
 }
 
 // static
-bool StructTraits<ui::mojom::CompositionUnderlineDataView,
-                  ui::CompositionUnderline>::
-    Read(ui::mojom::CompositionUnderlineDataView data,
-         ui::CompositionUnderline* out) {
+bool StructTraits<ui::mojom::ImeTextSpanDataView, ui::ImeTextSpan>::Read(
+    ui::mojom::ImeTextSpanDataView data,
+    ui::ImeTextSpan* out) {
   if (data.is_null())
     return false;
   out->start_offset = data.start_offset();
@@ -58,7 +57,7 @@
 bool StructTraits<ui::mojom::CompositionTextDataView, ui::CompositionText>::
     Read(ui::mojom::CompositionTextDataView data, ui::CompositionText* out) {
   return !data.is_null() && data.ReadText(&out->text) &&
-         data.ReadUnderlines(&out->underlines) &&
+         data.ReadImeTextSpans(&out->ime_text_spans) &&
          data.ReadSelection(&out->selection);
 }
 
diff --git a/services/ui/public/interfaces/ime/ime_struct_traits.h b/services/ui/public/interfaces/ime/ime_struct_traits.h
index 616f3f2..100d1c4 100644
--- a/services/ui/public/interfaces/ime/ime_struct_traits.h
+++ b/services/ui/public/interfaces/ime/ime_struct_traits.h
@@ -9,7 +9,7 @@
 #include "services/ui/public/interfaces/ime/ime.mojom-shared.h"
 #include "ui/base/ime/candidate_window.h"
 #include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/composition_underline.h"
+#include "ui/base/ime/ime_text_span.h"
 #include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ime/text_input_type.h"
 
@@ -74,30 +74,12 @@
 };
 
 template <>
-struct StructTraits<ui::mojom::CompositionUnderlineDataView,
-                    ui::CompositionUnderline> {
-  static uint32_t start_offset(const ui::CompositionUnderline& c) {
-    return c.start_offset;
-  }
-  static uint32_t end_offset(const ui::CompositionUnderline& c) {
-    return c.end_offset;
-  }
-  static uint32_t color(const ui::CompositionUnderline& c) { return c.color; }
-  static uint32_t thick(const ui::CompositionUnderline& c) { return c.thick; }
-  static uint32_t background_color(const ui::CompositionUnderline& c) {
-    return c.background_color;
-  }
-  static bool Read(ui::mojom::CompositionUnderlineDataView data,
-                   ui::CompositionUnderline* out);
-};
-
-template <>
 struct StructTraits<ui::mojom::CompositionTextDataView, ui::CompositionText> {
   static std::string text(const ui::CompositionText& c) {
     return base::UTF16ToUTF8(c.text);
   }
-  static ui::CompositionUnderlines underlines(const ui::CompositionText& c) {
-    return c.underlines;
+  static ui::ImeTextSpans ime_text_spans(const ui::CompositionText& c) {
+    return c.ime_text_spans;
   }
   static gfx::Range selection(const ui::CompositionText& c) {
     return c.selection;
@@ -107,6 +89,20 @@
 };
 
 template <>
+struct StructTraits<ui::mojom::ImeTextSpanDataView, ui::ImeTextSpan> {
+  static uint32_t start_offset(const ui::ImeTextSpan& c) {
+    return c.start_offset;
+  }
+  static uint32_t end_offset(const ui::ImeTextSpan& c) { return c.end_offset; }
+  static uint32_t color(const ui::ImeTextSpan& c) { return c.color; }
+  static uint32_t thick(const ui::ImeTextSpan& c) { return c.thick; }
+  static uint32_t background_color(const ui::ImeTextSpan& c) {
+    return c.background_color;
+  }
+  static bool Read(ui::mojom::ImeTextSpanDataView data, ui::ImeTextSpan* out);
+};
+
+template <>
 struct EnumTraits<ui::mojom::TextInputMode, ui::TextInputMode> {
   static ui::mojom::TextInputMode ToMojom(ui::TextInputMode text_input_mode);
   static bool FromMojom(ui::mojom::TextInputMode input, ui::TextInputMode* out);
diff --git a/services/ui/public/interfaces/ime/ime_struct_traits_unittest.cc b/services/ui/public/interfaces/ime/ime_struct_traits_unittest.cc
index cfaec6f8..c718f82 100644
--- a/services/ui/public/interfaces/ime/ime_struct_traits_unittest.cc
+++ b/services/ui/public/interfaces/ime/ime_struct_traits_unittest.cc
@@ -12,7 +12,7 @@
 #include "services/ui/public/interfaces/ime/ime_struct_traits_test.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/composition_text.h"
-#include "ui/base/ime/composition_underline.h"
+#include "ui/base/ime/ime_text_span.h"
 #include "ui/gfx/range/mojo/range_struct_traits.h"
 
 namespace ui {
@@ -110,9 +110,9 @@
 TEST_F(IMEStructTraitsTest, CompositionText) {
   CompositionText input;
   input.text = base::UTF8ToUTF16("abcdefghij");
-  input.underlines.push_back(CompositionUnderline(0, 2, SK_ColorGRAY, false));
-  input.underlines.push_back(
-      CompositionUnderline(3, 6, SK_ColorRED, true, SK_ColorGREEN));
+  input.ime_text_spans.push_back(ImeTextSpan(0, 2, SK_ColorGRAY, false));
+  input.ime_text_spans.push_back(
+      ImeTextSpan(3, 6, SK_ColorRED, true, SK_ColorGREEN));
   input.selection = gfx::Range(1, 7);
 
   CompositionText output;
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index c6a9073..60ccaffcc 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -125,7 +125,6 @@
 
   deps = [
     "//components/viz/host",
-    "//components/viz/host/hit_test",
     "//gpu/command_buffer/client",
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/ipc/client",
diff --git a/services/viz/public/interfaces/compositing/BUILD.gn b/services/viz/public/interfaces/compositing/BUILD.gn
index e9c3bf6..c157364 100644
--- a/services/viz/public/interfaces/compositing/BUILD.gn
+++ b/services/viz/public/interfaces/compositing/BUILD.gn
@@ -12,6 +12,7 @@
     "render_pass.mojom",
     "resource_settings.mojom",
     "returned_resource.mojom",
+    "shared_bitmap_allocation_notifier.mojom",
     "surface_info.mojom",
     "surface_sequence.mojom",
   ]
diff --git a/cc/ipc/shared_bitmap_allocation_notifier.mojom b/services/viz/public/interfaces/compositing/shared_bitmap_allocation_notifier.mojom
similarity index 96%
rename from cc/ipc/shared_bitmap_allocation_notifier.mojom
rename to services/viz/public/interfaces/compositing/shared_bitmap_allocation_notifier.mojom
index a10eff2..d8017cd 100644
--- a/cc/ipc/shared_bitmap_allocation_notifier.mojom
+++ b/services/viz/public/interfaces/compositing/shared_bitmap_allocation_notifier.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-module cc.mojom;
+module viz.mojom;
 
 import "gpu/ipc/common/mailbox.mojom";
 
diff --git a/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom b/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom
index ff251be..f368cd08 100644
--- a/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom
+++ b/services/viz/public/interfaces/hit_test/hit_test_region_list.mojom
@@ -45,9 +45,6 @@
 };
 
 struct HitTestRegionList {
-  // SurfaceId corresponding to this HitTestData.
-  cc.mojom.SurfaceId surface_id;
-
   // Flags indicate how to handle events that match no sub-regions.
   // kHitTestMine routes un-matched events to this surface (opaque).
   // kHitTestIgnore keeps previous match in the parent (transparent).
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 675126e..e734274 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -220,8 +220,8 @@
 #define SK_SUPPORT_LEGACY_DELTA_AA
 #endif
 
-#ifndef SK_SUPPORT_LEGACY_ROTATED_SHADERS
-#define SK_SUPPORT_LEGACY_ROTATED_SHADERS
+#ifndef SK_SUPPORT_LEGACY_RP_BLENDS
+#define SK_SUPPORT_LEGACY_RP_BLENDS
 #endif
 
 ///////////////////////// Imported from BUILD.gn and skia_common.gypi
diff --git a/storage/DEPS b/storage/DEPS
index 5d45d0bc..c4e59ed 100644
--- a/storage/DEPS
+++ b/storage/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+mojo",
   "+net",
+  "+services/network/public/cpp",
   "+sql",
   "+third_party/leveldatabase",
   "+third_party/sqlite",
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index b5f8a15..a64d797f 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -38,6 +38,8 @@
     "blob/blob_url_request_job.h",
     "blob/blob_url_request_job_factory.cc",
     "blob/blob_url_request_job_factory.h",
+    "blob/mojo_blob_reader.cc",
+    "blob/mojo_blob_reader.h",
     "blob/scoped_file.cc",
     "blob/scoped_file.h",
     "blob/shareable_blob_data_item.cc",
@@ -207,6 +209,7 @@
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
     "//net",
+    "//services/network/public/cpp",
     "//sql",
     "//third_party/leveldatabase",
     "//third_party/sqlite",
@@ -293,6 +296,7 @@
     ":browser",
     ":test_support",
     "//base/test:test_support",
+    "//mojo/common",
     "//mojo/edk/system",
     "//net:test_support",
     "//sql:test_support",
diff --git a/storage/browser/blob/blob_impl.cc b/storage/browser/blob/blob_impl.cc
index ab41aba..6db0007 100644
--- a/storage/browser/blob/blob_impl.cc
+++ b/storage/browser/blob/blob_impl.cc
@@ -4,9 +4,42 @@
 
 #include "storage/browser/blob/blob_impl.h"
 
+#include <utility>
 #include "storage/browser/blob/blob_data_handle.h"
+#include "storage/browser/blob/mojo_blob_reader.h"
 
 namespace storage {
+namespace {
+
+class ReaderDelegate : public MojoBlobReader::Delegate {
+ public:
+  ReaderDelegate(mojo::ScopedDataPipeProducerHandle handle,
+                 mojom::BlobReaderClientPtr client)
+      : handle_(std::move(handle)), client_(std::move(client)) {}
+
+  mojo::ScopedDataPipeProducerHandle PassDataPipe() override {
+    return std::move(handle_);
+  }
+
+  MojoBlobReader::Delegate::RequestSideData DidCalculateSize(
+      uint64_t total_size,
+      uint64_t content_size) override {
+    if (client_)
+      client_->OnCalculatedSize(total_size, content_size);
+    return MojoBlobReader::Delegate::DONT_REQUEST_SIDE_DATA;
+  }
+
+  void OnComplete(net::Error result, uint64_t total_written_bytes) override {
+    if (client_)
+      client_->OnComplete(result, total_written_bytes);
+  }
+
+ private:
+  mojo::ScopedDataPipeProducerHandle handle_;
+  mojom::BlobReaderClientPtr client_;
+};
+
+}  // namespace
 
 // static
 base::WeakPtr<BlobImpl> BlobImpl::Create(std::unique_ptr<BlobDataHandle> handle,
@@ -19,6 +52,23 @@
   bindings_.AddBinding(this, std::move(request));
 }
 
+void BlobImpl::ReadRange(uint64_t offset,
+                         uint64_t length,
+                         mojo::ScopedDataPipeProducerHandle handle,
+                         mojom::BlobReaderClientPtr client) {
+  MojoBlobReader::Create(
+      nullptr, handle_.get(),
+      net::HttpByteRange::Bounded(offset, offset + length - 1),
+      base::MakeUnique<ReaderDelegate>(std::move(handle), std::move(client)));
+}
+
+void BlobImpl::ReadAll(mojo::ScopedDataPipeProducerHandle handle,
+                       mojom::BlobReaderClientPtr client) {
+  MojoBlobReader::Create(
+      nullptr, handle_.get(), net::HttpByteRange(),
+      base::MakeUnique<ReaderDelegate>(std::move(handle), std::move(client)));
+}
+
 void BlobImpl::GetInternalUUID(GetInternalUUIDCallback callback) {
   std::move(callback).Run(handle_->uuid());
 }
diff --git a/storage/browser/blob/blob_impl.h b/storage/browser/blob/blob_impl.h
index 148ae96..9693504 100644
--- a/storage/browser/blob/blob_impl.h
+++ b/storage/browser/blob/blob_impl.h
@@ -20,6 +20,12 @@
                                         mojom::BlobRequest request);
 
   void Clone(mojom::BlobRequest request) override;
+  void ReadRange(uint64_t offset,
+                 uint64_t length,
+                 mojo::ScopedDataPipeProducerHandle handle,
+                 mojom::BlobReaderClientPtr client) override;
+  void ReadAll(mojo::ScopedDataPipeProducerHandle handle,
+               mojom::BlobReaderClientPtr client) override;
   void GetInternalUUID(GetInternalUUIDCallback callback) override;
 
   void FlushForTesting();
diff --git a/storage/browser/blob/blob_impl_unittest.cc b/storage/browser/blob/blob_impl_unittest.cc
index 051c715..19bdc45 100644
--- a/storage/browser/blob/blob_impl_unittest.cc
+++ b/storage/browser/blob/blob_impl_unittest.cc
@@ -4,7 +4,10 @@
 
 #include "storage/browser/blob/blob_impl.h"
 
+#include "base/task_scheduler/post_task.h"
 #include "base/test/scoped_task_environment.h"
+#include "mojo/common/data_pipe_drainer.h"
+#include "net/base/net_errors.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_storage_context.h"
@@ -12,6 +15,49 @@
 
 namespace storage {
 
+namespace {
+
+class DataPipeReader : public mojo::common::DataPipeDrainer::Client {
+ public:
+  DataPipeReader(std::string* data_out, base::OnceClosure done_callback)
+      : data_out_(data_out), done_callback_(std::move(done_callback)) {}
+
+  void OnDataAvailable(const void* data, size_t num_bytes) override {
+    data_out_->append(static_cast<const char*>(data), num_bytes);
+  }
+
+  void OnDataComplete() override { std::move(done_callback_).Run(); }
+
+ private:
+  std::string* data_out_;
+  base::OnceClosure done_callback_;
+};
+
+class MockBlobReaderClient : public mojom::BlobReaderClient {
+ public:
+  void OnCalculatedSize(uint64_t total_size,
+                        uint64_t expected_content_size) override {
+    total_size_ = total_size;
+    expected_content_size_ = expected_content_size;
+    calculated_size_ = true;
+  }
+
+  void OnComplete(int32_t status, uint64_t data_length) override {
+    status_ = static_cast<net::Error>(status);
+    data_length_ = data_length;
+    completed_ = true;
+  }
+
+  bool calculated_size_ = false;
+  uint64_t total_size_ = 0;
+  uint64_t expected_content_size_ = 0;
+  bool completed_ = false;
+  net::Error status_ = net::OK;
+  uint64_t data_length_ = 0;
+};
+
+}  // namespace
+
 class BlobImplTest : public testing::Test {
  public:
   void SetUp() override { context_ = base::MakeUnique<BlobStorageContext>(); }
@@ -39,6 +85,15 @@
     return received_uuid;
   }
 
+  std::string ReadDataPipe(mojo::ScopedDataPipeConsumerHandle pipe) {
+    base::RunLoop loop;
+    std::string data;
+    DataPipeReader reader(&data, loop.QuitClosure());
+    mojo::common::DataPipeDrainer drainer(&reader, std::move(pipe));
+    loop.Run();
+    return data;
+  }
+
  protected:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<BlobStorageContext> context_;
@@ -82,4 +137,198 @@
   EXPECT_FALSE(blob);
 }
 
+TEST_F(BlobImplTest, ReadAll) {
+  const std::string kId = "id";
+  const std::string kContents = "hello world";
+  auto handle = CreateBlobFromString(kId, kContents);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  MockBlobReaderClient client;
+  mojom::BlobReaderClientPtr client_ptr;
+  mojo::Binding<mojom::BlobReaderClient> client_binding(
+      &client, MakeRequest(&client_ptr));
+
+  mojo::DataPipe pipe;
+  ptr->ReadAll(std::move(pipe.producer_handle), std::move(client_ptr));
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ(kContents, received);
+
+  client_binding.FlushForTesting();
+  EXPECT_TRUE(client.calculated_size_);
+  EXPECT_EQ(kContents.size(), client.total_size_);
+  EXPECT_EQ(kContents.size(), client.expected_content_size_);
+
+  EXPECT_TRUE(client.completed_);
+  EXPECT_EQ(net::OK, client.status_);
+  EXPECT_EQ(kContents.size(), client.data_length_);
+}
+
+TEST_F(BlobImplTest, ReadAll_WithoutClient) {
+  const std::string kId = "id";
+  const std::string kContents = "hello world";
+  auto handle = CreateBlobFromString(kId, kContents);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  mojo::DataPipe pipe;
+  ptr->ReadAll(std::move(pipe.producer_handle), nullptr);
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ(kContents, received);
+}
+
+TEST_F(BlobImplTest, ReadAll_BrokenBlob) {
+  const std::string kId = "id";
+  auto handle = context_->AddBrokenBlob(
+      kId, "", "", BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  MockBlobReaderClient client;
+  mojom::BlobReaderClientPtr client_ptr;
+  mojo::Binding<mojom::BlobReaderClient> client_binding(
+      &client, MakeRequest(&client_ptr));
+
+  mojo::DataPipe pipe;
+  ptr->ReadAll(std::move(pipe.producer_handle), std::move(client_ptr));
+
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ("", received);
+
+  client_binding.FlushForTesting();
+  EXPECT_FALSE(client.calculated_size_);
+  EXPECT_TRUE(client.completed_);
+  EXPECT_EQ(net::ERR_FAILED, client.status_);
+  EXPECT_EQ(0u, client.data_length_);
+}
+
+TEST_F(BlobImplTest, ReadRange) {
+  const std::string kId = "id";
+  const std::string kContents = "hello world";
+  auto handle = CreateBlobFromString(kId, kContents);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  MockBlobReaderClient client;
+  mojom::BlobReaderClientPtr client_ptr;
+  mojo::Binding<mojom::BlobReaderClient> client_binding(
+      &client, MakeRequest(&client_ptr));
+
+  mojo::DataPipe pipe;
+  ptr->ReadRange(2, 5, std::move(pipe.producer_handle), std::move(client_ptr));
+
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ(kContents.substr(2, 5), received);
+
+  client_binding.FlushForTesting();
+  EXPECT_TRUE(client.calculated_size_);
+  EXPECT_EQ(kContents.size(), client.total_size_);
+  EXPECT_EQ(5u, client.expected_content_size_);
+
+  EXPECT_TRUE(client.completed_);
+  EXPECT_EQ(net::OK, client.status_);
+  EXPECT_EQ(5u, client.data_length_);
+}
+
+TEST_F(BlobImplTest, ReadRange_WithoutClient) {
+  const std::string kId = "id";
+  const std::string kContents = "hello world";
+  auto handle = CreateBlobFromString(kId, kContents);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  mojo::DataPipe pipe;
+  ptr->ReadRange(2, 5, std::move(pipe.producer_handle), nullptr);
+
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ(kContents.substr(2, 5), received);
+}
+
+TEST_F(BlobImplTest, ReadRange_TooLargeLength) {
+  const std::string kId = "id";
+  const std::string kContents = "hello world";
+  auto handle = CreateBlobFromString(kId, kContents);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  MockBlobReaderClient client;
+  mojom::BlobReaderClientPtr client_ptr;
+  mojo::Binding<mojom::BlobReaderClient> client_binding(
+      &client, MakeRequest(&client_ptr));
+
+  mojo::DataPipe pipe;
+  ptr->ReadRange(2, 15, std::move(pipe.producer_handle), std::move(client_ptr));
+
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ(kContents.substr(2, 15), received);
+
+  client_binding.FlushForTesting();
+  EXPECT_TRUE(client.calculated_size_);
+  EXPECT_EQ(kContents.size(), client.total_size_);
+  EXPECT_EQ(kContents.size() - 2, client.expected_content_size_);
+
+  EXPECT_TRUE(client.completed_);
+  EXPECT_EQ(net::OK, client.status_);
+  EXPECT_EQ(kContents.size() - 2, client.data_length_);
+}
+
+TEST_F(BlobImplTest, ReadRange_BrokenBlob) {
+  const std::string kId = "id";
+  auto handle = context_->AddBrokenBlob(
+      kId, "", "", BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  MockBlobReaderClient client;
+  mojom::BlobReaderClientPtr client_ptr;
+  mojo::Binding<mojom::BlobReaderClient> client_binding(
+      &client, MakeRequest(&client_ptr));
+
+  mojo::DataPipe pipe;
+  ptr->ReadRange(2, 5, std::move(pipe.producer_handle), std::move(client_ptr));
+
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ("", received);
+
+  client_binding.FlushForTesting();
+  EXPECT_FALSE(client.calculated_size_);
+  EXPECT_TRUE(client.completed_);
+  EXPECT_EQ(net::ERR_FAILED, client.status_);
+  EXPECT_EQ(0u, client.data_length_);
+}
+
+TEST_F(BlobImplTest, ReadRange_InvalidRange) {
+  const std::string kId = "id";
+  const std::string kContents = "hello world";
+  auto handle = CreateBlobFromString(kId, kContents);
+
+  mojom::BlobPtr ptr;
+  BlobImpl::Create(std::move(handle), MakeRequest(&ptr));
+
+  MockBlobReaderClient client;
+  mojom::BlobReaderClientPtr client_ptr;
+  mojo::Binding<mojom::BlobReaderClient> client_binding(
+      &client, MakeRequest(&client_ptr));
+
+  base::RunLoop loop;
+  mojo::DataPipe pipe;
+  ptr->ReadRange(15, 4, std::move(pipe.producer_handle), std::move(client_ptr));
+
+  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  EXPECT_EQ("", received);
+
+  client_binding.FlushForTesting();
+  EXPECT_FALSE(client.calculated_size_);
+  EXPECT_TRUE(client.completed_);
+  EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, client.status_);
+  EXPECT_EQ(0u, client.data_length_);
+}
+
 }  // namespace storage
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc
index ee0e27d..0f0e3c8 100644
--- a/storage/browser/blob/blob_registry_impl_unittest.cc
+++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -41,6 +41,18 @@
                             std::move(request));
   }
 
+  void ReadRange(uint64_t offset,
+                 uint64_t size,
+                 mojo::ScopedDataPipeProducerHandle,
+                 mojom::BlobReaderClientPtr) override {
+    NOTREACHED();
+  }
+
+  void ReadAll(mojo::ScopedDataPipeProducerHandle,
+               mojom::BlobReaderClientPtr) override {
+    NOTREACHED();
+  }
+
   void GetInternalUUID(GetInternalUUIDCallback callback) override {
     std::move(callback).Run(uuid_);
   }
diff --git a/storage/browser/blob/blob_url_request_job.cc b/storage/browser/blob/blob_url_request_job.cc
index b1ac09e..03190ad 100644
--- a/storage/browser/blob/blob_url_request_job.cc
+++ b/storage/browser/blob/blob_url_request_job.cc
@@ -142,9 +142,9 @@
 scoped_refptr<net::HttpResponseHeaders> BlobURLRequestJob::GenerateHeaders(
     net::HttpStatusCode status_code,
     BlobDataHandle* blob_handle,
-    BlobReader* blob_reader,
     net::HttpByteRange* byte_range,
-    int64_t* content_size) {
+    uint64_t total_size,
+    uint64_t content_size) {
   std::string status("HTTP/1.1 ");
   status.append(base::IntToString(status_code));
   status.append(" ");
@@ -154,10 +154,9 @@
       new net::HttpResponseHeaders(status);
 
   if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT) {
-    *content_size = blob_reader->remaining_bytes();
     std::string content_length_header(net::HttpRequestHeaders::kContentLength);
     content_length_header.append(": ");
-    content_length_header.append(base::Int64ToString(*content_size));
+    content_length_header.append(base::Uint64ToString(content_size));
     headers->AddHeader(content_length_header);
     if (status_code == net::HTTP_PARTIAL_CONTENT) {
       DCHECK(byte_range->IsValid());
@@ -167,8 +166,7 @@
           "%" PRId64 "-%" PRId64, byte_range->first_byte_position(),
           byte_range->last_byte_position()));
       content_range_header.append("/");
-      content_range_header.append(
-          base::StringPrintf("%" PRId64, blob_reader->total_size()));
+      content_range_header.append(base::StringPrintf("%" PRId64, total_size));
       headers->AddHeader(content_range_header);
     }
     if (!blob_handle->content_type().empty()) {
@@ -323,12 +321,16 @@
 }
 
 void BlobURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
-  int64_t content_size = 0;
+  uint64_t content_size = 0;
+  uint64_t total_size = 0;
+  if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT) {
+    content_size = blob_reader_->remaining_bytes();
+    set_expected_content_size(content_size);
+    total_size = blob_reader_->total_size();
+  }
   response_info_.reset(new net::HttpResponseInfo());
-  response_info_->headers =
-      GenerateHeaders(status_code, blob_handle_.get(), blob_reader_.get(),
-                      &byte_range_, &content_size);
-  set_expected_content_size(content_size);
+  response_info_->headers = GenerateHeaders(
+      status_code, blob_handle_.get(), &byte_range_, total_size, content_size);
   if (blob_reader_)
     response_info_->metadata = blob_reader_->side_data();
 
diff --git a/storage/browser/blob/blob_url_request_job.h b/storage/browser/blob/blob_url_request_job.h
index b67aaef..00649b8 100644
--- a/storage/browser/blob/blob_url_request_job.h
+++ b/storage/browser/blob/blob_url_request_job.h
@@ -48,14 +48,14 @@
   void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override;
 
   // Helper method to create the HTTP headers for the response.
-  // |blob_handles|, |blob_reader|, |byte_range| and |content_size| are only
+  // |blob_handles|, |total_size|, |byte_range| and |content_size| are only
   // used if status_code isn't an error.
   static scoped_refptr<net::HttpResponseHeaders> GenerateHeaders(
       net::HttpStatusCode status_code,
       BlobDataHandle* blob_handle,
-      BlobReader* blob_reader,
       net::HttpByteRange* byte_range,
-      int64_t* content_size);
+      uint64_t total_size,
+      uint64_t content_size);
 
   // Helper method to map from a net error to an http status code.
   static net::HttpStatusCode NetErrorToHttpStatusCode(int error_code);
diff --git a/storage/browser/blob/mojo_blob_reader.cc b/storage/browser/blob/mojo_blob_reader.cc
new file mode 100644
index 0000000..0e29999
--- /dev/null
+++ b/storage/browser/blob/mojo_blob_reader.cc
@@ -0,0 +1,212 @@
+// 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 "storage/browser/blob/mojo_blob_reader.h"
+
+#include "net/base/io_buffer.h"
+#include "services/network/public/cpp/net_adapters.h"
+#include "storage/browser/blob/blob_data_handle.h"
+
+namespace storage {
+
+// static
+void MojoBlobReader::Create(FileSystemContext* file_system_context,
+                            const BlobDataHandle* handle,
+                            const net::HttpByteRange& range,
+                            std::unique_ptr<Delegate> delegate) {
+  new MojoBlobReader(file_system_context, handle, range, std::move(delegate));
+}
+
+MojoBlobReader::MojoBlobReader(FileSystemContext* file_system_context,
+                               const BlobDataHandle* handle,
+                               const net::HttpByteRange& range,
+                               std::unique_ptr<Delegate> delegate)
+    : delegate_(std::move(delegate)),
+      byte_range_(range),
+      blob_reader_(handle->CreateReader(file_system_context)),
+      writable_handle_watcher_(FROM_HERE,
+                               mojo::SimpleWatcher::ArmingPolicy::MANUAL),
+      peer_closed_handle_watcher_(FROM_HERE,
+                                  mojo::SimpleWatcher::ArmingPolicy::MANUAL),
+      weak_factory_(this) {
+  DCHECK(delegate_);
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::Bind(&MojoBlobReader::Start, weak_factory_.GetWeakPtr()));
+}
+
+MojoBlobReader::~MojoBlobReader() {}
+
+void MojoBlobReader::Start() {
+  if (blob_reader_->net_error()) {
+    NotifyCompletedAndDeleteIfNeeded(blob_reader_->net_error());
+    return;
+  }
+
+  BlobReader::Status size_status = blob_reader_->CalculateSize(
+      base::Bind(&MojoBlobReader::DidCalculateSize, base::Unretained(this)));
+  switch (size_status) {
+    case BlobReader::Status::NET_ERROR:
+      NotifyCompletedAndDeleteIfNeeded(blob_reader_->net_error());
+      return;
+    case BlobReader::Status::IO_PENDING:
+      return;
+    case BlobReader::Status::DONE:
+      DidCalculateSize(net::OK);
+      return;
+  }
+
+  NOTREACHED();
+}
+
+void MojoBlobReader::NotifyCompletedAndDeleteIfNeeded(int result) {
+  if (!notified_completed_) {
+    delegate_->OnComplete(static_cast<net::Error>(result),
+                          total_written_bytes_);
+    notified_completed_ = true;
+  }
+
+  bool has_data_pipe = pending_write_ || response_body_stream_.is_valid();
+  if (!has_data_pipe)
+    delete this;
+}
+
+void MojoBlobReader::DidCalculateSize(int result) {
+  if (result != net::OK) {
+    NotifyCompletedAndDeleteIfNeeded(result);
+    return;
+  }
+
+  // Apply the range requirement.
+  if (!byte_range_.ComputeBounds(blob_reader_->total_size())) {
+    NotifyCompletedAndDeleteIfNeeded(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+    return;
+  }
+
+  DCHECK_LE(byte_range_.first_byte_position(),
+            byte_range_.last_byte_position() + 1);
+  uint64_t length = base::checked_cast<uint64_t>(
+      byte_range_.last_byte_position() - byte_range_.first_byte_position() + 1);
+
+  if (blob_reader_->SetReadRange(byte_range_.first_byte_position(), length) !=
+      BlobReader::Status::DONE) {
+    NotifyCompletedAndDeleteIfNeeded(blob_reader_->net_error());
+    return;
+  }
+
+  if (delegate_->DidCalculateSize(blob_reader_->total_size(),
+                                  blob_reader_->remaining_bytes()) ==
+      Delegate::REQUEST_SIDE_DATA) {
+    if (!blob_reader_->has_side_data()) {
+      DidReadSideData(BlobReader::Status::DONE);
+    } else {
+      BlobReader::Status read_status = blob_reader_->ReadSideData(
+          base::Bind(&MojoBlobReader::DidReadSideData, base::Unretained(this)));
+      if (read_status != BlobReader::Status::IO_PENDING)
+        DidReadSideData(BlobReader::Status::DONE);
+    }
+  } else {
+    StartReading();
+  }
+}
+
+void MojoBlobReader::DidReadSideData(BlobReader::Status status) {
+  if (status != BlobReader::Status::DONE) {
+    NotifyCompletedAndDeleteIfNeeded(blob_reader_->net_error());
+    return;
+  }
+  delegate_->DidReadSideData(blob_reader_->side_data());
+  StartReading();
+}
+
+void MojoBlobReader::StartReading() {
+  response_body_stream_ = delegate_->PassDataPipe();
+  peer_closed_handle_watcher_.Watch(
+      response_body_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      base::Bind(&MojoBlobReader::OnResponseBodyStreamClosed,
+                 base::Unretained(this)));
+  peer_closed_handle_watcher_.ArmOrNotify();
+
+  writable_handle_watcher_.Watch(
+      response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+      base::Bind(&MojoBlobReader::OnResponseBodyStreamReady,
+                 base::Unretained(this)));
+
+  // Start reading...
+  ReadMore();
+}
+
+void MojoBlobReader::ReadMore() {
+  DCHECK(!pending_write_.get());
+
+  uint32_t num_bytes;
+  // TODO: we should use the abstractions in MojoAsyncResourceHandler.
+  MojoResult result = network::NetToMojoPendingBuffer::BeginWrite(
+      &response_body_stream_, &pending_write_, &num_bytes);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    // The pipe is full. We need to wait for it to have more space.
+    writable_handle_watcher_.ArmOrNotify();
+    return;
+  } else if (result != MOJO_RESULT_OK) {
+    // The response body stream is in a bad state. Bail.
+    writable_handle_watcher_.Cancel();
+    response_body_stream_.reset();
+    NotifyCompletedAndDeleteIfNeeded(net::ERR_UNEXPECTED);
+    return;
+  }
+
+  CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
+  auto buf =
+      base::MakeRefCounted<network::NetToMojoIOBuffer>(pending_write_.get());
+  int bytes_read;
+  BlobReader::Status read_status = blob_reader_->Read(
+      buf.get(), static_cast<int>(num_bytes), &bytes_read,
+      base::Bind(&MojoBlobReader::DidRead, base::Unretained(this), false));
+  switch (read_status) {
+    case BlobReader::Status::NET_ERROR:
+      NotifyCompletedAndDeleteIfNeeded(blob_reader_->net_error());
+      return;
+    case BlobReader::Status::IO_PENDING:
+      // Wait for DidRead.
+      return;
+    case BlobReader::Status::DONE:
+      if (bytes_read > 0) {
+        DidRead(true, bytes_read);
+      } else {
+        writable_handle_watcher_.Cancel();
+        pending_write_->Complete(0);
+        pending_write_ = nullptr;  // This closes the data pipe.
+        NotifyCompletedAndDeleteIfNeeded(net::OK);
+        return;
+      }
+  }
+}
+
+void MojoBlobReader::DidRead(bool completed_synchronously, int num_bytes) {
+  delegate_->DidRead(num_bytes);
+  response_body_stream_ = pending_write_->Complete(num_bytes);
+  total_written_bytes_ += num_bytes;
+  pending_write_ = nullptr;
+  if (completed_synchronously) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&MojoBlobReader::ReadMore, weak_factory_.GetWeakPtr()));
+  } else {
+    ReadMore();
+  }
+}
+
+void MojoBlobReader::OnResponseBodyStreamClosed(MojoResult result) {
+  response_body_stream_.reset();
+  pending_write_ = nullptr;
+  NotifyCompletedAndDeleteIfNeeded(net::ERR_ABORTED);
+}
+
+void MojoBlobReader::OnResponseBodyStreamReady(MojoResult result) {
+  // TODO(jam): Handle a bad |result| value.
+  DCHECK_EQ(result, MOJO_RESULT_OK);
+  ReadMore();
+}
+
+}  // namespace storage
diff --git a/storage/browser/blob/mojo_blob_reader.h b/storage/browser/blob/mojo_blob_reader.h
new file mode 100644
index 0000000..04221e1
--- /dev/null
+++ b/storage/browser/blob/mojo_blob_reader.h
@@ -0,0 +1,143 @@
+// 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 STORAGE_BROWSER_BLOB_MOJO_BLOB_READER_H_
+#define STORAGE_BROWSER_BLOB_MOJO_BLOB_READER_H_
+
+#include <memory>
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_byte_range.h"
+#include "storage/browser/blob/blob_reader.h"
+#include "storage/browser/storage_browser_export.h"
+
+namespace net {
+class IOBufferWithSize;
+}
+
+namespace network {
+class NetToMojoPendingBuffer;
+}
+
+namespace storage {
+class BlobDataHandle;
+class FileSystemContext;
+
+// Reads a blob into a data pipe. Owns itself, and owns its delegate. Self
+// destructs when reading is complete.
+class STORAGE_EXPORT MojoBlobReader {
+ public:
+  // Methods on this delegate are called in the order they are defined here.
+  // With the exception of DidRead, each method is called at most once.
+  // OnComplete is always called, and is always the last method to be called,
+  // every other method could be skipped in case of errors, or if the method is
+  // otherwise not applicable.
+  class Delegate {
+   public:
+    enum RequestSideData { REQUEST_SIDE_DATA, DONT_REQUEST_SIDE_DATA };
+
+    virtual ~Delegate() {}
+
+    // Called when the blob being read has been fully constructed and its size
+    // is known. |total_size| is the total size of the blob, while
+    // |content_size| is the size of the subset of this blob that matches the
+    // range passed to Create.
+    // Return |REQUEST_SIDE_DATA| if the blob's side data should be returned,
+    // otherwise MojoBlobReader will skip reading side data and immediately
+    // start reading the actual blob contents. Side data is used for example by
+    // service worker code to store compiled javascript alongside the script
+    // source in the cache.
+    virtual RequestSideData DidCalculateSize(uint64_t total_size,
+                                             uint64_t content_size) = 0;
+
+    // Called if DidCalculateSize returned |REQUEST_SIDE_DATA|, with the side
+    // data associated with the blob being read. If the blob doesn't have side
+    // data this method is called with null.
+    virtual void DidReadSideData(net::IOBufferWithSize* data) {}
+
+    // Called when the MojoBlobReader actually starts reading data from the
+    // blob. Should return a data pipe to which all the data read from the blob
+    // should be written.
+    virtual mojo::ScopedDataPipeProducerHandle PassDataPipe() = 0;
+
+    // Called whenever some amount of data is read from the blob and about to be
+    // written to the data pipe.
+    virtual void DidRead(int num_bytes) {}
+
+    // Called when reading the blob has finished. If an error occurs this could
+    // be the only method that gets called, but either way this method is always
+    // the last to be called, shortly before the delegate is deleted.
+    // |total_written_bytes| indicated the total number of bytes that were read
+    // from the blob, and written to the data pipe. When successful this should
+    // always be equal to the |content_size| that was passed to
+    // DidCalculateSize.
+    virtual void OnComplete(net::Error result,
+                            uint64_t total_written_bytes) = 0;
+  };
+
+  static void Create(FileSystemContext* file_system_context,
+                     const BlobDataHandle* handle,
+                     const net::HttpByteRange& range,
+                     std::unique_ptr<Delegate> delegate);
+
+ private:
+  MojoBlobReader(FileSystemContext* file_system_context,
+                 const BlobDataHandle* handle,
+                 const net::HttpByteRange& range,
+                 std::unique_ptr<Delegate> delegate);
+  ~MojoBlobReader();
+
+  void Start();
+
+  void NotifyCompletedAndDeleteIfNeeded(int result);
+
+  void DidCalculateSize(int result);
+  void DidReadSideData(BlobReader::Status status);
+  void StartReading();
+  void ReadMore();
+  void DidRead(bool completed_synchronously, int num_bytes);
+  void OnResponseBodyStreamClosed(MojoResult result);
+  void OnResponseBodyStreamReady(MojoResult result);
+
+  std::unique_ptr<Delegate> delegate_;
+
+  // The range of the blob that should be read. Could be unbounded if the entire
+  // blob is being read.
+  net::HttpByteRange byte_range_;
+
+  // Underlying BlobReader data is being read from.
+  std::unique_ptr<BlobReader> blob_reader_;
+
+  // Mojo data pipe where the data that is being read is written to. Will be
+  // null during write operations, at which time |pending_write_| owns the data
+  // pipe instead.
+  mojo::ScopedDataPipeProducerHandle response_body_stream_;
+
+  // During a write operation this owns the data pipe handle and gives access to
+  // the pipe's internal buffer where data should be written.
+  scoped_refptr<network::NetToMojoPendingBuffer> pending_write_;
+
+  // Watchers to keep track of the state of the data pipe. One watches for the
+  // pipe being writable, while the other watches for the pipe unexpectedly
+  // closing.
+  mojo::SimpleWatcher writable_handle_watcher_;
+  mojo::SimpleWatcher peer_closed_handle_watcher_;
+
+  // Total number of bytes written to the data pipe so far.
+  int64_t total_written_bytes_ = 0;
+
+  // Set to true when the delegate's OnComplete has been called. Used to make
+  // sure OnComplete isn't called more than once.
+  bool notified_completed_ = false;
+
+  base::WeakPtrFactory<MojoBlobReader> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoBlobReader);
+};
+
+}  // namespace storage
+
+#endif
diff --git a/storage/public/interfaces/blobs.mojom b/storage/public/interfaces/blobs.mojom
index eb51dd8..90654ea2 100644
--- a/storage/public/interfaces/blobs.mojom
+++ b/storage/public/interfaces/blobs.mojom
@@ -9,11 +9,41 @@
 import "mojo/common/time.mojom";
 import "url/mojo/url.mojom";
 
+// Interface that can be implemented to be informed of certain information while
+// reading the data for a blob.
+interface BlobReaderClient {
+  // Called when the size of the blob being read has been calculated:
+  //   |total_size| is the total size of the blob.
+  //   |expected_content_size| is how many bytes should be sent over the data
+  //      pipe, taking into account a range if the blob was read with ReadRange.
+  // If an error occurs while reading the blob, this method might not get called.
+  OnCalculatedSize(uint64 total_size,
+                   uint64 expected_content_size);
+
+  // Called when reading the blob finished (with success or failure). Status is
+  // a net::Error indicating any potential error that might have occurred,
+  // |data_length| tells the reader how many bytes were written into the data
+  // pipe, and can be used as a sanity check to make sure all bytes were
+  // received.
+  OnComplete(int32 status, uint64 data_length);
+};
+
 // This interface provides access to a blob in the blob system.
 interface Blob {
   // Creates a copy of this Blob reference.
   Clone(Blob& blob);
 
+  // Causes the entire contents of this blob to be written into the given data
+  // pipe. An optional BlobReaderClient will be informed of the result of the
+  // read operation.
+  ReadAll(handle<data_pipe_producer> pipe, BlobReaderClient? client);
+
+  // Causes a subrange of the contents of this blob to be written into the given
+  // data pipe. An optional BlobReaderClient will be informed of the result of
+  // the read operation.
+  ReadRange(uint64 offset, uint64 length, handle<data_pipe_producer> pipe,
+            BlobReaderClient? client);
+
   // This method is an implementation detail of the blob system. You should not
   // ever need to call it directly.
   // This returns the internal UUID of the blob, used by the blob system to
diff --git a/testing/android/docs/instrumentation.md b/testing/android/docs/instrumentation.md
index 458e03f5..cfd9f0c 100644
--- a/testing/android/docs/instrumentation.md
+++ b/testing/android/docs/instrumentation.md
@@ -51,13 +51,20 @@
 In many cases, Chromium has extended the instrumentation test framework
 classes to implement additional features.
 
-### Test runners
+### Tracing
 
-[todo](/testing/android/docs/todo.md)
+Enabling tracing during a test run allows all the function calls involved to be
+observed in a visual display (using Chrome's built-in chrome://tracing feature).
+To run a test with tracing, add the `--trace-output` flag to the command used to
+call the instrumentation test (either running the test_runner.py script, or a
+generated binary such as `run_chrome_public_test_apk`). The `--trace-output` flag
+takes a filename, which, after the test run, will contain a JSON file readable
+by chrome://tracing.
 
-### Test cases
-
-[todo](/testing/android/docs/todo.md)
+By default, the trace includes only certain function calls important to the test
+run, both within the Python test runner framework and the Java code running on
+the device. For a more detailed look, add the (no-argument) `--trace-all` flag.
+This causes every function called on the Python side to be added to the trace.
 
 ### Annotations
 
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 6e904f6..ffb4d42 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -24,16 +24,18 @@
   "Android Tests with Tracing": {
     "instrumentation_tests": [
       {
+        "args": [
+          "-f",
+          "ContextualSearchTapEventTest#*"
+        ],
         "test": "chrome_public_test_apk",
         "trace_output": true
       },
       {
-        "test": "chrome_sync_shell_test_apk",
-        "trace_output": true
+        "test": "chrome_sync_shell_test_apk"
       },
       {
-        "test": "content_shell_test_apk",
-        "trace_output": true
+        "test": "content_shell_test_apk"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 27a41f4..cf86565 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -10472,8 +10472,7 @@
   },
   "Fuchsia": {
     "additional_compile_targets": [
-      "gl_unittests",
-      "net_unittests"
+      "gl_unittests"
     ],
     "gtest_tests": [
       {
@@ -10549,6 +10548,16 @@
         "test": "mojo_system_unittests"
       },
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
+          "--test-launcher-batch-limit=200"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
+        "test": "net_unittests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": false
         },
@@ -11263,18 +11272,6 @@
             }
           ]
         },
-        "test": "oop_heap_profiling_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "android_devices": "1",
-              "device_type": "coho"
-            }
-          ]
-        },
         "test": "remoting_unittests"
       },
       {
@@ -11682,12 +11679,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "oop_heap_profiling_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "pdf_unittests"
       },
       {
@@ -12000,12 +11991,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "oop_heap_profiling_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "pdf_unittests"
       },
       {
@@ -12295,12 +12280,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "oop_heap_profiling_unit_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "pdf_unittests"
       },
       {
@@ -13668,7 +13647,7 @@
             },
             {
               "gpu": "8086:0a2e",
-              "hidpi": 0,
+              "hidpi": "0",
               "os": "Mac-10.12.5"
             }
           ],
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 78e420f..0540cb24 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -2408,413 +2408,6 @@
     "isolated_scripts": []
   },
   "Linux ChromiumOS Ozone Builder": {},
-  "Linux Debug (Intel HD 530)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "angle_end2end_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "angle_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "audio_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "gl_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--no-xvfb"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "gl_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "context_lost_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "depth_capture_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "gpu_process_launch_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "hardware_accelerated_feature_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--expected-vendor-id",
-          "8086",
-          "--expected-device-id",
-          "1912"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "info_collection_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--os-type",
-          "linux",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "maps_pixel_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--refimg-cloud-storage-bucket",
-          "chromium-gpu-archive/reference-images",
-          "--os-type",
-          "linux",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "pixel_test",
-        "non_precommit_args": [
-          "--upload-refimg-to-cloud-storage"
-        ],
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "precommit_args": [
-          "--download-refimg-from-cloud-storage"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "screenshot_sync_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "trace_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-passthrough-cmd-decoder"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_gl_passthrough_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      }
-    ]
-  },
   "Linux Debug (NVIDIA)": {
     "gtest_tests": [
       {
@@ -3862,481 +3455,6 @@
       }
     ]
   },
-  "Linux Release (Intel HD 530)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "angle_end2end_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "angle_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "audio_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--enable-gpu",
-          "--no-xvfb",
-          "--test-launcher-jobs=1",
-          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
-        ],
-        "name": "tab_capture_end2end_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "gl_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--no-xvfb"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "gl_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "context_lost_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "depth_capture_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "gpu_process_launch_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "hardware_accelerated_feature_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--expected-vendor-id",
-          "8086",
-          "--expected-device-id",
-          "1912"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "info_collection_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--os-type",
-          "linux",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "maps_pixel_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--refimg-cloud-storage-bucket",
-          "chromium-gpu-archive/reference-images",
-          "--os-type",
-          "linux",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "pixel_test",
-        "non_precommit_args": [
-          "--upload-refimg-to-cloud-storage"
-        ],
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "precommit_args": [
-          "--download-refimg-from-cloud-storage"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "screenshot_sync_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "trace_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-passthrough-cmd-decoder",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl2_conformance_gl_passthrough_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ],
-          "shards": 15
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl2_conformance_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ],
-          "shards": 15
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-passthrough-cmd-decoder"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_gl_passthrough_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Ubuntu"
-            }
-          ]
-        }
-      }
-    ]
-  },
   "Linux Release (Intel HD 630)": {
     "gtest_tests": [
       {
@@ -11898,550 +11016,6 @@
       }
     ]
   },
-  "Win10 Debug (Intel HD 530)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "angle_end2end_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "angle_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "angle_white_box_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "audio_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gl_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gl_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--use-angle=d3d9"
-        ],
-        "name": "gles2_conform_d3d9_test",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--use-angle=gl",
-          "--disable-gpu-sandbox"
-        ],
-        "name": "gles2_conform_gl_test",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-angle=d3d11",
-          "--use-test-data-path",
-          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
-        ],
-        "name": "video_decode_accelerator_d3d11_unittest",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "video_decode_accelerator_unittest",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-angle=d3d9",
-          "--use-test-data-path",
-          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
-        ],
-        "name": "video_decode_accelerator_d3d9_unittest",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "video_decode_accelerator_unittest",
-        "use_xvfb": false
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "context_lost_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "depth_capture_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "gpu_process_launch_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "hardware_accelerated_feature_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--expected-vendor-id",
-          "8086",
-          "--expected-device-id",
-          "1912"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "info_collection_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--os-type",
-          "win",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "maps_pixel_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--refimg-cloud-storage-bucket",
-          "chromium-gpu-archive/reference-images",
-          "--os-type",
-          "win",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "pixel_test",
-        "non_precommit_args": [
-          "--upload-refimg-to-cloud-storage"
-        ],
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "precommit_args": [
-          "--download-refimg-from-cloud-storage"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "screenshot_sync_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "trace_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_d3d9_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=gl"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_gl_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=debug",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      }
-    ]
-  },
   "Win10 Debug (NVIDIA)": {
     "gtest_tests": [
       {
@@ -12528,6 +11102,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -13005,591 +11597,6 @@
       }
     ]
   },
-  "Win10 Release (Intel HD 530)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "angle_end2end_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "angle_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "angle_white_box_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "audio_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--enable-gpu",
-          "--no-xvfb",
-          "--test-launcher-jobs=1",
-          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
-        ],
-        "name": "tab_capture_end2end_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gl_tests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gl_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--use-angle=d3d9"
-        ],
-        "name": "gles2_conform_d3d9_test",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
-          "--use-angle=gl",
-          "--disable-gpu-sandbox"
-        ],
-        "name": "gles2_conform_gl_test",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "gles2_conform_test",
-        "use_xvfb": false
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "swiftshader_unittests",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-angle=d3d11",
-          "--use-test-data-path",
-          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
-        ],
-        "name": "video_decode_accelerator_d3d11_unittest",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "video_decode_accelerator_unittest",
-        "use_xvfb": false
-      },
-      {
-        "args": [
-          "--use-angle=d3d9",
-          "--use-test-data-path",
-          "--test_video_data=test-25fps.h264:320:240:250:258:::1"
-        ],
-        "name": "video_decode_accelerator_d3d9_unittest",
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        },
-        "test": "video_decode_accelerator_unittest",
-        "use_xvfb": false
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "context_lost_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "depth_capture_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "gpu_process_launch_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "hardware_accelerated_feature_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--expected-vendor-id",
-          "8086",
-          "--expected-device-id",
-          "1912"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "info_collection_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--os-type",
-          "win",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "maps_pixel_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--refimg-cloud-storage-bucket",
-          "chromium-gpu-archive/reference-images",
-          "--os-type",
-          "win",
-          "--build-revision",
-          "${got_revision}",
-          "--test-machine-name",
-          "${buildername}"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "pixel_test",
-        "non_precommit_args": [
-          "--upload-refimg-to-cloud-storage"
-        ],
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "precommit_args": [
-          "--download-refimg-from-cloud-storage"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "screenshot_sync_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "trace_test",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl2_conformance_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ],
-          "shards": 15
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d11 --use-passthrough-cmd-decoder"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=d3d9"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_d3d9_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-angle=gl"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_gl_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl_conformance_tests",
-        "override_compile_targets": [
-          "telemetry_gpu_integration_test"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1912",
-              "os": "Windows-10"
-            }
-          ]
-        }
-      }
-    ]
-  },
   "Win10 Release (Intel HD 630)": {
     "gtest_tests": [
       {
@@ -13690,6 +11697,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -14275,6 +12300,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -14885,6 +12928,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-10"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -15557,6 +13618,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -16096,6 +14175,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -16692,6 +14789,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": false,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -17297,6 +15412,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -17975,6 +16108,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -18632,6 +16783,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -19250,6 +17419,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index 5f677998..9231b165 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -2050,6 +2050,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
@@ -2408,6 +2426,24 @@
       },
       {
         "args": [
+          "--use-gpu-in-tests",
+          "--use-passthrough-cmd-decoder"
+        ],
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "10de:104a",
+              "os": "Windows-2008ServerR2-SP1"
+            }
+          ]
+        },
+        "test": "gl_tests",
+        "use_xvfb": false
+      },
+      {
+        "args": [
           "--use-gpu-in-tests"
         ],
         "swarming": {
diff --git a/testing/buildbot/filters/fuchsia.net_unittests.filter b/testing/buildbot/filters/fuchsia.net_unittests.filter
index d7065d5..84bc6cc 100644
--- a/testing/buildbot/filters/fuchsia.net_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.net_unittests.filter
@@ -1,5 +1,4 @@
 # TODO(fuchsia): Fix these tests and remove the filter. crbug.com/731302 .
-
 -*HTTPS*
 -*QuicEndToEndTest.Large*
 -*QuicEndToEndTest.TokenBinding*
@@ -8,18 +7,19 @@
 -*RootCert*
 -*Socket*
 -BbrSenderTest.SimpleTransferAckDecimation2
+-BidirectionalStreamTest.TestHonorAlternativeServiceHeader
 -CertNetFetcherImplTest*
 -CertVerifyProcInternalTest.*
 -CloseOnConnectHttpServerTest.ServerImmediatelyClosesConnection
 -DiskCache*
 -EffectiveConnectionTypeTest*
 -EmbeddedTestServer*
--FileStreamTest.*
 -HttpNetworkTransactionTest.UploadUnreadableFile
 -HttpServerTest.*
 -NetworkInterfacesTest.GetNetworkList
 -NetworkQualitiesPrefManager*
 -NetworkQualityEstimatorTest*
+-NetworkThrottleManager*
 -Proxy*
 -PythonUtils*
 -QuicSimpleClientTest*
@@ -32,3 +32,12 @@
 -TokenBindingURLRequest*
 -URLFetcher*
 -URLRequest*
+
+# TODO(fuchsia): These run too slowly at present. crbug.com/745094
+-BbrSenderTest.*
+-CryptoServerTests/*
+-HttpNetworkTransactionTest.*
+-*Quic*
+-RangeOfTreeSizes/*
+-*Spdy*
+-*SendAlgorithmTests.*
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index b03eeb6a..d2e1074 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -143,8 +143,10 @@
 -TouchInputBrowserTest.MultiPointTouchPress
 -TouchInputBrowserTest.TouchHandlerNoConsume
 -WebContentsImplBrowserTest.ClearNonVisiblePendingOnFail
+-WebContentsImplBrowserTest.DidGetResourceResponseStartUpdatesWaitingState
 -WebContentsImplBrowserTest.DownloadImage_Deny_FileImage
 -WebContentsImplBrowserTest.GetSizeForNewRenderView
+-WebContentsImplBrowserTest.LoadingStateChangedForSameDocumentNavigation
 -WebContentsImplBrowserTest.UserAgentOverride
 -WebContentsViewAuraTest.ScreenshotForSwappedOutRenderViews
 -WebRtcGetUserMediaOldConstraintsBrowserTest.GetUserMediaWithInvalidMandatorySourceID
@@ -161,3 +163,7 @@
 # content/network/url_loader_impl.cc should handle failure in
 # URLLoaderImpl::OnResponseBodyStreamRead().
 -SRC_ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
+
+# https://crbug.com/754827
+-ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest_Cancelled
+-BrowserSideNavigationBrowserDisableWebSecurityTest.ValidateBaseUrlForDataUrl
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index f0c0957..71a4061 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -777,10 +777,6 @@
     "label": "//chrome/installer/mini_installer:next_version_mini_installer",
     "type": "additional_compile_target",
   },
-  "oop_heap_profiling_unit_tests": {
-    "label": "//chrome/profiling:unit_tests",
-    "type": "console_test_launcher",
-  },
   "ozone_unittests": {
     "label": "//ui/ozone:ozone_unittests",
     "type": "console_test_launcher",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 22b5e67..be426c28 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1696,8 +1696,8 @@
                     "params": {
                         "availability": "any",
                         "event_new_tab_opened": "name:new_tab_opened;comparator:==0;window:3650;storage:3650",
+                        "event_new_tab_session_time_met": "name:new_tab_session_time_met;comparator:>=1;window:3650;storage:3650",
                         "event_omnibox_used": "name:omnibox_used;comparator:>=1;window:3650;storage:3650",
-                        "event_session_time": "name:session_time;comparator:>=1;window:3650;storage:3650",
                         "event_trigger": "name:new_tab_trigger;comparator:any;window:3650;storage:3650",
                         "event_used": "name:new_tab_clicked;comparator:any;window:3650;storage:3650",
                         "session_rate": "<=3",
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 4213f70..94db067 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -24,7 +24,7 @@
 /apache-win32/modules/*.dll
 /apk-patch-size-estimator/lib/*.jar
 /asan
-/bazel/desugar/*.jar
+/bazel/desugar/Desugar.jar
 /bidichecker
 /bison
 /boringssl/src
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index f7ed034..f3edfc1 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -277,7 +277,6 @@
 Bug(none) external/wpt/navigation-timing/nav2_test_attributes_values.html [ Failure Timeout ]
 Bug(none) external/wpt/navigation-timing/nav2_test_unloadEvents_previous_document_cross_origin.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/notifications/shownotification-resolve-manual.https.html [ Failure Timeout ]
-Bug(none) external/wpt/offscreen-canvas [ Crash Failure Timeout ]
 Bug(none) external/wpt/preload/fetch-destination.https.html [ Crash Failure Timeout ]
 Bug(none) external/wpt/preload/single-download-preload.html [ Failure Timeout ]
 Bug(none) external/wpt/resource-timing/resource_TAO_match_origin.htm [ Failure Timeout ]
@@ -387,19 +386,15 @@
 Bug(none) http/tests/appcache/fallback.html [ Failure ]
 Bug(none) http/tests/appcache/local-content.html [ Timeout ]
 Bug(none) http/tests/appcache/main-resource-hash.html [ Timeout ]
-Bug(none) http/tests/appcache/main-resource-redirect.html [ Timeout ]
-Bug(715632) http/tests/appcache/manifest-redirect-2.html [ Crash ]
-Bug(715632) http/tests/appcache/manifest-redirect.html [ Crash ]
+Bug(754827) http/tests/appcache/main-resource-redirect.html [ Crash ]
 Bug(none) http/tests/appcache/manifest-parsing.html [ Failure ]
 Bug(none) http/tests/appcache/multi-fallback.html [ Failure Timeout ]
 Bug(none) http/tests/appcache/non-html.xhtml [ Crash ]
-Bug(none) http/tests/appcache/offline-access.html [ Timeout ]
-Bug(none) http/tests/appcache/online-fallback-layering.html [ Timeout ]
+Bug(754827) http/tests/appcache/offline-access.html [ Crash ]
+Bug(754827) http/tests/appcache/online-fallback-layering.html [ Failure ]
 Bug(none) http/tests/appcache/online-whitelist.html [ Failure ]
 Bug(none) http/tests/appcache/reload.html [ Crash Failure Timeout ]
 Bug(none) http/tests/appcache/remove-cache.html [ Crash Failure Timeout ]
-Bug(715632) http/tests/appcache/resource-redirect.html [ Crash ]
-Bug(715632) http/tests/appcache/resource-redirect-2.html [ Crash ]
 Bug(none) http/tests/appcache/simple.html [ Timeout ]
 Bug(none) http/tests/appcache/top-frame-1.html [ Timeout ]
 Bug(none) http/tests/appcache/top-frame-2.html [ Crash Timeout ]
@@ -729,21 +724,39 @@
 Bug(none) http/tests/history/push-state-in-new-frame.html [ Timeout ]
 Bug(none) http/tests/htmlimports/import-cors-credentials.html [ Failure ]
 Bug(none) http/tests/https/verify-ssl-enabled.php [ Timeout ]
-Bug(none) http/tests/inspector [ Crash Failure Timeout ]
+Bug(none) http/tests/inspector/console-xhr-logging.html [ Failure ]
+Bug(none) http/tests/inspector/console-resource-errors.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions-headers.html [ Failure ]
+Bug(none) http/tests/inspector/extensions-ignore-cache.html [ Crash ]
+Bug(none) http/tests/inspector/extensions-useragent.html [ Crash ]
+Bug(none) http/tests/inspector/extensions/extensions-panel.html [ Failure ]
+Bug(none) http/tests/inspector/network-preflight-options.html [ Timeout ]
+Bug(none) http/tests/inspector/network/har-content.html [ Failure Timeout ]
+Bug(none) http/tests/inspector/network/load-resource-for-frontend.html [ Failure Timeout ]
+Bug(none) http/tests/inspector/network/network-content-replacement-xhr.html [ Timeout ]
+Bug(none) http/tests/inspector/network/network-cyrillic-xhr.html [ Timeout ]
+Bug(none) http/tests/inspector/network/network-empty-xhr.html [ Timeout ]
+Bug(none) http/tests/inspector/network/network-xhr-sync.html [ Timeout ]
+Bug(none) http/tests/inspector/resource-tree/resource-tree-no-xhrs.html [ Timeout ]
+Bug(none) http/tests/inspector/network/network-datareceived.html [ Failure ]
+Bug(none) http/tests/inspector/network/network-xhr-replay.html [ Failure ]
+Bug(none) http/tests/inspector/network/ping-response.html [ Failure ]
+Bug(none) http/tests/inspector/network/warning-for-long-cookie.html [ Failure ]
+Bug(none) http/tests/inspector/network/x-frame-options-deny.html [ Failure ]
+Bug(none) http/tests/inspector/network/long-script-content.html [ Crash ]
+Bug(none) http/tests/inspector/persistence/persistence-tabbed-editor-tabs-order.html [ Crash ]
+Bug(none) http/tests/inspector/resource-har-conversion.html [ Failure ]
+Bug(none) http/tests/inspector/resource-parameters.html [ Failure ]
+Bug(none) http/tests/inspector/resource-parameters-ipv6.html [ Timeout ]
+Bug(none) http/tests/inspector/service-workers/service-workers-bypass-for-network-redirect.html [ Crash ]
+Bug(none) http/tests/inspector/service-workers/service-workers-force-update-on-page-load.html [ Timeout ]
+Bug(none) http/tests/inspector/service-workers/service-workers-navigation-preload.html [ Timeout ]
+Bug(none) http/tests/inspector/service-workers/service-workers-redundant.html [ Timeout ]
+Bug(none) http/tests/inspector/service-workers/service-workers-view.html [ Timeout ]
+Bug(none) http/tests/inspector/sources/debugger/pause-in-removed-frame.html [ Timeout ]
 Bug(none) http/tests/inspector-enabled [ Failure Timeout ]
 Bug(none) http/tests/inspector-protocol [ Failure Timeout ]
-Bug(none) http/tests/inspector-protocol/cachestorage/read-cached-response.js [ Timeout ]
 Bug(none) http/tests/inspector-protocol/page/frameScheduledNavigation.js [ Crash ]
-Bug(none) http/tests/inspector/extensions/extensions-api.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-audits-api.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-audits-content-script.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-audits.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-events.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-network.html [ Failure Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-reload.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-resources.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/extensions-timeline-api.html [ Timeout ]
-Bug(none) http/tests/inspector/extensions/multiple-extensions.html [ Timeout ]
 Bug(none) http/tests/linkHeader/link-preconnect-schemeless.https.php [ Timeout ]
 Bug(none) http/tests/loading/307-after-303-after-post.html [ Failure ]
 Bug(none) http/tests/loading/doc-write-sync-third-party-script-reload.html [ Crash ]
@@ -803,7 +816,7 @@
 Bug(none) http/tests/navigation/ping-cross-origin-from-https.html [ Timeout ]
 Bug(none) http/tests/navigation/ping-same-origin.html [ Failure ]
 Bug(none) http/tests/navigation/post-basic.html [ Failure ]
-Bug(none) http/tests/navigation/post-frames-goback1.html [ Failure ]
+Bug(none) http/tests/navigation/post-frames-goback1.html [ Crash Failure ]
 Bug(none) http/tests/navigation/post-frames.html [ Failure ]
 Bug(none) http/tests/navigation/post-goback1.html [ Failure ]
 Bug(none) http/tests/navigation/redirect-on-back-updates-history-item.html [ Timeout ]
@@ -894,7 +907,6 @@
 Bug(none) http/tests/push_messaging/unsubscribe-in-service-worker.html [ Crash Failure Timeout ]
 Bug(none) http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-blocked-with-redirect.html [ Failure ]
 Bug(none) http/tests/security/contentSecurityPolicy/1.1/form-action-src-redirect-blocked.html [ Failure ]
-Bug(none) http/tests/security/contentSecurityPolicy/block-mixed-content-hides-warning.html [ Failure ]
 Bug(none) http/tests/security/contentSecurityPolicy/cascade/same-origin-window-open.html [ Timeout ]
 Bug(none) http/tests/security/contentSecurityPolicy/cascade/same-origin-with-own-policy-window-open.html [ Timeout ]
 Bug(none) http/tests/security/contentSecurityPolicy/cascade/same-origin-with-own-policy.html [ Timeout ]
@@ -902,164 +914,37 @@
 Bug(none) http/tests/security/contentSecurityPolicy/frame-src-child-frame-navigates-to-blocked-origin.html [ Failure Timeout ]
 Bug(none) http/tests/security/contentSecurityPolicy/frame-src-cross-origin-load.html [ Failure ]
 Bug(none) http/tests/security/contentSecurityPolicy/frame-src-redirect-blocked.html [ Failure ]
-Bug(none) http/tests/security/contentSecurityPolicy/register-bypassing-scheme-script.https.html [ Timeout ]
 Bug(none) http/tests/security/contentSecurityPolicy/report-same-origin-with-cookies.php [ Failure ]
-Bug(none) http/tests/security/contentSecurityPolicy/script-src-star-cross-scheme.html [ Failure ]
-Bug(none) http/tests/security/contentSecurityPolicy/source-list-parsing-10.html [ Failure ]
 Bug(none) http/tests/security/cookies/first-party-cookie-allow-xslt.xml [ Failure ]
 Bug(none) http/tests/security/cookies/third-party-cookie-blocking-main-frame.html [ Failure ]
 Bug(none) http/tests/security/cookies/third-party-cookie-blocking-worker.html [ Failure ]
 Bug(none) http/tests/security/cookies/xmlhttprequest.html [ Timeout ]
 Bug(none) http/tests/security/cors-rfc1918 [ Crash Timeout ]
-Bug(none) http/tests/security/cross-frame-access-parent-explicit-domain-isolated-world.html [ Timeout ]
-Bug(none) http/tests/security/cross-frame-access-parent-isolated-world.html [ Timeout ]
-Bug(none) http/tests/security/cross-frame-access-protocol-explicit-domain.html [ Timeout ]
-Bug(none) http/tests/security/cross-frame-access-protocol.html [ Timeout ]
-Bug(none) http/tests/security/cross-origin-OffscreenCanvas2D-transferToImageBitmap.html [ Timeout ]
-Bug(none) http/tests/security/cross-origin-OffscreenCanvasWebGL-texImage2D.html [ Timeout ]
-Bug(none) http/tests/security/cross-origin-createImageBitmap.html [ Timeout ]
-Bug(none) http/tests/security/detached-window-cross-origin-access.html [ Timeout ]
-Bug(none) http/tests/security/document-all.html [ Failure ]
 Bug(none) http/tests/security/img-crossorigin-cookies.html [ Failure ]
 Bug(none) http/tests/security/img-redirect-to-crossorigin-credentials.html [ Failure ]
 Bug(none) http/tests/security/listener/xss-XMLHttpRequest-addEventListener.html [ Timeout ]
-Bug(none) http/tests/security/local-video-source-from-remote.html [ Timeout ]
-Bug(none) http/tests/security/location-change-from-detached-DOMWindow.html [ Timeout ]
-Bug(none) http/tests/security/media-element-audio-source-node-cross-origin-allowed.html [ Timeout ]
-Bug(none) http/tests/security/media-element-audio-source-node-cross-origin-with-credentials.html [ Failure Timeout ]
 Bug(none) http/tests/security/media-element-audio-source-node-cross-origin.html [ Failure Pass Timeout ]
 Bug(none) http/tests/security/media-element-audio-source-node-same-origin.html [ Failure Pass ]
 Bug(none) http/tests/security/mime-type-execute-as-html-01.html [ Failure ]
 Bug(none) http/tests/security/mime-type-execute-as-html-04.html [ Failure ]
-Bug(none) http/tests/security/mixedContent/about-blank-iframe-in-main-frame.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/active-subresource-in-http-iframe-not-blocked.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/active-subresource-in-iframe-blocked.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/data-url-iframe-in-main-frame.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/data-url-script-in-data-iframe.https.html [ Timeout ]
 Bug(none) http/tests/security/mixedContent/filesystem-url-in-iframe.html [ Failure Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-async-post-xhr-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-audio-video-in-main-frame.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-css-image-with-reload.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-css-in-iframe.html [ Failure ]
-Bug(none) http/tests/security/mixedContent/insecure-css-in-main-frame.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-css-resources.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-empty-srcset-in-main-frame-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-eventsource-in-main-frame.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-fetch-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-font-in-main-frame.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-formSubmission-in-invisible-DOM.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-formSubmission-in-main-frame-allowed.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-formSubmission-in-main-frame-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-formSubmission-in-main-frame-javascript-allowed.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-formSubmission-in-main-frame.html [ Timeout ]
 Bug(none) http/tests/security/mixedContent/insecure-frame-in-data-iframe-in-main-frame-blocked.html [ Failure Timeout ]
 Bug(none) http/tests/security/mixedContent/insecure-iframe-in-iframe.html [ Failure ]
 Bug(none) http/tests/security/mixedContent/insecure-iframe-in-main-frame-allowed.html [ Failure Timeout ]
 Bug(none) http/tests/security/mixedContent/insecure-iframe-in-main-frame-blocked.html [ Failure Timeout ]
 Bug(none) http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-iframe-with-hsts.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-image-in-iframe.html [ Failure ]
-Bug(none) http/tests/security/mixedContent/insecure-image-in-main-frame-allowed.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-image-in-main-frame-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-image-in-main-frame.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-localhost-allowed.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-picture-in-main-frame-blocked.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-plugin-in-iframe.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-prefetch-in-main-frame.html [ Failure ]
-Bug(none) http/tests/security/mixedContent/insecure-script-in-data-iframe-in-main-frame-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-script-in-iframe.html [ Failure ]
-Bug(none) http/tests/security/mixedContent/insecure-script-in-main-frame-allowed.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-script-in-main-frame-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-script-through-redirection.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-srcset-in-main-frame-blocked.html [ Timeout ]
 Bug(none) http/tests/security/mixedContent/insecure-sync-post-xhr-allowed.html [ Failure Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-sync-post-xhr-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-texttrack-in-main-frame-blocked.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/insecure-xhr-in-main-frame.https.html [ Timeout ]
 Bug(none) http/tests/security/mixedContent/nonwebby-scheme-in-iframe-allowed.https.html [ Failure Timeout ]
-Bug(none) http/tests/security/mixedContent/preload-insecure-image-in-main-frame-blocked.html [ Timeout ]
 Bug(none) http/tests/security/mixedContent/redirect-http-to-https-iframe-in-main-frame.html [ Failure Timeout ]
-Bug(none) http/tests/security/mixedContent/redirect-http-to-https-script-in-iframe.html [ Failure ]
 Bug(none) http/tests/security/mixedContent/redirect-https-to-http-iframe-in-main-frame.html [ Failure Timeout ]
-Bug(none) http/tests/security/mixedContent/redirect-https-to-http-script-in-iframe.html [ Failure ]
-Bug(none) http/tests/security/mixedContent/strict-mode-image-blocked.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/strict-mode-image-in-frame-blocked.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/strict-mode-image-no-policy.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/strict-mode-image-reportonly.https.php [ Timeout ]
-Bug(none) http/tests/security/mixedContent/strict-mode-via-pref-image-blocked.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/strict-mode-websocket-blocked.https.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/websocket/insecure-websocket-in-sandbox-in-secure-page.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/websocket/insecure-websocket-in-secure-page-allowed.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/websocket/insecure-websocket-in-secure-page-worker-allowed.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/websocket/insecure-websocket-in-secure-page-worker.html [ Timeout ]
-Bug(none) http/tests/security/mixedContent/websocket/insecure-websocket-in-secure-page.html [ Timeout ]
-Bug(none) http/tests/security/no-indexeddb-from-sandbox.html [ Failure ]
-Bug(none) http/tests/security/no-popup-from-sandbox-top.html [ Failure ]
-Bug(none) http/tests/security/no-popup-from-sandbox.html [ Failure ]
 Bug(none) http/tests/security/no-referrer.html [ Timeout ]
 Bug(none) http/tests/security/offscreen-canvas-worker-read-blocked-by-setting.html [ Crash Pass Timeout ]
-Bug(none) http/tests/security/opened-document-security-origin-resets-on-navigation.html [ Timeout ]
-Bug(none) http/tests/security/originHeader/origin-header-for-https.html [ Timeout ]
-Bug(none) http/tests/security/popup-allowed-by-sandbox-can-navigate.html [ Failure ]
-Bug(none) http/tests/security/popup-allowed-by-sandbox-is-sandboxed-control.html [ Failure ]
-Bug(none) http/tests/security/popup-allowed-by-sandbox-is-sandboxed.html [ Failure ]
-Bug(none) http/tests/security/popup-allowed-by-sandbox-when-allowed.html [ Failure ]
-Bug(none) http/tests/security/powerfulFeatureRestrictions/geolocation-on-sandboxed-insecure-origin.html [ Timeout ]
-Bug(none) http/tests/security/powerfulFeatureRestrictions/geolocation-on-secure-origin-in-insecure-origin.html [ Timeout ]
-Bug(none) http/tests/security/powerfulFeatureRestrictions/geolocation-on-secure-origin-in-secure-origin.html [ Timeout ]
-Bug(none) http/tests/security/powerfulFeatureRestrictions/serviceworker-on-insecure-origin.html [ Failure ]
-Bug(none) http/tests/security/referrer-on-client-redirect.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-attribute-anchor-no-referrer-when-downgrade.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-attribute-area-no-referrer-when-downgrade.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-attribute-iframe-no-referrer-when-downgrade.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-attribute-img-no-referrer-when-downgrade.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-conflicting-policies.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-always.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-default.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-never.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-no-referrer-when-downgrade.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-no-referrer.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-origin-when-crossorigin.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-origin.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-https-unsafe-url.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-redirect-link.html [ Timeout ]
-Bug(none) http/tests/security/referrer-policy-redirect.html [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/basic-header-cross-origin-with-origin-when-cross-origin.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/basic-header-cross-origin-with-origin.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/basic-header-downgrade-with-no-referrer-when-downgrade.https.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/basic-header-no-downgrade-with-no-referrer-when-downgrade.https.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/basic-header-unsafe-url.https.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/legacy-always.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/legacy-default.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/legacy-never.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/legacy-origin-when-crossorigin.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/referrer-policy-cross-origin-redirect.php [ Timeout ]
-Bug(none) http/tests/security/referrerPolicyHeader/referrer-policy-header-on-cross-origin-redirect-response.https.html [ Timeout ]
-Bug(none) http/tests/security/sandboxed-opener-can-close-window.html [ Failure ]
-Bug(none) http/tests/security/secureContexts/authenticated.html [ Timeout ]
-Bug(none) http/tests/security/secureContexts/authenticated_sandbox.html [ Timeout ]
-Bug(none) http/tests/security/secureContexts/authenticated_srcdoc.html [ Timeout ]
 Bug(none) http/tests/security/secureContexts/authenticated_worker.https.html [ Timeout ]
-Bug(none) http/tests/security/secureContexts/unauthenticated.html [ Timeout ]
-Bug(none) http/tests/security/secureContexts/unauthenticated_sandbox.html [ Timeout ]
-Bug(none) http/tests/security/secureContexts/unauthenticated_srcdoc.html [ Timeout ]
 Bug(none) http/tests/security/secureContexts/unauthenticated_worker.html [ Timeout ]
 Bug(none) http/tests/security/suborigins/suborigin-cookies.php [ Timeout ]
 Bug(none) http/tests/security/suborigins/suborigin-service-worker-fetch-event.html [ Crash Failure Timeout ]
 Bug(none) http/tests/security/suborigins/suborigin-service-worker-no-xorigin-caching.html [ Crash Timeout ]
 Bug(none) http/tests/security/suborigins/suborigin-unsafe-cookies.php [ Timeout ]
-Bug(none) http/tests/security/upgrade-insecure-requests/basic-upgrade.https.html [ Timeout ]
-Bug(none) http/tests/security/upgrade-insecure-requests/form-upgrade.html [ Timeout ]
-Bug(none) http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Timeout ]
-Bug(none) http/tests/security/upgrade-insecure-requests/sandbox-upgrade.https.php [ Timeout ]
-Bug(none) http/tests/security/video-cross-origin-readback.html [ Timeout ]
-Bug(none) http/tests/security/video-cross-origin-via-dom.html [ Timeout ]
-Bug(none) http/tests/security/webgl-remote-read-remote-image-allowed-with-credentials.html [ Timeout ]
-Bug(none) http/tests/security/webgl-remote-read-remote-image-allowed.html [ Timeout ]
-Bug(none) http/tests/security/webgl-remote-read-remote-image-blocked-no-crossorigin.html [ Timeout ]
-Bug(none) http/tests/security/window-named-proto.html [ Failure ]
-Bug(none) http/tests/security/window-named-valueOf.html [ Failure ]
-Bug(none) http/tests/security/xss-exception.html [ Timeout ]
 Bug(none) http/tests/sendbeacon/beacon-cookie.html [ Failure ]
 Bug(none) http/tests/sendbeacon/beacon-cross-origin.https.html [ Timeout ]
 Bug(none) http/tests/sendbeacon/beacon-same-origin.html [ Failure ]
@@ -1093,21 +978,11 @@
 Bug(none) inspector-protocol/worker/exception-from-worker-contains-stack.js [ Timeout ]
 Bug(none) inspector-protocol/worker/worker-console.js [ Crash Timeout ]
 Bug(none) inspector/agents-enable-disable.html [ Timeout ]
-Bug(none) inspector/animation/animation-KeyframeEffectReadOnly-crash.html [ Timeout ]
-Bug(none) inspector/animation/animation-group-matching-animations.html [ Timeout ]
-Bug(none) inspector/animation/animation-group-matching-transitions.html [ Timeout ]
 Bug(none) inspector/animation/animation-transition-setTiming-crash.html [ Timeout ]
 Bug(none) inspector/audits/audits-empty-stylesheet.html [ Timeout ]
 Bug(none) inspector/audits/audits-panel-functional.html [ Timeout ]
 Bug(none) inspector/audits/audits-panel-noimages-functional.html [ Timeout ]
-Bug(none) inspector/changes/changes-highlighter.html [ Timeout ]
-Bug(none) inspector/changes/changes-sidebar.html [ Timeout ]
 Bug(none) inspector/components/chunked-file-reader.html [ Failure Timeout ]
-Bug(none) inspector/components/dom-extension.html [ Timeout ]
-Bug(none) inspector/components/file-path-scoring.html [ Timeout ]
-Bug(none) inspector/components/parsed-url.html [ Timeout ]
-Bug(none) inspector/components/progress-bar.html [ Timeout ]
-Bug(none) inspector/components/segmented-range.html [ Timeout ]
 Bug(none) inspector/components/throttler.html [ Failure ]
 Bug(none) inspector/console/console-copy-treeoutline.html [ Failure ]
 Bug(none) inspector/console/console-export.html [ Failure ]
@@ -1120,138 +995,37 @@
 Bug(none) inspector/console/console-log-short-hand-method.html [ Failure ]
 Bug(none) inspector/console/console-object-constructor-name.html [ Failure ]
 Bug(none) inspector/console/console-tests.html [ Failure ]
-Bug(none) inspector/console/console-uncaught-promise.html [ Failure Timeout ]
-Bug(none) inspector/coverage/coverage-repeated.html [ Failure Timeout ]
 Bug(none) inspector/coverage/coverage-view-filter.html [ Failure ]
-Bug(none) inspector/coverage/coverage-view.html [ Failure Timeout ]
-Bug(none) inspector/coverage/decorations-after-script-formatter.html [ Timeout ]
-Bug(none) inspector/coverage/gutter-js.html [ Timeout ]
 Bug(none) inspector/coverage/multiple-instances-merge.html [ Timeout Failure ]
-Bug(none) inspector/coverage/reveal-autoformat.html [ Timeout ]
-Bug(none) inspector/coverage/segments-merge.html [ Timeout ]
 Bug(none) inspector/device-mode/device-mode-responsive.html [ Timeout ]
 Bug(none) inspector/device-mode/device-mode-switching-devices.html [ Timeout ]
 Bug(none) inspector/domdebugger/domdebugger-getEventListeners.html [ Timeout ]
-Bug(none) inspector/editor/text-editor-ctrl-d-2.html [ Timeout ]
 Bug(none) inspector/editor/text-editor-enter-behaviour.html [ Timeout ]
-Bug(none) inspector/editor/text-editor-reveal-line.html [ Timeout ]
 Bug(none) inspector/editor/text-editor-selection-to-search.html [ Timeout ]
-Bug(none) inspector/editor/text-editor-smart-braces.html [ Timeout ]
-Bug(none) inspector/editor/text-editor-token-at-position.html [ Timeout ]
-Bug(none) inspector/elements/accessibility/autocomplete-attribute.html [ Timeout ]
-Bug(none) inspector/elements/accessibility/edit-aria-attributes.html [ Timeout ]
-Bug(none) inspector/elements/attribute-modified-ns.html [ Timeout ]
-Bug(none) inspector/elements/bidi-dom-tree.html [ Timeout ]
-Bug(none) inspector/elements/dom-search-crash.html [ Timeout ]
 Bug(none) inspector/elements/edit/edit-dom-actions-4.html [ Timeout ]
-Bug(none) inspector/elements/elements-img-tooltip.html [ Timeout ]
-Bug(none) inspector/elements/elements-panel-reload-assert.html [ Timeout ]
 Bug(none) inspector/elements/elements-panel-restore-selection-when-node-comes-later.html [ Timeout ]
-Bug(none) inspector/elements/elements-panel-rewrite-href.html [ Timeout ]
-Bug(none) inspector/elements/elements-panel-search.html [ Timeout ]
-Bug(none) inspector/elements/elements-panel-selection-on-refresh.html [ Timeout ]
-Bug(none) inspector/elements/elements-panel-styles.html [ Timeout ]
-Bug(none) inspector/elements/event-listener-sidebar-remove.html [ Timeout ]
-Bug(none) inspector/elements/hide-shortcut.html [ Timeout ]
-Bug(none) inspector/elements/highlight/highlight-css-shapes-outside-scroll.html [ Timeout ]
 Bug(none) inspector/elements/highlight/highlight-dom-updates.html [ Timeout ]
 Bug(none) inspector/elements/highlight/highlight-node-transformed.html [ Timeout ]
 Bug(none) inspector/elements/highlight/highlight-node.html [ Timeout ]
-Bug(none) inspector/elements/highlight/highlight-svg-root-zoomed.html [ Timeout ]
-Bug(none) inspector/elements/highlight/highlight-svg-root.html [ Timeout ]
-Bug(none) inspector/elements/shadow/breadcrumb-shadow-roots.html [ Timeout ]
-Bug(none) inspector/elements/shadow/create-shadow-root.html [ Timeout ]
 Bug(none) inspector/elements/shadow/elements-panel-shadow-selection-on-refresh-1.html [ Timeout ]
 Bug(none) inspector/elements/shadow/elements-panel-shadow-selection-on-refresh-2.html [ Timeout ]
-Bug(none) inspector/elements/shadow/elements-panel-shadow-selection-on-refresh-3.html [ Timeout ]
 Bug(none) inspector/elements/shadow/inspect-deep-shadow-element.html [ Timeout ]
-Bug(none) inspector/elements/shadow/update-shadowdom.html [ Timeout ]
 Bug(none) inspector/elements/styles-1/color-aware-property-value-edit.html [ Timeout ]
-Bug(none) inspector/elements/styles-1/css-live-edit.html [ Timeout ]
-Bug(none) inspector/elements/styles-1/disable-property-workingcopy-update.html [ Timeout ]
-Bug(none) inspector/elements/styles-1/edit-media-text.html [ Timeout ]
-Bug(none) inspector/elements/styles-1/edit-name-with-trimmed-value.html [ Timeout ]
-Bug(none) inspector/elements/styles-1/edit-value-inside-property.html [ Timeout ]
 Bug(none) inspector/elements/styles-1/empty-background-url.html [ Failure ]
-Bug(none) inspector/elements/styles-2/add-import-rule.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/cssom-shorthand-important.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/force-pseudo-state.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/inactive-properties.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/inject-stylesheet.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/media-emulation.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/mixed-case-color-aware-properties.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/page-reload-update-sidebar.html [ Timeout ]
-Bug(none) inspector/elements/styles-2/property-ui-location.html [ Timeout Failure ]
-Bug(none) inspector/elements/styles-3/style-autocomplete.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-add-blank-property.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-add-new-rule-colon.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-add-new-rule-to-stylesheet.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-add-new-rule.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-change-node-while-editing.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-commit-editing.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-disable-inherited.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-disable-then-change.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-disable-then-enable-overriden-ua.html [ Timeout ]
-Bug(none) inspector/elements/styles-3/styles-variables.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-formatting.html [ Timeout ]
 Bug(none) inspector/elements/styles-4/styles-inline-element-style-changes-should-not-force-style-recalc.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-live-locations-leak.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-new-API.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-properties-overload.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-should-not-force-sync-style-recalc.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-update-from-js.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-update-links-3.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/styles-url-linkify.html [ Timeout ]
-Bug(none) inspector/elements/styles-4/stylesheet-source-url-comment.html [ Timeout ]
-Bug(none) inspector/elements/styles/cancel-upon-invalid-property.html [ Timeout ]
-Bug(none) inspector/elements/styles/original-content-provider.html [ Timeout ]
-Bug(none) inspector/elements/styles/url-multiple-collapsing.html [ Timeout ]
-Bug(none) inspector/file-system-mapping.html [ Timeout ]
 Bug(none) inspector/file-system-project.html [ Failure ]
-Bug(none) inspector/geolocation-emulation-tests.html [ Timeout ]
-Bug(none) inspector/import-open-inspector.html [ Timeout ]
-Bug(none) inspector/input-event-warning.html [ Timeout ]
-Bug(none) inspector/inspected-objects-not-overriden.html [ Timeout ]
-Bug(none) inspector/layers/layer-replay-scale.html [ Timeout ]
 Bug(none) inspector/layers/layers-3d-view-hit-testing.html [ Timeout ]
-Bug(none) inspector/modules-load-elements.html [ Timeout ]
-Bug(none) inspector/modules-load-initial.html [ Timeout ]
-Bug(none) inspector/modules-load-network.html [ Timeout ]
-Bug(none) inspector/modules-load-source.html [ Timeout ]
-Bug(none) inspector/network/network-cookies-pane.html [ Timeout ]
 Bug(none) inspector/network/network-json-parser.html [ Timeout ]
-Bug(none) inspector/profiler/cpu-profiler-save-load.html [ Timeout ]
-Bug(none) inspector/profiler/heap-snapshot-loader.html [ Timeout ]
 Bug(none) inspector/quick-open/command-menu.html [ Timeout ]
-Bug(none) inspector/runtime/runtime-es6-setSymbolPropertyValue.html [ Timeout ]
-Bug(none) inspector/sass/test-ast-css-1.html [ Timeout ]
-Bug(none) inspector/sass/test-ast-diff-1.html [ Timeout ]
-Bug(none) inspector/sass/test-ast-editing-1.html [ Timeout ]
 Bug(none) inspector/sass/test-ast-scss-3.html [ Timeout ]
 Bug(none) inspector/sass/test-ast-scss-6.html [ Timeout ]
-Bug(none) inspector/sass/test-edit-remove-property.html [ Timeout ]
-Bug(none) inspector/sass/test-edit-toggle-property.html [ Timeout ]
-Bug(none) inspector/sass/test-mapping-bad.html [ Timeout ]
-Bug(none) inspector/sass/test-mapping-good.html [ Timeout ]
-Bug(none) inspector/sass/test-mapping-with-cache-busting-url.html [ Timeout ]
-Bug(none) inspector/screen-orientation-override.html [ Timeout ]
-Bug(none) inspector/sha1.html [ Timeout ]
 Bug(none) inspector/sources/debugger-breakpoints/breakpoint-manager-listeners-count.html [ Failure ]
-Bug(none) inspector/sources/debugger-breakpoints/breakpoints-in-anonymous-script-with-two-targets.html [ Timeout ]
 Bug(none) inspector/sources/debugger-breakpoints/event-listener-breakpoints-xhr.html [ Timeout ]
-Bug(none) inspector/sources/debugger-pause/pause-in-inline-script.html [ Timeout ]
-Bug(none) inspector/sources/debugger-step/debugger-step-out-document-write.html [ Timeout ]
-Bug(none) inspector/sources/debugger-ui/script-snippet-model.html [ Timeout ]
 Bug(none) inspector/sources/debugger-ui/watch-expressions-preserve-expansion.html [ Failure ]
 Bug(none) inspector/sources/debugger/dynamic-script-tag.html [ Failure ]
-Bug(none) inspector/tabbed-pane-closeable-persistence.html [ Timeout ]
-Bug(none) inspector/tabbed-pane-max-tab-width-calculation.html [ Timeout ]
-Bug(none) inspector/tracing-session-id.html [ Timeout ]
 Bug(none) inspector/tracing/console-timeline.html [ Failure ]
 Bug(none) inspector/tracing/timeline-network/timeline-network-resource-details.html [ Failure ]
 Bug(none) inspector/tracing/timeline-network/timeline-network-resource.html [ Failure ]
-Bug(none) inspector/user-agent-setting.html [ Timeout ]
-Bug(none) inspector/version-controller.html [ Timeout ]
 Bug(none) installedapp/getinstalledrelatedapps-empty.html [ Timeout ]
 Bug(none) installedapp/getinstalledrelatedapps.html [ Timeout ]
 Bug(none) loader/iframe-src-change-onload-crash.html [ Timeout ]
@@ -1263,19 +1037,10 @@
 Bug(none) paint/invalidation/video-unmute-repaint.html [ Timeout ]
 Bug(none) payments/payment-request-interface.html [ Failure ]
 Bug(none) payments/promises-keep-request-alive.html [ Timeout ]
-Bug(none) plugins/createScriptableObject-before-start.html [ Failure ]
-Bug(none) plugins/focus-change-3-change-blur.html [ Failure ]
-Bug(none) plugins/focus-change-4-change-focus-and-blur.html [ Failure ]
-Bug(none) plugins/gesture-events-scrolled.html [ Timeout Failure ]
-Bug(none) plugins/gesture-events.html [ Timeout ]
 Bug(none) plugins/iframe-plugin-bgcolor.html [ Timeout ]
-Bug(none) plugins/plugin-document-back-forward.html [ Timeout ]
-Bug(none) plugins/plugin-initiate-popup-window.html [ Timeout ]
 Bug(none) presentation/presentation-controller-close-connection.html [ Timeout ]
 Bug(none) presentation/presentation-controller-connection-closed-by-receiver.html [ Timeout ]
 Bug(none) presentation/presentation-controller-terminate-connection.html [ Timeout ]
-Bug(none) presentation/presentation-navigation-multipleurls.html [ Timeout ]
-Bug(none) presentation/presentation-navigation.html [ Timeout ]
 Bug(none) presentation/presentation-onreceiverconnection.html [ Timeout ]
 Bug(none) presentation/presentation-receiver-terminate-connection.html [ Timeout ]
 Bug(none) presentation/presentation-reconnect.html [ Timeout ]
@@ -1284,9 +1049,6 @@
 Bug(none) presentation/presentationconnectionavailableevent-ctor-mock.html [ Timeout ]
 Bug(none) printing/subframes-percentage-height.html [ Failure ]
 Bug(none) scrollbars/listbox-scrollbar-combinations.html [ Failure ]
-Bug(none) storage/domstorage/events/basic.html [ Failure Timeout ]
-Bug(none) tables/mozilla/core/col_widths_fix_autoFixPer.html [ Timeout ]
-Bug(none) tables/mozilla_expected_failures/marvin/backgr_fixed-bg.html [ Failure ]
 Bug(none) traversal/node-iterator-009.html [ Failure ]
 Bug(none) traversal/tree-walker-006.html [ Failure ]
 Bug(none) virtual [ Crash Failure Timeout ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index ce44765..ff6f070 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -541,7 +541,6 @@
 crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-002.htm [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-003.htm [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-004.htm [ Failure ]
-crbug.com/635619 virtual/layout_ng/fast/block/float/floats-wrap-inside-inline-007.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/formatting-context-changes.html [ Failure Crash ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/independent-align-positioning.html [ Failure ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/intruding-painted-twice.html [ Failure ]
@@ -1737,6 +1736,34 @@
 crbug.com/626703 virtual/threaded/transitions/transition-end-event-multiple-03.html [ Pass Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.cap.butt.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.cap.open.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.cap.square.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.cap.valid.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.join.bevel.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.join.invalid.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.miter.rightangle.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.miter.valid.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.union.html [ Skip ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.width.invalid.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/2d.line.width.transformed.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/canvas_linestyles_linecap_001.htm [ Timeout ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/2dcontext/line-styles/lineto_a.html [ Timeout ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/WebCryptoAPI/secure_context/crypto-subtle-non-secure-context-not-available.sub.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/WebCryptoAPI/secure_context/crypto-subtle-secure-context-available.https.sub.html [ Skip ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/apng/animated-png-timeout.html [ Timeout ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-no-referrer-when-downgrade.https.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-no-referrer.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-origin-when-cross-origin.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-origin.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-same-origin.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-strict-origin-when-cross-origin.https.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-strict-origin.https.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/beacon/headers/header-referrer-unsafe-url.https.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html [ Crash ]
+crbug.com/626703 [ Android Mac10.9 ] external/wpt/clipboard-apis/async-write-text-read-text-manual.https.html [ Skip ]
+crbug.com/626703 [ Android Mac10.9 ] virtual/outofblink-cors/external/wpt/fetch/api/basic/integrity-worker.html [ Timeout ]
+crbug.com/626703 [ Android Mac10.9 ] virtual/service-worker-script-streaming/external/wpt/service-workers/service-worker/claim-affect-other-registration.https.html [ Timeout ]
 crbug.com/626703 external/wpt/beacon/headers/header-content-type.html [ Pass Timeout ]
 crbug.com/626703 [ Win ] external/wpt/css/css-ui-3/outline-004.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-ui-3/text-overflow-001.html [ Pass Failure ]
@@ -2869,15 +2896,7 @@
 crbug.com/736177 [ Mac10.12 ] fast/css/text-overflow-input.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] fast/dom/HTMLMeterElement/meter-optimums.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] fast/dynamic/012.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/control-restrict-line-height.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] fast/forms/form-element-geometry.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/input-appearance-height.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/placeholder-position.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/search/search-appearance-basic.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/search/search-cancel-button-style-sharing.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/search/search-display-none-cancel-button.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/search/search-rtl.html [ Failure Pass ]
-crbug.com/736177 [ Mac10.12 ] fast/forms/search/searchfield-heights.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] fast/inline/absolute-positioned-inline-in-centred-block.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] fast/inline/left-right-center-inline-alignment-in-ltr-and-rtl-blocks.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] fast/lists/ordered-list-with-no-ol-tag.html [ Failure Pass ]
@@ -2923,6 +2942,46 @@
 crbug.com/736177 [ Mac10.12 ] virtual/off-main-thread-fetch/http/tests/misc/acid2-pixel.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] virtual/prefer_compositing_to_lcd_text/scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 ] virtual/rootlayerscrolls/scrollbars/custom-scrollbar-with-incomplete-style.html [ Failure Pass ]
+# These tests are failing on both Mac-10.12 when using an Intel GPU and Mac-10.11.6 when using no GPU
+crbug.com/736177 [ Mac ] fast/forms/control-restrict-line-height.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/input-appearance-height.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/placeholder-position.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/search/search-appearance-basic.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/search/search-cancel-button-style-sharing.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/search/search-display-none-cancel-button.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/search/search-rtl.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] fast/forms/search/searchfield-heights.html [ Failure Pass ]
+# These tests are failing on Mac-10.12 when using an Intel GPU and Mac Retina
+crbug.com/736177 [ Mac10.12 Retina ] compositing/overflow/theme-affects-visual-overflow.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css1/box_properties/acid_test.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css2.1/t09-c5526c-display-00-e.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css3/selectors3/html/css3-modsel-25.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css3/selectors3/html/css3-modsel-70.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css3/selectors3/xhtml/css3-modsel-25.xml [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css3/selectors3/xhtml/css3-modsel-70.xml [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css3/selectors3/xml/css3-modsel-25.xml [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] css3/selectors3/xml/css3-modsel-70.xml [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/block/basic/011.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/block/margin-collapse/103.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/css/non-standard-checkbox-size.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/001.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/basic-inputs.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/checkbox/checkbox-appearance-basic.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/file/file-input-disabled.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/formmove.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/formmove2.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/indeterminate.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/input-value.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/forms/radio/radio-appearance-basic.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/inline/positionedLifetime.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/parser/bad-xml-slash.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/replaced/replaced-breaking.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/text/textIteratorNilRenderer.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] fast/text/whitespace/normal-after-nowrap-breaking.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] paint/invalidation/forms/checkbox-focus-by-mouse-then-keydown.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] paint/invalidation/forms/radio-focus-by-mouse-then-keydown.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] tables/mozilla/bugs/bug1318.html [ Failure Pass ]
+crbug.com/736177 [ Mac10.12 Retina ] tables/mozilla/bugs/bug4527.html [ Failure Pass ]
 
 crbug.com/736177 [ Mac ] svg/as-border-image/svg-as-border-image.html [ Failure Pass ]
 
@@ -2945,9 +3004,6 @@
 # module script lacks XHTML support
 crbug.com/717643 external/wpt/html/semantics/scripting-1/the-script-element/module/module-in-xhtml.xhtml [ Failure ]
 
-# Skia rebaseline
-skbug.com/6931 [ Linux ] fast/text/sub-pixel/text-scaling-pixel.html [ NeedsManualRebaseline ]
-
 # Geolocation tests
 crbug.com/745079 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ]
 
@@ -2990,6 +3046,7 @@
 
 # Sheriff failure 2017-08-11
 crbug.com/754657 http/tests/media/media-source/mediasource-duration.html [ Pass Failure ]
+crbug.com/626703 virtual/outofblink-cors/external/wpt/fetch/api/response/response-cancel-stream.html [ Pass Timeout ]
 
 # Ganesh dither changes
 crbug.com/753462 virtual/gpu/fast/canvas/canvas-text-alignment.html [ NeedsManualRebaseline ]
@@ -2997,3 +3054,9 @@
 crbug.com/753462 virtual/gpu/fast/canvas/gradient-add-second-start-end-stop.html [ NeedsManualRebaseline ]
 
 crbug.com/746904 [ Win ] fast/text/ellipsis-in-relative-inline.html [ Failure Pass ]
+
+crbug.com/731018 [ Mac ] sensor/accelerometer.html [ Failure Pass Crash ]
+crbug.com/731018 [ Mac ] sensor/ambient-light-sensor.html [ Failure Pass Crash ]
+crbug.com/731018 [ Mac ] sensor/gyroscope.html [ Failure Pass Crash ]
+crbug.com/731018 [ Mac ] sensor/magnetometer.html [ Failure Pass Crash ]
+crbug.com/731018 [ Mac ] sensor/orientation-sensor.html [ Failure Pass Crash ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 91ccd6c..ff22aa17 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -4845,6 +4845,54 @@
      {}
     ]
    ],
+   "compat/webkit-linear-gradient-line-bottom.html": [
+    [
+     "/compat/webkit-linear-gradient-line-bottom.html",
+     [
+      [
+       "/compat/green-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "compat/webkit-linear-gradient-line-left.html": [
+    [
+     "/compat/webkit-linear-gradient-line-left.html",
+     [
+      [
+       "/compat/green-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "compat/webkit-linear-gradient-line-right.html": [
+    [
+     "/compat/webkit-linear-gradient-line-right.html",
+     [
+      [
+       "/compat/green-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "compat/webkit-linear-gradient-line-top.html": [
+    [
+     "/compat/webkit-linear-gradient-line-top.html",
+     [
+      [
+       "/compat/green-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "compat/webkit-text-fill-color-property-001a.html": [
     [
      "/compat/webkit-text-fill-color-property-001a.html",
@@ -68073,6 +68121,11 @@
      {}
     ]
    ],
+   "compat/green-ref.html": [
+    [
+     {}
+    ]
+   ],
    "compat/webkit-text-fill-color-property-001-ref.html": [
     [
      {}
@@ -79153,6 +79206,11 @@
      {}
     ]
    ],
+   "css/css-transforms-2/animation/resources/interpolation-testcommon.js": [
+    [
+     {}
+    ]
+   ],
    "css/css-transforms-2/css-rotate-2d-3d-001-ref.html": [
     [
      {}
@@ -83713,6 +83771,11 @@
      {}
     ]
    ],
+   "css/motion-1/animation/resources/interpolation-testcommon.js": [
+    [
+     {}
+    ]
+   ],
    "css/motion-1/offset-path-ray-ref.html": [
     [
      {}
@@ -86388,6 +86451,11 @@
      {}
     ]
    ],
+   "editing/data/insert-list-items-in-table-cells.js": [
+    [
+     {}
+    ]
+   ],
    "editing/data/inserthorizontalrule.js": [
     [
      {}
@@ -126320,6 +126388,24 @@
      {}
     ]
    ],
+   "css/css-transforms-2/animation/rotate-interpolation.html": [
+    [
+     "/css/css-transforms-2/animation/rotate-interpolation.html",
+     {}
+    ]
+   ],
+   "css/css-transforms-2/animation/scale-interpolation.html": [
+    [
+     "/css/css-transforms-2/animation/scale-interpolation.html",
+     {}
+    ]
+   ],
+   "css/css-transforms-2/animation/translate-interpolation.html": [
+    [
+     "/css/css-transforms-2/animation/translate-interpolation.html",
+     {}
+    ]
+   ],
    "css/css-transforms-2/parsing/rotate-parsing-invalid.html": [
     [
      "/css/css-transforms-2/parsing/rotate-parsing-invalid.html",
@@ -126860,6 +126946,30 @@
      {}
     ]
    ],
+   "css/motion-1/animation/offset-anchor-interpolation.html": [
+    [
+     "/css/motion-1/animation/offset-anchor-interpolation.html",
+     {}
+    ]
+   ],
+   "css/motion-1/animation/offset-distance-interpolation.html": [
+    [
+     "/css/motion-1/animation/offset-distance-interpolation.html",
+     {}
+    ]
+   ],
+   "css/motion-1/animation/offset-position-interpolation.html": [
+    [
+     "/css/motion-1/animation/offset-position-interpolation.html",
+     {}
+    ]
+   ],
+   "css/motion-1/animation/offset-rotate-interpolation.html": [
+    [
+     "/css/motion-1/animation/offset-rotate-interpolation.html",
+     {}
+    ]
+   ],
    "css/motion-1/parsing/offset-anchor-parsing-invalid.html": [
     [
      "/css/motion-1/parsing/offset-anchor-parsing-invalid.html",
@@ -129878,6 +129988,12 @@
      {}
     ]
    ],
+   "editing/run/insert-list-items-in-table-cell.html": [
+    [
+     "/editing/run/insert-list-items-in-table-cell.html",
+     {}
+    ]
+   ],
    "editing/run/inserthorizontalrule.html": [
     [
      "/editing/run/inserthorizontalrule.html",
@@ -165638,6 +165754,12 @@
      {}
     ]
    ],
+   "wasm/create_multiple_memory.worker.js": [
+    [
+     "/wasm/create_multiple_memory.worker.html",
+     {}
+    ]
+   ],
    "wasm/wasm_indexeddb_test.html": [
     [
      "/wasm/wasm_indexeddb_test.html",
@@ -174884,7 +175006,7 @@
    "support"
   ],
   "./.travis.yml": [
-   "250f9bc8512445215c63504d28305b723c4b3385",
+   "8057d3e8925cb559271139ebbbfc516cad484a6c",
    "support"
   ],
   "./CONTRIBUTING.md": [
@@ -181644,7 +181766,7 @@
    "support"
   ],
   "WebIDL/current-realm.html": [
-   "167c7c1f53ae2bf457f6b3f917f0ef988c585c7c",
+   "3d9564314c7ce59ce6a29dfa94c35e496e214bf5",
    "testharness"
   ],
   "WebIDL/ecmascript-binding/es-exceptions/DOMException-constants.any.js": [
@@ -181816,7 +181938,7 @@
    "support"
   ],
   "XMLHttpRequest/access-control-and-redirects.htm": [
-   "2b635d3c25e01a971ce2e75efa63ede57194a8b3",
+   "ff051032814b1242f527970edc58675e3e14fb98",
    "testharness"
   ],
   "XMLHttpRequest/access-control-basic-allow-access-control-origin-header.htm": [
@@ -183272,7 +183394,7 @@
    "support"
   ],
   "beacon/headers/header-content-type-expected.txt": [
-   "369e5f74f98a790f954289e3786974c2941fb682",
+   "cfce1555852fb39dee45de50c96d2510d7c99267",
    "support"
   ],
   "beacon/headers/header-content-type.html": [
@@ -183595,6 +183717,26 @@
    "80579a1e88af8e60c7446f34446b90bd3e9cf8c7",
    "support"
   ],
+  "compat/green-ref.html": [
+   "4b23ea52d785a6dd19785bd4278bf700eb5547f8",
+   "support"
+  ],
+  "compat/webkit-linear-gradient-line-bottom.html": [
+   "af59a0aa3b8a195ba7ef401b582be9384a23a388",
+   "reftest"
+  ],
+  "compat/webkit-linear-gradient-line-left.html": [
+   "f131166051da9a82ede93f076f15832f61f39234",
+   "reftest"
+  ],
+  "compat/webkit-linear-gradient-line-right.html": [
+   "2d87c4a09d77f3171fa91bbf8f2f0b1a412b7d94",
+   "reftest"
+  ],
+  "compat/webkit-linear-gradient-line-top.html": [
+   "be7fb91dc6459617c20232cd5333e9b3340f3341",
+   "reftest"
+  ],
   "compat/webkit-text-fill-color-currentColor.html": [
    "7512b6ce45b5528ee7b9794c32954e954d736f88",
    "testharness"
@@ -186596,7 +186738,7 @@
    "reftest"
   ],
   "css-fonts/variations/font-parse-numeric-stretch-style-weight.html": [
-   "421e402060f1418562819bb6efae85a7a1cc8958",
+   "b9aa593e5fcba0d7af8f66446d473608a7025f1c",
    "testharness"
   ],
   "css-fonts/variations/resources/variabletest_box.ttf": [
@@ -210843,6 +210985,22 @@
    "86257012c615c6580cea602a1ee2f8617dcbb336",
    "support"
   ],
+  "css/css-transforms-2/animation/resources/interpolation-testcommon.js": [
+   "e53ebd086f11169bf516f4d2e68449290943ba98",
+   "support"
+  ],
+  "css/css-transforms-2/animation/rotate-interpolation.html": [
+   "f5b8b30c9bc0789d59b3451789c24009b93dd21f",
+   "testharness"
+  ],
+  "css/css-transforms-2/animation/scale-interpolation.html": [
+   "51271e16c98a7a4e6405c28d149e77eb439e670c",
+   "testharness"
+  ],
+  "css/css-transforms-2/animation/translate-interpolation.html": [
+   "9a28f675c790c3dec66f655418051a5a7ba52ec7",
+   "testharness"
+  ],
   "css/css-transforms-2/backface-visibility-hidden-001.html": [
    "d12cfa3b31b7f98d0ff5693bfde49247d34cb156",
    "reftest"
@@ -220087,6 +220245,26 @@
    "13d88ccf8c2f03db7e1db7f6fc704ba46a06e0e1",
    "support"
   ],
+  "css/motion-1/animation/offset-anchor-interpolation.html": [
+   "30eb3ea68aa502d93125d4e54ff15c0a96458cfa",
+   "testharness"
+  ],
+  "css/motion-1/animation/offset-distance-interpolation.html": [
+   "f242f67f0d1a1c25a871d4ae949d42f9d1df0027",
+   "testharness"
+  ],
+  "css/motion-1/animation/offset-position-interpolation.html": [
+   "6c21c93c0715af758033d7b582f072988b379b15",
+   "testharness"
+  ],
+  "css/motion-1/animation/offset-rotate-interpolation.html": [
+   "37c091bd5a59d4853bc9f5c82e157ac3d4f29643",
+   "testharness"
+  ],
+  "css/motion-1/animation/resources/interpolation-testcommon.js": [
+   "51a3f4808392b7bcce4f2569256c540a3be817dc",
+   "support"
+  ],
   "css/motion-1/offset-path-ray-ref.html": [
    "dfea2bd9cadf9d1b83e95bc2dbffef3539937ff1",
    "support"
@@ -225699,6 +225877,10 @@
    "821cc4fefcfce96832c1a4547698801b554ae426",
    "support"
   ],
+  "editing/data/insert-list-items-in-table-cells.js": [
+   "f6e8969d30a53e42c6adace5bd9598862b92c611",
+   "support"
+  ],
   "editing/data/inserthorizontalrule.js": [
    "118414ee839b94b9a747e0dfd2068d728c171db7",
    "support"
@@ -225951,6 +226133,10 @@
    "1bed1283ad3b220b2949c0720cba60105c888ec2",
    "testharness"
   ],
+  "editing/run/insert-list-items-in-table-cell.html": [
+   "7bb730a43273a9220ef491511acbdd5c91fbc9b7",
+   "testharness"
+  ],
   "editing/run/inserthorizontalrule-expected.txt": [
    "6535eed7f208b2eca892bdec4e712c240db6a9f5",
    "support"
@@ -229516,7 +229702,7 @@
    "testharness"
   ],
   "hr-time/timeOrigin.html": [
-   "3bb4eb8162169e582fea93a7cb1f6257325a0606",
+   "f07da265bb9d1036589ff116b14ea1370abc4194",
    "testharness"
   ],
   "hr-time/timing-attack.html": [
@@ -229708,7 +229894,7 @@
    "testharness"
   ],
   "html/browsers/browsing-the-web/history-traversal/PopStateEvent.html": [
-   "2a7ed0827fc61af7b3bdd238577887aff1902ea7",
+   "5a9c575a86adbbbca30734992b4d80c22f3973a1",
    "testharness"
   ],
   "html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html": [
@@ -245800,7 +245986,7 @@
    "testharness"
   ],
   "intersection-observer/timestamp.html": [
-   "b26e1b4b295722e8def9a2d6870465fe4140873c",
+   "b9bf8d472d7751ec4a1ebee925d12668bedeee7a",
    "testharness"
   ],
   "intersection-observer/unclipped-root.html": [
@@ -264300,7 +264486,7 @@
    "support"
   ],
   "server-timing/test_server_timing.html": [
-   "3f582e55c8d96a4fb1589ad9bd6e7ca26b75b7a2",
+   "bd80353850b822d6ee4a6b32ffae0e6e2120c61f",
    "testharness"
   ],
   "server-timing/test_server_timing.html.sub.headers": [
@@ -268847,6 +269033,10 @@
    "652193b876206d7a0f361f145469a604d03e3784",
    "support"
   ],
+  "wasm/create_multiple_memory.worker.js": [
+   "893d408fc56d030416a3c89ae3680dc028ecf1d7",
+   "testharness"
+  ],
   "wasm/incrementer.wasm": [
    "acdf9d22c042ea3b2637c14b1576b4c8ffb4e97a",
    "support"
@@ -271956,7 +272146,7 @@
    "support"
   ],
   "webusb/idlharness.https.html": [
-   "0da5e66dd0b0e82d25082733465c22c54a96d1de",
+   "135ae431cdac90d34b85fe1ea355abaf9a9fb732",
    "testharness"
   ],
   "webusb/resources/fake-devices.js": [
@@ -275076,7 +275266,7 @@
    "testharness"
   ],
   "workers/Worker_dispatchEvent_ErrorEvent.htm": [
-   "a1100df5a79ed7b484a8d5c5746bd646a165242b",
+   "a27efcba6fcdbb34bb07ac8553a6bbfa04761008",
    "testharness"
   ],
   "workers/Worker_script_mimetype.htm": [
@@ -275348,7 +275538,7 @@
    "testharness"
   ],
   "workers/data-url.html": [
-   "50abaf936cfb58ba14e6870c9b7f239f5d54f59c",
+   "8f854b63f97beae3d814478946ad86836a5cea0a",
    "testharness"
   ],
   "workers/interfaces.worker-expected.txt": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/.travis.yml b/third_party/WebKit/LayoutTests/external/wpt/.travis.yml
index 2744822..f4469e0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/.travis.yml
+++ b/third_party/WebKit/LayoutTests/external/wpt/.travis.yml
@@ -83,11 +83,8 @@
     - env: JOB=build_css SCRIPT=css/build-css-testsuites.sh
     - env:
         - secure: "YTSXPwI0DyCA1GhYrLT9KMEV6b7QQKuEeaQgeFDP38OTzJ1+cIj3CC4SRNqbnJ/6SJwPGcdqSxLuV8m4e5HFFnyCcQnJe6h8EMsTehZ7W3j/fP9UYrJqYqvGpe3Vj3xblO5pwBYmq7sg3jAmmuCgAgOW6VGf7cRMucrsmFeo7VM="
-        - JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=chrome:unstable
-    - env:
-        - secure: "YTSXPwI0DyCA1GhYrLT9KMEV6b7QQKuEeaQgeFDP38OTzJ1+cIj3CC4SRNqbnJ/6SJwPGcdqSxLuV8m4e5HFFnyCcQnJe6h8EMsTehZ7W3j/fP9UYrJqYqvGpe3Vj3xblO5pwBYmq7sg3jAmmuCgAgOW6VGf7cRMucrsmFeo7VM="
         - JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=sauce:MicrosoftEdge:14.14393 PLATFORM='Windows 10'
-      env:
+    - env:
         - secure: "YTSXPwI0DyCA1GhYrLT9KMEV6b7QQKuEeaQgeFDP38OTzJ1+cIj3CC4SRNqbnJ/6SJwPGcdqSxLuV8m4e5HFFnyCcQnJe6h8EMsTehZ7W3j/fP9UYrJqYqvGpe3Vj3xblO5pwBYmq7sg3jAmmuCgAgOW6VGf7cRMucrsmFeo7VM="
         - JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=sauce:safari:10.0 PLATFORM='macOS 10.12'
 script:
diff --git a/third_party/WebKit/LayoutTests/external/wpt/WebIDL/current-realm.html b/third_party/WebKit/LayoutTests/external/wpt/WebIDL/current-realm.html
index fd24709b..e015ec5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/WebIDL/current-realm.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/WebIDL/current-realm.html
@@ -110,8 +110,13 @@
      test(function() {
        var c = self[0].document.createElement("canvas"),
            obj = c.getContext(val)
-       assert_global(obj)
 
+       // WebGL might not be enabled in this environment
+       if (!obj && val === "webgl") {
+         return;
+       }
+
+       assert_global(obj)
        obj = HTMLCanvasElement.prototype.getContext.call(c, val)
        assert_global(obj)
      }, "getContext " + val)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/access-control-and-redirects.htm b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/access-control-and-redirects.htm
index 10c5e2f5..dcdf400 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/access-control-and-redirects.htm
+++ b/third_party/WebKit/LayoutTests/external/wpt/XMLHttpRequest/access-control-and-redirects.htm
@@ -16,7 +16,6 @@
       assert_equals(xhr.responseText, "PASS: Cross-domain access allowed.");
       test.done();
     }
-
     function runAsync(test, url)
     {
       const xhr = new XMLHttpRequest();
@@ -28,7 +27,6 @@
       xhr.send();
       test.done();
     }
-
     test(t => {
       runSync(t, "resources/redirect-cors.py?location=" + get_host_info().HTTP_REMOTE_ORIGIN +
           "/XMLHttpRequest/resources/access-control-basic-allow.py")
@@ -37,7 +35,6 @@
       runAsync(t, "resources/redirect-cors.py?location=" + get_host_info().HTTP_REMOTE_ORIGIN +
           "/XMLHttpRequest/resources/access-control-basic-allow.py")
     }, "Local async redirect to remote origin");
-
     test(t => {
       runSync(t, get_host_info().HTTP_REMOTE_ORIGIN +
           "/XMLHttpRequest/resources/redirect-cors.py?location=" + get_host_info().HTTP_ORIGIN +
@@ -48,7 +45,6 @@
           "/XMLHttpRequest/resources/redirect-cors.py?location=" + get_host_info().HTTP_ORIGIN +
           "/XMLHttpRequest/resources/access-control-basic-allow.py&allow_origin=true")
     }, "Remote async redirect to local origin");
-
     test(t => {
       runSync(t, get_host_info().HTTP_REMOTE_ORIGIN +
           "/XMLHttpRequest/resources/redirect-cors.py?location=" + get_host_info().HTTP_REMOTE_ORIGIN +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/compat/green-ref.html b/third_party/WebKit/LayoutTests/external/wpt/compat/green-ref.html
new file mode 100644
index 0000000..2671ff6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/compat/green-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A green 100x100 block</title>
+<link rel="author" title="Xidorn Quan" href="me@upsuper.ort">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<div style="width: 100px; height: 100px; background: green;"></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-bottom.html b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-bottom.html
new file mode 100644
index 0000000..21e1c894
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-bottom.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>-webkit-linear-gradient(bottom)</title>
+<link rel="author" title="Xidorn Quan" href="me@upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://compat.spec.whatwg.org/#css-gradients-webkit-linear-gradient">
+<meta name="assert" content="'bottom' in -webkit-linear-gradient is equivalent to 'to top' in modern syntax">
+<link rel="match" href="green-ref.html">
+<style>
+  #outer {
+    width: 100px;
+    height: 100px;
+    overflow: hidden;
+  }
+  #inner {
+    width: 100px;
+    height: 200px;
+    background-image: -webkit-linear-gradient(bottom, red 50%, green 50%);
+  }
+</style>
+<div id="outer">
+  <div id="inner"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-left.html b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-left.html
new file mode 100644
index 0000000..b0e13c1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-left.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>-webkit-linear-gradient(left)</title>
+<link rel="author" title="Xidorn Quan" href="me@upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://compat.spec.whatwg.org/#css-gradients-webkit-linear-gradient">
+<meta name="assert" content="'left' in -webkit-linear-gradient is equivalent to 'to right' in modern syntax">
+<link rel="match" href="green-ref.html">
+<style>
+  #outer {
+    width: 100px;
+    height: 100px;
+    overflow: hidden;
+  }
+  #inner {
+    width: 200px;
+    height: 100px;
+    background-image: -webkit-linear-gradient(left, green 50%, red 50%);
+  }
+</style>
+<div id="outer">
+  <div id="inner"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-right.html b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-right.html
new file mode 100644
index 0000000..0f1a4b9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-right.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>-webkit-linear-gradient(right)</title>
+<link rel="author" title="Xidorn Quan" href="me@upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://compat.spec.whatwg.org/#css-gradients-webkit-linear-gradient">
+<meta name="assert" content="'right' in -webkit-linear-gradient is equivalent to 'to left' in modern syntax">
+<link rel="match" href="green-ref.html">
+<style>
+  #outer {
+    width: 100px;
+    height: 100px;
+    overflow: hidden;
+  }
+  #inner {
+    width: 200px;
+    height: 100px;
+    background-image: -webkit-linear-gradient(right, red 50%, green 50%);
+  }
+</style>
+<div id="outer">
+  <div id="inner"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-top.html b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-top.html
new file mode 100644
index 0000000..579d882
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/compat/webkit-linear-gradient-line-top.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>-webkit-linear-gradient(top)</title>
+<link rel="author" title="Xidorn Quan" href="me@upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://compat.spec.whatwg.org/#css-gradients-webkit-linear-gradient">
+<meta name="assert" content="'top' in -webkit-linear-gradient is equivalent to 'to bottom' in modern syntax">
+<link rel="match" href="green-ref.html">
+<style>
+  #outer {
+    width: 100px;
+    height: 100px;
+    overflow: hidden;
+  }
+  #inner {
+    width: 100px;
+    height: 200px;
+    background-image: -webkit-linear-gradient(top, green 50%, red 50%);
+  }
+</style>
+<div id="outer">
+  <div id="inner"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html b/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html
index 53cef20..5152424 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css-fonts/variations/font-parse-numeric-stretch-style-weight.html
@@ -21,12 +21,15 @@
     'calc(100 + 300)',
     'calc(0.2 + 205.5)',
   ],
-  'stretch': ['51%', '199%', 'calc(10% + 20%)']
+  'stretch': ['51%', '199%', 'calc(10% + 20%)'],
+  'style' : [ 'normal', 'italic', 'oblique', 'oblique 50deg', 'oblique -90deg', 'oblique 90deg',
+              'oblique calc(30deg + 20deg)' ]
 };
 
 var styleInvalidTests = {
   'weight': ['100 400', 'calc(0 - 100)', 'calc(200 + 801)'],
-  'stretch': ['100% 110%', '0%', '100% 150%', 'calc(1 + 10%)']
+  'stretch': ['100% 110%', '0%', '100% 150%', 'calc(1 + 10%)'],
+  'style' : [ 'normal 10deg', 'italic 10deg', 'oblique -91deg', 'oblique 91deg', 'oblique calc(90deg + 20deg)']
 };
 
 function testParseStyle() {
@@ -70,6 +73,13 @@
     ['ultra-condensed', 'ultra-condensed'],
     ['ultra-expanded', 'ultra-expanded'],
   ],
+  'style' : [
+    [ "normal", "normal" ],
+    [ "italic", "italic" ],
+    [ "oblique", "oblique" ],
+    [ "oblique 10deg", "oblique 10deg" ],
+    [ "oblique 10deg 20deg", "oblique 10deg 20deg" ]
+  ]
 };
 
 var faceInvalidTests = {
@@ -87,9 +97,11 @@
     'a b c',
   ],
   'stretch': [
-    '0%', '60% 70% 80%', 'a%', 'a b c', '0.1', '-60% 80%', 'ultra-expannnned',
-    '50% 0'
+    '-0.5%', '-1%', '0%', 'calc(0% - 10%)', '60% 70% 80%', 'a%', 'a b c', '0.1',
+    '-60% 80%', 'ultra-expannnned', '50% 0'
   ],
+  'style' : [ 'oblique 100deg', 'oblique italic', 'oblique -91deg', 'oblique 0',
+              'oblique 10', 'iiitalic', '90deg', '11', 'italic 90deg' ]
 };
 
 function testParseFace() {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-anchor-interpolation.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-anchor-interpolation.html
new file mode 100644
index 0000000..414a0bd0d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-anchor-interpolation.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>offset-anchor interpolation</title>
+    <link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
+    <link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-anchor-property">
+    <meta name="assert" content="offset-anchor supports <position> animation.">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/interpolation-testcommon.js"></script>
+    <style>
+      body {
+        width: 500px;
+        height: 500px;
+        transform: rotate(0deg);
+      }
+      div {
+        width: 10px;
+        height: 10px;
+      }
+    </style>
+  </head>
+  <body>
+    <script>
+      test_interpolation({
+        property: 'offset-anchor',
+        from: '220px 240px',
+        to: '300px 400px',
+      }, [
+        {at: -1, expect: '140px 80px'},
+        {at: 0, expect: '220px 240px'},
+        {at: 0.125, expect: '230px 260px'},
+        {at: 0.875, expect: '290px 380px'},
+        {at: 1, expect: '300px 400px'},
+        {at: 2, expect: '380px 560px'}
+      ]);
+
+      test_interpolation({
+        property: 'offset-anchor',
+        from: 'left 480px top 400px',
+        to: 'right -140% bottom -60%',
+      }, [
+        {at: -1, expect: 'calc(960px - 240%) calc(800px - 160%)'},
+        {at: 0, expect: 'left 480px top 400px'},
+        {at: 0.125, expect: 'calc(420px + 30%) calc(350px + 20%)'},
+        {at: 0.875, expect: 'calc(210% + 60px) calc(140% + 50px)'},
+        {at: 1, expect: 'right -140% bottom -60%'},
+        {at: 2, expect: 'calc(480% - 480px) calc(320% - 400px)'}
+      ]);
+
+      test_interpolation({
+        property: 'offset-anchor',
+        from: 'left top',
+        to: 'left 8px bottom 20%',
+      }, [
+        {at: -1, expect: '-8px -80%'},
+        {at: 0, expect: 'left top'},
+        {at: 0.125, expect: '1px 10%'},
+        {at: 0.875, expect: '7px 70%'},
+        {at: 1, expect: 'left 8px bottom 20%'},
+        {at: 2, expect: '16px 160%'}
+      ]);
+
+      test_no_interpolation({
+        property: 'offset-anchor',
+        from: 'right 10px top 20%',
+        to: 'auto'
+      });
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-distance-interpolation.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-distance-interpolation.html
new file mode 100644
index 0000000..a56878a0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-distance-interpolation.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>offset-distance interpolation</title>
+    <link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
+    <link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-distance-property">
+    <meta name="assert" content="offset-distance supports animation.">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/interpolation-testcommon.js"></script>
+  </head>
+  <body>
+    <script>
+      test_interpolation({
+        property: 'offset-distance',
+        from: '-30px',
+        to: '50px',
+      }, [
+        {at: -1, expect: '-110px'},
+        {at: 0, expect: '-30px'},
+        {at: 0.125, expect: '-20px'},
+        {at: 0.875, expect: '40px'},
+        {at: 1, expect: '50px'},
+        {at: 2, expect: '130px'}
+      ]);
+
+      test_interpolation({
+        property: 'offset-distance',
+        from: '20%',
+        to: '100%',
+      }, [
+        {at: -1, expect: '-60%'},
+        {at: 0, expect: '20%'},
+        {at: 0.125, expect: '30%'},
+        {at: 0.875, expect: '90%'},
+        {at: 1, expect: '100%'},
+        {at: 2, expect: '180%'}
+      ]);
+
+      test_interpolation({
+        property: 'offset-distance',
+        from: 'calc(20% - 30px)',
+        to: 'calc(50px + 100%)',
+      }, [
+        {at: -1, expect: 'calc(-110px + -60%)'},
+        {at: 0, expect: 'calc(20% - 30px)'},
+        {at: 0.125, expect: 'calc(-20px + 30%)'},
+        {at: 0.875, expect: 'calc(40px + 90%)'},
+        {at: 1, expect: 'calc(50px + 100%)'},
+        {at: 2, expect: 'calc(130px + 180%)'}
+      ]);
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-position-interpolation.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-position-interpolation.html
new file mode 100644
index 0000000..c31034d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-position-interpolation.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>offset-position interpolation</title>
+    <link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
+    <link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-position-property">
+    <meta name="assert" content="offset-position supports <position> animation.">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/interpolation-testcommon.js"></script>
+    <style>
+      body {
+        width: 500px;
+        height: 500px;
+        transform: rotate(0deg);
+      }
+      div {
+        width: 10px;
+        height: 10px;
+      }
+    </style>
+  </head>
+  <body>
+    <script>
+      test_interpolation({
+        property: 'offset-position',
+        from: '220px 240px',
+        to: '300px 400px',
+      }, [
+        {at: -1, expect: '140px 80px'},
+        {at: 0, expect: '220px 240px'},
+        {at: 0.125, expect: '230px 260px'},
+        {at: 0.875, expect: '290px 380px'},
+        {at: 1, expect: '300px 400px'},
+        {at: 2, expect: '380px 560px'}
+      ]);
+
+      test_interpolation({
+        property: 'offset-position',
+        from: 'left 480px top 400px',
+        to: 'right -140% bottom -60%',
+      }, [
+        {at: -1, expect: 'calc(960px - 240%) calc(800px - 160%)'},
+        {at: 0, expect: 'left 480px top 400px'},
+        {at: 0.125, expect: 'calc(420px + 30%) calc(350px + 20%)'},
+        {at: 0.875, expect: 'calc(210% + 60px) calc(140% + 50px)'},
+        {at: 1, expect: 'right -140% bottom -60%'},
+        {at: 2, expect: 'calc(480% - 480px) calc(320% - 400px)'}
+      ]);
+
+      test_interpolation({
+        property: 'offset-position',
+        from: 'left top',
+        to: 'left 8px bottom 20%',
+      }, [
+        {at: -1, expect: '-8px -80%'},
+        {at: 0, expect: 'left top'},
+        {at: 0.125, expect: '1px 10%'},
+        {at: 0.875, expect: '7px 70%'},
+        {at: 1, expect: 'left 8px bottom 20%'},
+        {at: 2, expect: '16px 160%'}
+      ]);
+
+      test_no_interpolation({
+        property: 'offset-position',
+        from: 'right 10px top 20%',
+        to: 'auto'
+      });
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-rotate-interpolation.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-rotate-interpolation.html
new file mode 100644
index 0000000..a73c1a4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/offset-rotate-interpolation.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>offset-rotate interpolation</title>
+    <link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
+    <link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-rotate-property">
+    <meta name="assert" content="offset-rotate supports animation.">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/interpolation-testcommon.js"></script>
+  </head>
+  <body>
+    <script>
+      test_interpolation({
+        property: 'offset-rotate',
+        from: '100deg',
+        to: '180deg',
+      }, [
+        {at: -1, expect: '20deg'},
+        {at: 0, expect: '100deg'},
+        {at: 0.125, expect: '110deg'},
+        {at: 0.875, expect: '170deg'},
+        {at: 1, expect: '180deg'},
+        {at: 2, expect: '260deg'}
+      ]);
+
+      test_interpolation({
+        property: 'offset-rotate',
+        from: 'auto 100deg',
+        to: 'reverse',
+      }, [
+        {at: -1, expect: 'auto 20deg'},
+        {at: 0, expect: 'auto 100deg'},
+        {at: 0.125, expect: 'auto 110deg'},
+        {at: 0.875, expect: 'auto 170deg'},
+        {at: 1, expect: 'reverse'},
+        {at: 2, expect: 'auto 260deg'}
+      ]);
+
+      // No interpolation between auto/reverse and angle.
+      test_no_interpolation({
+        property: 'offset-rotate',
+        from: 'reverse 90deg',
+        to: '360deg',
+      });
+
+      test_no_interpolation({
+        property: 'offset-rotate',
+        from: '6rad',
+        to: 'auto',
+      });
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/resources/interpolation-testcommon.js b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/resources/interpolation-testcommon.js
new file mode 100644
index 0000000..be86c43
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion-1/animation/resources/interpolation-testcommon.js
@@ -0,0 +1,65 @@
+'use strict';
+function test_interpolation(settings, expectations) {
+  // Returns a timing function that at 0.5 evaluates to progress.
+  function timingFunction(progress) {
+    if (progress === 0)
+      return 'steps(1, end)';
+    if (progress === 1)
+      return 'steps(1, start)';
+    var y = (8 * progress - 1) / 6;
+    return 'cubic-bezier(0, ' + y + ', 1, ' + y + ')';
+  }
+
+  test(function(){
+    assert_true(CSS.supports(settings.property, settings.from), 'Value "' + settings.from + '" is supported by ' + settings.property);
+    assert_true(CSS.supports(settings.property, settings.to), 'Value "' + settings.to + '" is supported by ' + settings.property);
+  }, '"' + settings.from + '" and "' + settings.to + '" are valid ' + settings.property + ' values');
+
+  for (var i = 0; i < expectations.length; ++i) {
+    var progress = expectations[i].at;
+    var expectation = expectations[i].expect;
+    var animationId = 'anim' + i;
+    var targetId = 'target' + i;
+    var referenceId = 'reference' + i;
+
+    test(function(){
+      assert_true(CSS.supports(settings.property, expectation), 'Value "' + expectation + '" is supported by ' + settings.property);
+
+      var stylesheet = document.createElement('style');
+      stylesheet.textContent =
+        '#' + targetId + ' {\n' +
+        '  animation: 2s ' + timingFunction(progress) + ' -1s paused ' + animationId + ';\n' +
+        '}\n' +
+        '@keyframes ' + animationId + ' {\n' +
+        '  0% { ' + settings.property + ': ' + settings.from + '; }\n' +
+        '  100% { ' + settings.property + ': ' + settings.to + '; }\n' +
+        '}\n' +
+        '#' + referenceId + ' {\n' +
+        '  ' + settings.property + ': ' + expectation + ';\n' +
+        '}\n';
+      document.head.appendChild(stylesheet);
+
+      var target = document.createElement('div');
+      target.id = targetId;
+      document.body.appendChild(target);
+
+      var reference = document.createElement('div');
+      reference.id = referenceId;
+      document.body.appendChild(reference);
+      reference.style = '';
+
+      assert_equals(getComputedStyle(target)[settings.property], getComputedStyle(reference)[settings.property]);
+    }, 'Animation between "' + settings.from + '" and "' + settings.to + '" at progress ' + progress);
+  }
+}
+
+function test_no_interpolation(settings) {
+  var expectatFrom = [-1, 0, 0.125].map(function (progress) {
+    return {at: progress, expect: settings.from};
+  });
+  var expectatTo = [0.875, 1, 2].map(function (progress) {
+    return {at: progress, expect: settings.to};
+  });
+
+  test_interpolation(settings, expectatFrom.concat(expectatTo));
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html
index ace09e5..85205c1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html
@@ -6,12 +6,6 @@
 <div id="log"></div>
 <script>
 test(function () {
-  var e = document.createEvent('PopStateEvent');
-  var eProto = Object.getPrototypeOf(e);
-  assert_equals(eProto, PopStateEvent.prototype);
-}, 'document.createEvent');
-
-test(function () {
   assert_false('initPopStateEvent' in PopStateEvent.prototype,
                'There should be no PopStateEvent#initPopStateEvent');
 }, 'initPopStateEvent');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/intersection-observer/timestamp.html b/third_party/WebKit/LayoutTests/external/wpt/intersection-observer/timestamp.html
index cffd915..90032560 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/intersection-observer/timestamp.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/intersection-observer/timestamp.html
@@ -87,7 +87,13 @@
   // Test results are only significant if there's a gap between
   // top window time and iframe window time.
   assert_greater_than(topWindowTimeBeforeNotification, iframeWindowTimeAfterNotification,
-    "Time ranges for top and iframe windows are disjoint.");
+    "Time ranges for top and iframe windows are disjoint. Times: " +
+      [topWindowTimeOnTestStart, topWindowTimeBeforeCreatingIframe,
+       topWindowTimeBeforeNotification, topWindowTimeAfterNotification,
+       iframeWindowTimeBeforeNotification, iframeWindowTimeAfterNotification,
+       topWindowEntries[1].time - topWindowTimeBeforeNotification,
+       iframeWindowEntries[1].time - iframeWindowTimeBeforeNotification
+      ]);
 
   assert_equals(topWindowEntries.length, 2, "Top window observer has two notifications.");
   assert_between_inclusive(
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html b/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html
index 43164d69..6a6c8977 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/test_server_timing.html
@@ -11,21 +11,21 @@
       window.addEventListener('load', function() {
         // there should be exactly two server-timing entries, 1 for document, 1 for img#one
         test_entries(performance.getEntriesByType('navigation')[0].serverTiming, [{
-          value: 1.2,
-          metric: 'metric1',
+          duration: 1.2,
+          name: 'metric1',
           description: 'document',
         }])
         test_entries(performance.getEntriesByName(document.querySelector('img#one').src)[0].serverTiming, [{
-          value: 3.4,
-          metric: 'metric2',
+          duration: 3.4,
+          name: 'metric2',
           description: 'blue.png',
         }])
 
         new PerformanceObserver(function(entryList, observer) {
           // there should be exactly one server-timing entry, 1 for img#two
           test_entries(entryList.getEntriesByName(document.querySelector('img#two').src)[0].serverTiming, [{
-            value: 5.6,
-            metric: 'metric3',
+            duration: 5.6,
+            name: 'metric3',
             description: 'green.png',
           }])
           observer.disconnect()
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/Worker_dispatchEvent_ErrorEvent.htm b/third_party/WebKit/LayoutTests/external/wpt/workers/Worker_dispatchEvent_ErrorEvent.htm
index aea7e02..11ade605 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/Worker_dispatchEvent_ErrorEvent.htm
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/Worker_dispatchEvent_ErrorEvent.htm
@@ -25,12 +25,6 @@
 });
 
 test(function() {
-  var e = document.createEvent("ErrorEvent");
-  var eProto = Object.getPrototypeOf(e);
-  assert_equals(eProto, ErrorEvent.prototype);
-}, "document.createEvent('ErrorEvent')");
-
-test(function() {
   var e = new ErrorEvent("error");
   assert_false("initErrorEvent" in e, "should not be supported");
 }, "initErrorEvent");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html b/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html
index 3a4eb6c..8743490f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/data-url.html
@@ -25,7 +25,8 @@
     w.onmessage = t.step_func_done(function(e) {
       assert_unreached('Should not receive any message back.');
     });
-    w.onerror = t.step_func_done(function() {
+    w.onerror = t.step_func_done(function(e) {
+      assert_true(true, 'Should throw ' + e.message);
     });
   }, test_desc);
 }
@@ -50,8 +51,9 @@
 assert_worker_sends_pass('cross-origin worker', '', 'fetch("/").then(() => self.postMessage("FAIL"), () => self.postMessage("PASS"))');
 
 // 'data:' workers have opaque origin
-assert_worker_sends_pass('worker has opaque origin', 'application/javascript', 'if (self.location.origin == "null") postMessage("PASS"); else postMessage("FAIL");');
+assert_worker_sends_pass('worker has opaque origin', 'application/javascript', 'if (self.location.origin == "null") {postMessage("PASS");} else {postMessage("FAIL");}');
 
+setup({allow_uncaught_exception:true});
 // invalid javascript will trigger an ErrorEvent
 assert_worker_construction_fails('invalid javascript produces error', 'application/javascript', '}x=3');
 
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index ade4312..f46a177a 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-bounding-client-rect.html b/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-bounding-client-rect.html
index 4150018..f2e03d2c 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-bounding-client-rect.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-bounding-client-rect.html
@@ -8,16 +8,17 @@
     testRunner.dumpAsText();
 
     var range = document.createRange();
-    range.selectNodeContents(document.getElementById('div'));
+    range.selectNode(document.getElementById('div'));
     var rect = range.getBoundingClientRect();
 
     window.internals.setPageScaleFactor(2);
     var scaledRange = document.createRange();
-    scaledRange.selectNodeContents(document.getElementById('div'));
+    scaledRange.selectNode(document.getElementById('div'));
     var scaledRect = scaledRange.getBoundingClientRect();
 
     var result = document.getElementById("result");
-    if (rect.left == scaledRect.left &&
+    if (rect.width && rect.height &&
+        rect.left == scaledRect.left &&
         rect.top == scaledRect.top &&
         rect.right == scaledRect.right &&
         rect.bottom == scaledRect.bottom)
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-client-rects.html b/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-client-rects.html
index 65529dd..487c0937 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-client-rects.html
+++ b/third_party/WebKit/LayoutTests/fast/dom/Range/scale-page-client-rects.html
@@ -8,18 +8,19 @@
     testRunner.dumpAsText();
 
     var range = document.createRange();
-    range.selectNodeContents(document.getElementById('div'));
+    range.selectNode(document.getElementById('div'));
     var rectList = range.getClientRects();
     var rect = rectList[0];
 
     window.internals.setPageScaleFactor(2);
     var scaledRange = document.createRange();
-    scaledRange.selectNodeContents(document.getElementById('div'));
+    scaledRange.selectNode(document.getElementById('div'));
     var scaledRectList = scaledRange.getClientRects();
     var scaledRect = scaledRectList[0];
 
     var result = document.getElementById("result");
-    if (rect.left == scaledRect.left &&
+    if (rect.width && rect.height &&
+        rect.left == scaledRect.left &&
         rect.top == scaledRect.top &&
         rect.right == scaledRect.right &&
         rect.bottom == scaledRect.bottom)
diff --git a/third_party/WebKit/LayoutTests/fast/forms/password-length-emoji-expected.html b/third_party/WebKit/LayoutTests/fast/forms/password-length-emoji-expected.html
deleted file mode 100644
index 4f451de..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/password-length-emoji-expected.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!doctype html>
-<html>
-<body>
-<p> This tests that the number of bullet points shown in a password
-field reflects the number of "grapheme clusters" even if some of the grapheme
-clusters are characters outside the Basic Multilingual Plane, or combining
-characters. For <a href="https://crbug.com/486880">crbug.com/486880</a>.</p>
-<input type="password" value="x"></input> <!-- one ascii character -->
-<br><input type="password" value="cafe"></input> <!-- without combining character -->
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/password-length-emoji.html b/third_party/WebKit/LayoutTests/fast/forms/password-length-emoji.html
deleted file mode 100644
index 22258396..0000000
--- a/third_party/WebKit/LayoutTests/fast/forms/password-length-emoji.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!doctype html>
-<html>
-<body>
-<p> This tests that the number of bullet points shown in a password
-field reflects the number of "grapheme clusters" even if some of the grapheme
-clusters are characters outside the Basic Multilingual Plane, or combining
-characters. For <a href="https://crbug.com/486880">crbug.com/486880</a>.</p>
-<input type="password" value="😃"></input> <!-- Smiley face (U+1F603) -->
-<br><input type="password" value="cafe&#x0301;"></input> <!-- with combining character -->
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/gradients/background-clipped-expected.png b/third_party/WebKit/LayoutTests/fast/gradients/background-clipped-expected.png
index 3f4bcff..190b298a 100644
--- a/third_party/WebKit/LayoutTests/fast/gradients/background-clipped-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/gradients/background-clipped-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/sub-frame-svg-background-expected.html b/third_party/WebKit/LayoutTests/fast/repaint/sub-frame-svg-background-expected.html
new file mode 100644
index 0000000..b0f6008f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/repaint/sub-frame-svg-background-expected.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<iframe srcdoc="<html style='background:green;'></html>"></iframe>
+
diff --git a/third_party/WebKit/LayoutTests/fast/repaint/sub-frame-svg-background.html b/third_party/WebKit/LayoutTests/fast/repaint/sub-frame-svg-background.html
new file mode 100644
index 0000000..693e29d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/repaint/sub-frame-svg-background.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script>
+  if (window.testRunner) {
+    testRunner.overridePreference("WebKitWebSecurityEnabled", false);
+    testRunner.waitUntilDone();
+  }
+
+  function runTest() {
+    runAfterLayoutAndPaint(function(){
+        var frame = document.getElementById("frame");
+        frame.contentDocument.documentElement.style.background = "green";
+    }, true);
+  }
+</script>
+<iframe id="frame" src="data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20style%3D%22background%3Ared%3B%22%3E%3C%2Fsvg%3E" onload="runTest();"></iframe>
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/datagrid-editable-longtext-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/datagrid-editable-longtext-expected.txt
new file mode 100644
index 0000000..1b4bd59f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/datagrid-editable-longtext-expected.txt
@@ -0,0 +1,31 @@
+This tests long text in datagrid.
+Original lengths
+key text length: 1500
+value text length: 1000
+
+Test committing a long key
+key element is being edited
+key text length: 3000
+Blurring the key
+Editor value committed.
+key text length: 3000
+
+Test no-op editing the key
+key element is being edited
+key text length: 3000
+Blurring the key
+key text length: 3000
+
+Test committing a long value
+value element is being edited
+value text length: 3000
+Blurring the value
+Editor value committed.
+value text length: 1000
+
+Test no-op editing the value
+value element is being edited
+value text length: 3000
+Blurring the value
+value text length: 1000
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/datagrid-editable-longtext.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/datagrid-editable-longtext.js
new file mode 100644
index 0000000..cd25055
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/datagrid-editable-longtext.js
@@ -0,0 +1,72 @@
+TestRunner.loadModule('data_grid').then(test);
+
+function test() {
+  TestRunner.addResult("This tests long text in datagrid.");
+
+  var columns = [
+    {id: "key", title: "Key column", editable: true, longText: false},
+    {id: "value", title: "Value column", editable: true, longText: true}
+  ];
+  var dataGrid = new DataGrid.DataGrid(columns, onEdit);
+  UI.inspectorView.element.appendChild(dataGrid.element);
+
+  var rootNode = dataGrid.rootNode();
+  var node = new DataGrid.DataGridNode({key: "k".repeat(1500), value: "v".repeat(1500)});
+  rootNode.appendChild(node);
+
+  var keyElement = dataGrid.element.querySelector(".data .key-column");
+  var valueElement = dataGrid.element.querySelector(".data .value-column");
+
+  TestRunner.addResult("Original lengths");
+  dumpKeyLength();
+  dumpValueLength();
+
+  TestRunner.addResult("\nTest committing a long key");
+  dataGrid._startEditing(keyElement);
+  keyElement.textContent = "k".repeat(3000);
+  dumpKeyLength();
+  TestRunner.addResult("Blurring the key");
+  keyElement.blur();
+  dumpKeyLength();
+
+  TestRunner.addResult("\nTest no-op editing the key");
+  dataGrid._startEditing(keyElement);
+  dumpKeyLength();
+  TestRunner.addResult("Blurring the key");
+  keyElement.blur();
+  dumpKeyLength();
+
+  TestRunner.addResult("\nTest committing a long value");
+  dataGrid._startEditing(valueElement);
+  valueElement.textContent = "v".repeat(3000);
+  dumpValueLength();
+  TestRunner.addResult("Blurring the value");
+  valueElement.blur();
+  dumpValueLength();
+
+  TestRunner.addResult("\nTest no-op editing the value");
+  dataGrid._startEditing(valueElement);
+  dumpValueLength();
+  TestRunner.addResult("Blurring the value");
+  valueElement.blur();
+  dumpValueLength();
+
+
+  TestRunner.completeTest();
+
+  function dumpKeyLength() {
+    if (keyElement.classList.contains("being-edited"))
+      TestRunner.addResult("key element is being edited");
+    TestRunner.addResult("key text length: " + keyElement.textContent.length);
+  }
+
+  function dumpValueLength() {
+    if (valueElement.classList.contains("being-edited"))
+      TestRunner.addResult("value element is being edited");
+    TestRunner.addResult("value text length: " + valueElement.textContent.length);
+  }
+
+  function onEdit() {
+    TestRunner.addResult("Editor value committed.");
+  }
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-active-and-passive-reload.html b/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-active-and-passive-reload.html
index c6d6a1b..35a56592 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-active-and-passive-reload.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-active-and-passive-reload.html
@@ -11,8 +11,20 @@
     InspectorTest.addResult("\nBefore Refresh --------------");
 
     var mixedExplanations = [
-        /** @type {!Protocol.Security.SecurityStateExplanation} */ ({"securityState": Protocol.Security.SecurityState.Neutral, "summary": "Neutral Test Summary", "description": "Neutral Test Description", mixedContentType: Protocol.Security.MixedContentType.OptionallyBlockable}),
-        /** @type {!Protocol.Security.SecurityStateExplanation} */ ({"securityState": Protocol.Security.SecurityState.Insecure, "summary": "Insecure Test Summary", "description": "Insecure Test Description", mixedContentType: Protocol.Security.MixedContentType.Blockable})
+        {
+            securityState: Protocol.Security.SecurityState.Neutral,
+            summary: "Neutral Test Summary",
+            description: "Neutral Test Description",
+            mixedContentType: Protocol.Security.MixedContentType.OptionallyBlockable,
+            certificate: []
+        },
+        {
+            securityState: Protocol.Security.SecurityState.Insecure,
+            summary: "Insecure Test Summary",
+            description: "Insecure Test Description",
+            mixedContentType: Protocol.Security.MixedContentType.Blockable,
+            certificate: []
+        }
     ];
     InspectorTest.mainTarget.model(Security.SecurityModel).dispatchEventToListeners(Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState(Protocol.Security.SecurityState.Neutral, true, mixedExplanations, insecureContentStatus, null));
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-reload.html b/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-reload.html
index 8200b6e..b8ada9e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-reload.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/security/mixed-content-reload.html
@@ -11,7 +11,13 @@
     InspectorTest.addResult("\nBefore Refresh --------------");
 
     var mixedExplanations = [
-        /** @type {!Protocol.Security.SecurityStateExplanation} */ ({"securityState": Protocol.Security.SecurityState.Neutral, "summary": "Neutral Test Summary", "description": "Neutral Test Description", mixedContentType: Protocol.Security.MixedContentType.OptionallyBlockable})
+        {
+            securityState: Protocol.Security.SecurityState.Neutral,
+            summary: "Neutral Test Summary",
+            description: "Neutral Test Description",
+            mixedContentType: Protocol.Security.MixedContentType.OptionallyBlockable,
+            certificate: []
+        }
     ];
     InspectorTest.mainTarget.model(Security.SecurityModel).dispatchEventToListeners(Security.SecurityModel.Events.SecurityStateChanged, new Security.PageSecurityState(Protocol.Security.SecurityState.Neutral, true, mixedExplanations, insecureContentStatus, null));
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-explanation-ordering.html b/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-explanation-ordering.html
index d6d2707..cf1bcc2 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-explanation-ordering.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/security/security-explanation-ordering.html
@@ -13,26 +13,28 @@
     // first to make sure it gets reordered.
     var explanations = [
         {
-            "description": "Public-key pinning was bypassed by a local root certificate.",
-            "securityState": "info",
-            "summary": "Public-Key Pinning Bypassed"
+            description: "Public-key pinning was bypassed by a local root certificate.",
+            securityState: "info",
+            summary: "Public-Key Pinning Bypassed",
+            certificate: []
         },
         {
-            "hasCertificate": "true",
-            "description": "The connection to this site is using a valid, trusted server certificate.",
-            "securityState": "secure",
-            "summary": "Valid Certificate"
+            description: "The connection to this site is using a valid, trusted server certificate.",
+            securityState: "secure",
+            summary: "Valid Certificate",
+            certificate: ["BASE64CERTIFICATE"]
         },
         {
-            "description": "The connection to this site uses a strong protocol (TLS 1.2), a strong key exchange (ECDHE_RSA), and an obsolete cipher (AES_256_CBC with HMAC-SHA1).",
-            "securityState": "secure",
-            "summary": "Obsolete Connection Settings"
-
+            description: "The connection to this site uses a strong protocol (TLS 1.2), a strong key exchange (ECDHE_RSA), and an obsolete cipher (AES_256_CBC with HMAC-SHA1).",
+            securityState: "secure",
+            summary: "Obsolete Connection Settings",
+            certificate: []
         },
         {
-            "description": "All resources on this page are served securely.",
-            "securityState": "secure",
-            "summary": "Secure resources"
+            description: "All resources on this page are served securely.",
+            securityState: "secure",
+            summary: "Secure resources",
+            certificate: []
         }
     ];
 
diff --git a/third_party/WebKit/LayoutTests/images/exif-orientation-height-image-document-expected.png b/third_party/WebKit/LayoutTests/images/exif-orientation-height-image-document-expected.png
index cd52fb195..cdb6f4f5 100644
--- a/third_party/WebKit/LayoutTests/images/exif-orientation-height-image-document-expected.png
+++ b/third_party/WebKit/LayoutTests/images/exif-orientation-height-image-document-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/browser/browser-version-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/browser/browser-version-expected.txt
new file mode 100644
index 0000000..24247d8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/browser/browser-version-expected.txt
@@ -0,0 +1,6 @@
+Verifies Browser.getVersion method.
+version.protocolVersion: OK
+version.revision: OK
+version.userAgent: OK
+version.jsVersion: OK
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/browser/browser-version.js b/third_party/WebKit/LayoutTests/inspector-protocol/browser/browser-version.js
new file mode 100644
index 0000000..4673acb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/browser/browser-version.js
@@ -0,0 +1,14 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startBlank('Verifies Browser.getVersion method.');
+  var response = await dp.Browser.getVersion();
+  check('protocolVersion', /^\d+\.\d+$/); // e.g. 1.2
+  check('revision', /^@[0-9abcdef]+$/); // e.g. @7d9cc1464f836e6d8c1ab9396a48c656df153d58
+  check('userAgent', /^.+$/); // e.g. Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.77.34.5 Safari/537.36
+  check('jsVersion', /^.+$/); // e.g. 6.2.196
+  testRunner.completeTest();
+
+  function check(fieldName, regex) {
+    var status = regex.test(response.result[fieldName]) ? 'OK' : 'FAIL';
+    testRunner.log(`version.${fieldName}: ${status}`);
+  }
+})
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/absolute-sized-document-no-scrollbars-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/absolute-sized-document-no-scrollbars-expected.txt
index df6f7c4..a01cdd3 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/absolute-sized-document-no-scrollbars-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/absolute-sized-document-no-scrollbars-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "style change"
+        },
+        {
           "object": "LayoutSVGRect rect",
           "rect": [0, 0, 576, 432],
           "reason": "style change"
@@ -26,6 +31,10 @@
   ],
   "objectPaintInvalidations": [
     {
+      "object": "LayoutView #document",
+      "reason": "style change"
+    },
+    {
       "object": "LayoutSVGRoot svg",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
index d8e84657..93d817a 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/relative-sized-document-scrollbars-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutView #document",
+          "rect": [0, 0, 800, 600],
+          "reason": "style change"
+        },
+        {
           "object": "LayoutSVGRect rect",
           "rect": [6, 4, 788, 592],
           "reason": "style change"
@@ -21,6 +26,10 @@
   ],
   "objectPaintInvalidations": [
     {
+      "object": "LayoutView #document",
+      "reason": "style change"
+    },
+    {
       "object": "LayoutSVGRoot svg",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png
index d41092cd..e2bde95 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png
index b4cad73..1b3c400 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
index 85b3331..d8e4bc4 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
index a3f2569..028e3c1a 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-inner-bleed-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-inner-bleed-expected.png
index 0eab2bdf..cbb7d12 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-inner-bleed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-inner-bleed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-expected.png
index 1795b005..55b60e3 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-shadow-expected.png
index 9b5c937..ebe81ec 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/image-object-in-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/image-object-in-canvas-expected.png
index fd50afb..b6a7f6c 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/image-object-in-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/image-object-in-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png
index cf3831d..e3b1a02 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png
index 4cb5bc6..80125af 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
index 41911ac..df8d472 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/batik/filters/feTile-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/batik/filters/feTile-expected.png
index 28a45a3..7f5d1c2 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/batik/filters/feTile-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/batik/filters/feTile-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/smallFonts-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/smallFonts-expected.png
index 4a6abd7..9baedf1 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/smallFonts-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/smallFonts-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/textFeatures-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/textFeatures-expected.png
index e7a2fb2..b1c7cef 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/textFeatures-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/batik/text/textFeatures-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/focus-ring-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/focus-ring-expected.png
index 14da8c6e..b4534f5 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/focus-ring-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/focus-ring-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pattern-rotate-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pattern-rotate-expected.png
index ecabe78..ada9f41 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pattern-rotate-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/pattern-rotate-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/transformed-outlines-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/transformed-outlines-expected.png
index 16826b45..43fbe89 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/transformed-outlines-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/transformed-outlines-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/hixie/perf/004-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/hixie/perf/004-expected.png
index 4c5450f..0862b5ba7 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/hixie/perf/004-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/hixie/perf/004-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-inside-transformed-html-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
index 998fed4..988c294e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-with-svg-transform-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-with-svg-transform-expected.png
index d20544ae..0bdd37bb 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-with-svg-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/transforms/text-with-pattern-with-svg-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/transforms/transformed-document-element-expected.png b/third_party/WebKit/LayoutTests/platform/linux/transforms/transformed-document-element-expected.png
index f43c0d24..49c2a80 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/transforms/transformed-document-element-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/transforms/transformed-document-element-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
index 1795b005..55b60e3 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
index 9b5c937..ebe81ec 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
index c93f73f0..a7603f3 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/placeholder-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/placeholder-position-expected.png
index 0f91d1e0..aee0d16 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/placeholder-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/placeholder-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/placeholder-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/placeholder-position-expected.png
index 284ceea..68da93f71 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/placeholder-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/forms/placeholder-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-expected.png
index 9877fa03..c0aea11f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-shadow-expected.png
index f6b9b659..6f89722 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/placeholder-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/placeholder-position-expected.png
index 655db98..ba959f0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/placeholder-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/placeholder-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/layers/scroll-with-transform-layer-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/layers/scroll-with-transform-layer-expected.png
index b94996b..066f8be 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/layers/scroll-with-transform-layer-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/layers/scroll-with-transform-layer-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/foreign-object-skew-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/foreign-object-skew-expected.png
index c0aa7b730..35db139c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/foreign-object-skew-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/foreign-object-skew-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
index 9877fa03..c0aea11f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
index f6b9b659..6f89722 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/placeholder-position-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/placeholder-position-expected.png
index 284ceea..68da93f71 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/placeholder-position-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/forms/placeholder-position-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png
index cd6d214..5050e02e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png
index 9517ed2a..a344d8c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png
index 349bd305..09073d2 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
index fbc8982..8881d88 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-image-rotate-transform-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-image-rotate-transform-expected.png
index c79a602..baf5817 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-image-rotate-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-image-rotate-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-inner-bleed-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-inner-bleed-expected.png
index 57c869b09..88f67dc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-inner-bleed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-inner-bleed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-expected.png
index 89b633e1..4cbdf75 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-shadow-expected.png
index bfd4965..90973311 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/image-object-in-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/image-object-in-canvas-expected.png
index ebff45f8..6ef7d5c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/image-object-in-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/image-object-in-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png
index f4f175d..423ae30 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/layers/scroll-with-transform-layer-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/layers/scroll-with-transform-layer-expected.png
index ddc33d6..e59d3a3 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/layers/scroll-with-transform-layer-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/layers/scroll-with-transform-layer-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png
index 161c887..977b328 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
index c2ded6e8..759daf8 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/filters/feTile-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/filters/feTile-expected.png
index 5364dc6..cb260e92 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/filters/feTile-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/filters/feTile-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/smallFonts-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/smallFonts-expected.png
index 0f003bd..2e864da 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/smallFonts-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/smallFonts-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textFeatures-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textFeatures-expected.png
index b4b029d..83765b08 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textFeatures-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textFeatures-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/focus-ring-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/focus-ring-expected.png
index b90cbed..0301123 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/focus-ring-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/focus-ring-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/foreign-object-skew-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/foreign-object-skew-expected.png
index a71aefe..c5a07ab 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/foreign-object-skew-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/foreign-object-skew-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pattern-rotate-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pattern-rotate-expected.png
index 17ecef5..9be0afc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pattern-rotate-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/pattern-rotate-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/transformed-outlines-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/transformed-outlines-expected.png
index c7379b7..ec336cb 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/transformed-outlines-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/transformed-outlines-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/hixie/perf/004-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/hixie/perf/004-expected.png
index 46b1b9d2..f8392e94 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/hixie/perf/004-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/hixie/perf/004-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-inside-transformed-html-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
index 731024c..7c7281fc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-with-svg-transform-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-with-svg-transform-expected.png
index 580ebf3..0fafdf7 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-with-svg-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/transforms/text-with-pattern-with-svg-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-document-element-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-document-element-expected.png
index b3a8853..6ad401a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-document-element-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-document-element-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
index 89b633e1..4cbdf75 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
index bfd4965..90973311 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
index ae68f84..5a04d50 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png
index 326cf1e..3349df3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png
index d59dd8e..19c47c13 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png
index ec7415fb..6f6ae4c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
index e91a060..8ac880e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/repeat/negative-offset-repeat-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-image-rotate-transform-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-image-rotate-transform-expected.png
index 3efca06..1d8da6b 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-image-rotate-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-image-rotate-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-inner-bleed-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-inner-bleed-expected.png
index b26d4fd..d5c1163 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-inner-bleed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-inner-bleed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-expected.png
index db2e272..5754aae 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-shadow-expected.png
index a578bd10..05a993d 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/image-object-in-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/image-object-in-canvas-expected.png
index e8f7162..bdbc6ab 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/image-object-in-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/image-object-in-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png
index 4d902fa..589c940 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png
index 9cec965..f76e53c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
index a281ad5..89f387f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/animated-path-inside-transformed-html-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/batik/filters/feTile-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/batik/filters/feTile-expected.png
index bfc9e2c..630122b1 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/batik/filters/feTile-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/batik/filters/feTile-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/smallFonts-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/smallFonts-expected.png
index b170da1a..c7b51fd 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/smallFonts-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/smallFonts-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/textFeatures-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/textFeatures-expected.png
index a436184..a1a56c3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/textFeatures-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/batik/text/textFeatures-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/focus-ring-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/focus-ring-expected.png
index f141ebb..45c44f9 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/focus-ring-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/focus-ring-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pattern-rotate-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pattern-rotate-expected.png
index 740098a..8adf3b5 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/pattern-rotate-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/pattern-rotate-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/transformed-outlines-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/transformed-outlines-expected.png
index 9fe658ad..de89ae7 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/transformed-outlines-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/transformed-outlines-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/hixie/perf/004-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/hixie/perf/004-expected.png
index d57f21b2..aa0fd8c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/hixie/perf/004-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/hixie/perf/004-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-inside-transformed-html-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
index 48034234..3738002 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-inside-transformed-html-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-with-svg-transform-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-with-svg-transform-expected.png
index 8b2d555..a36dd18 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-with-svg-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/transforms/text-with-pattern-with-svg-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/transforms/transformed-document-element-expected.png b/third_party/WebKit/LayoutTests/platform/win/transforms/transformed-document-element-expected.png
index 137dee85..c639d58 100644
--- a/third_party/WebKit/LayoutTests/platform/win/transforms/transformed-document-element-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/transforms/transformed-document-element-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
index db2e272..5754aae 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
index a578bd10..05a993d 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
index e73fe3a..35673c7 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/display_list_2d_canvas/fast/canvas/image-object-in-canvas-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 1649230..bbfecdf 100644
--- a/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/display_list_2d_canvas/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/exif-orientation-height-image-document-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/exif-orientation-height-image-document-expected.png
index 679d3b5f..6366d72 100644
--- a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/exif-orientation-height-image-document-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/exif-orientation-height-image-document-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
index 553a40d..adcfe35b 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
@@ -4939,8 +4939,8 @@
 interface PerformanceServerTiming
     attribute @@toStringTag
     getter description
-    getter metric
-    getter value
+    getter duration
+    getter name
     method constructor
 interface PerformanceTiming
     attribute @@toStringTag
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index f1c0e7d..ff1ddb4 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -4946,8 +4946,8 @@
 interface PerformanceServerTiming
     attribute @@toStringTag
     getter description
-    getter metric
-    getter value
+    getter duration
+    getter name
     method constructor
 interface PerformanceTiming
     attribute @@toStringTag
diff --git a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
index 0e04b24..c4c6528 100644
--- a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
@@ -911,7 +911,7 @@
 }
 
 void {{v8_class_or_partial}}::install{{feature.name}}(ScriptState* scriptState, v8::Local<v8::Object> instance) {
-  V8PerContextData* perContextData = V8PerContextData::From(scriptState->GetContext());
+  V8PerContextData* perContextData = scriptState->PerContextData();
   v8::Local<v8::Object> prototype = perContextData->PrototypeForType(&{{v8_class}}::wrapperTypeInfo);
   v8::Local<v8::Function> interface = perContextData->ConstructorForType(&{{v8_class}}::wrapperTypeInfo);
   ALLOW_UNUSED_LOCAL(interface);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
index fd15644..d2afdd9 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
@@ -199,7 +199,7 @@
 }
 
 void V8TestConstants::installFeatureName1(ScriptState* scriptState, v8::Local<v8::Object> instance) {
-  V8PerContextData* perContextData = V8PerContextData::From(scriptState->GetContext());
+  V8PerContextData* perContextData = scriptState->PerContextData();
   v8::Local<v8::Object> prototype = perContextData->PrototypeForType(&V8TestConstants::wrapperTypeInfo);
   v8::Local<v8::Function> interface = perContextData->ConstructorForType(&V8TestConstants::wrapperTypeInfo);
   ALLOW_UNUSED_LOCAL(interface);
@@ -218,7 +218,7 @@
 }
 
 void V8TestConstants::installFeatureName2(ScriptState* scriptState, v8::Local<v8::Object> instance) {
-  V8PerContextData* perContextData = V8PerContextData::From(scriptState->GetContext());
+  V8PerContextData* perContextData = scriptState->PerContextData();
   v8::Local<v8::Object> prototype = perContextData->PrototypeForType(&V8TestConstants::wrapperTypeInfo);
   v8::Local<v8::Function> interface = perContextData->ConstructorForType(&V8TestConstants::wrapperTypeInfo);
   ALLOW_UNUSED_LOCAL(interface);
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
index f88287b..4748e12 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -13060,7 +13060,7 @@
 }
 
 void V8TestObject::installFeatureName(ScriptState* scriptState, v8::Local<v8::Object> instance) {
-  V8PerContextData* perContextData = V8PerContextData::From(scriptState->GetContext());
+  V8PerContextData* perContextData = scriptState->PerContextData();
   v8::Local<v8::Object> prototype = perContextData->PrototypeForType(&V8TestObject::wrapperTypeInfo);
   v8::Local<v8::Function> interface = perContextData->ConstructorForType(&V8TestObject::wrapperTypeInfo);
   ALLOW_UNUSED_LOCAL(interface);
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp
index 070f582..0d32c442 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterfacePartial.cpp
@@ -507,7 +507,7 @@
 }
 
 void V8TestInterfacePartial::installOriginTrialPartialFeature(ScriptState* scriptState, v8::Local<v8::Object> instance) {
-  V8PerContextData* perContextData = V8PerContextData::From(scriptState->GetContext());
+  V8PerContextData* perContextData = scriptState->PerContextData();
   v8::Local<v8::Object> prototype = perContextData->PrototypeForType(&V8TestInterface::wrapperTypeInfo);
   v8::Local<v8::Function> interface = perContextData->ConstructorForType(&V8TestInterface::wrapperTypeInfo);
   ALLOW_UNUSED_LOCAL(interface);
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index f80c029..fb9988c5 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1314,7 +1314,6 @@
     "css/invalidation/StyleInvalidatorTest.cpp",
     "css/parser/CSSLazyParsingTest.cpp",
     "css/parser/CSSParserFastPathsTest.cpp",
-    "css/parser/CSSParserTokenStreamTest.cpp",
     "css/parser/CSSParserTokenTest.cpp",
     "css/parser/CSSPropertyParserTest.cpp",
     "css/parser/CSSSelectorParserTest.cpp",
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index ee3fcd9..5d886d9 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -351,7 +351,6 @@
     "parser/CSSParserToken.h",
     "parser/CSSParserTokenRange.cpp",
     "parser/CSSParserTokenRange.h",
-    "parser/CSSParserTokenStream.cpp",
     "parser/CSSParserTokenStream.h",
     "parser/CSSPropertyParser.cpp",
     "parser/CSSPropertyParser.h",
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp
index 5e68393..9d9803f 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserImpl.cpp
@@ -169,7 +169,8 @@
   CSSParserImpl parser(context, document.ElementSheet().Contents());
   CSSTokenizer tokenizer(string);
   CSSParserTokenStream stream(tokenizer);
-  parser.ConsumeDeclarationList(stream, StyleRule::kStyle);
+  // TODO(shend): Use streams instead of ranges
+  parser.ConsumeDeclarationList(stream.MakeRangeToEOF(), StyleRule::kStyle);
   return CreateStylePropertySet(parser.parsed_properties_, mode);
 }
 
@@ -182,7 +183,8 @@
     rule_type = StyleRule::kViewport;
   CSSTokenizer tokenizer(string);
   CSSParserTokenStream stream(tokenizer);
-  parser.ConsumeDeclarationList(stream, rule_type);
+  // TODO(shend): Use streams instead of ranges
+  parser.ConsumeDeclarationList(stream.MakeRangeToEOF(), rule_type);
   if (parser.parsed_properties_.IsEmpty())
     return false;
 
@@ -846,57 +848,6 @@
       CreateStylePropertySet(parsed_properties_, context_->Mode()));
 }
 
-void CSSParserImpl::ConsumeDeclarationList(CSSParserTokenStream& stream,
-                                           StyleRule::RuleType rule_type) {
-  DCHECK(parsed_properties_.IsEmpty());
-
-  bool use_observer = observer_wrapper_ && (rule_type == StyleRule::kStyle ||
-                                            rule_type == StyleRule::kKeyframe);
-  DCHECK(!use_observer);  // TODO(shend): Implement streaming with observers.
-
-  while (!stream.AtEnd()) {
-    switch (stream.UncheckedPeek().GetType()) {
-      case kWhitespaceToken:
-      case kSemicolonToken:
-        stream.UncheckedConsume();
-        break;
-      case kIdentToken: {
-        // TODO(shend): Use streams instead of ranges
-        auto range = stream.MakeRangeToEOF();
-
-        const CSSParserToken* declaration_start = &range.Peek();
-        while (!range.AtEnd() && range.Peek().GetType() != kSemicolonToken)
-          range.ConsumeComponentValue();
-
-        ConsumeDeclaration(range.MakeSubRange(declaration_start, &range.Peek()),
-                           rule_type);
-
-        stream.UpdatePositionFromRange(range);
-        break;
-      }
-      case kAtKeywordToken: {
-        AllowedRulesType allowed_rules =
-            rule_type == StyleRule::kStyle &&
-                    RuntimeEnabledFeatures::CSSApplyAtRulesEnabled()
-                ? kApplyRules
-                : kNoRules;
-
-        // TODO(shend): Use streams instead of ranges
-        auto range = stream.MakeRangeToEOF();
-        StyleRuleBase* rule = ConsumeAtRule(range, allowed_rules);
-        stream.UpdatePositionFromRange(range);
-        DCHECK(!rule);
-        break;
-      }
-      default:  // Parse error, unexpected token in declaration list
-        while (!stream.AtEnd() &&
-               stream.UncheckedPeek().GetType() != kSemicolonToken)
-          stream.UncheckedConsumeComponentValue();
-        break;
-    }
-  }
-}
-
 void CSSParserImpl::ConsumeDeclarationList(CSSParserTokenRange range,
                                            StyleRule::RuleType rule_type) {
   DCHECK(parsed_properties_.IsEmpty());
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h b/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h
index a467eea3f..df169ad 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserImpl.h
@@ -21,7 +21,6 @@
 class CSSParserContext;
 class CSSParserObserver;
 class CSSParserObserverWrapper;
-class CSSParserTokenStream;
 class StyleRule;
 class StyleRuleBase;
 class StyleRuleCharset;
@@ -141,8 +140,6 @@
   StyleRule* ConsumeStyleRule(CSSParserTokenRange prelude,
                               CSSParserTokenRange block);
 
-  void ConsumeDeclarationList(CSSParserTokenStream&, StyleRule::RuleType);
-  // TODO(shend): Remove this overload once we switch over to streams.
   void ConsumeDeclarationList(CSSParserTokenRange, StyleRule::RuleType);
   void ConsumeDeclaration(CSSParserTokenRange, StyleRule::RuleType);
   void ConsumeDeclarationValue(CSSParserTokenRange,
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.cpp
deleted file mode 100644
index 0a2eaf1..0000000
--- a/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.cpp
+++ /dev/null
@@ -1,20 +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 "core/css/parser/CSSParserTokenStream.h"
-
-namespace blink {
-
-void CSSParserTokenStream::UncheckedConsumeComponentValue() {
-  unsigned nesting_level = 0;
-  do {
-    const CSSParserToken& token = UncheckedConsume();
-    if (token.GetBlockType() == CSSParserToken::kBlockStart)
-      nesting_level++;
-    else if (token.GetBlockType() == CSSParserToken::kBlockEnd)
-      nesting_level--;
-  } while (nesting_level && !AtEnd());
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h b/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h
index fbfb0ad..ba4c05b 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h
+++ b/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h
@@ -6,75 +6,23 @@
 #define CSSParserTokenStream_h
 
 #include "CSSTokenizer.h"
-#include "core/css/parser/CSSParserTokenRange.h"
 
 namespace blink {
 
 // A streaming interface to CSSTokenizer that tokenizes on demand.
-// Methods prefixed with "Unchecked" can only be called after Peek()
-// returns a non-EOF token or after AtEnd() returns false, with no
-// subsequent modifications to the stream such as a consume.
-class CORE_EXPORT CSSParserTokenStream {
+class CSSParserTokenStream {
   DISALLOW_NEW();
 
  public:
   explicit CSSParserTokenStream(CSSTokenizer& tokenizer)
-      : tokenizer_(tokenizer), next_index_(0) {
+      : tokenizer_(tokenizer) {
     DCHECK_EQ(tokenizer.tokens_.size(), 0U);
   }
 
-  // TODO(shend): Remove this method. We should never convert from a range to a
-  // stream. We can remove this once all the functions in CSSParserImpl.h accept
-  // streams.
-  void UpdatePositionFromRange(const CSSParserTokenRange& range) {
-    next_index_ = range.begin() - tokenizer_.tokens_.begin();
-  }
-
-  const CSSParserToken& Peek() const {
-    if (next_index_ == tokenizer_.tokens_.size()) {
-      // Reached end of token buffer, but might not be end of input.
-      if (tokenizer_.TokenizeSingle().IsEOF())
-        return g_static_eof_token;
-    }
-    DCHECK_LT(next_index_, tokenizer_.tokens_.size());
-    return UncheckedPeek();
-  }
-
-  const CSSParserToken& UncheckedPeek() const {
-    DCHECK_LT(next_index_, tokenizer_.tokens_.size());
-    return tokenizer_.tokens_[next_index_];
-  }
-
-  const CSSParserToken& Consume() {
-    const CSSParserToken& token = Peek();
-    if (!token.IsEOF())
-      next_index_++;
-
-    DCHECK_LE(next_index_, tokenizer_.tokens_.size());
-    return token;
-  }
-
-  const CSSParserToken& UncheckedConsume() {
-    DCHECK_LE(next_index_, tokenizer_.tokens_.size());
-    return tokenizer_.tokens_[next_index_++];
-  }
-
-  bool AtEnd() const { return Peek().IsEOF(); }
-
-  // Range represents all tokens from current position to EOF.
-  // Eagerly consumes all the remaining input.
-  // TODO(shend): Remove this method once we switch over to using streams
-  // completely.
-  CSSParserTokenRange MakeRangeToEOF() {
-    return tokenizer_.TokenRange().MakeSubRange(
-        tokenizer_.tokens_.begin() + next_index_, tokenizer_.tokens_.end());
-  }
-
-  void UncheckedConsumeComponentValue();
+  CSSParserTokenRange MakeRangeToEOF() { return tokenizer_.TokenRange(); }
 
  private:
   CSSTokenizer& tokenizer_;
-  size_t next_index_;  // Index of next token to be consumed.
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserTokenStreamTest.cpp b/third_party/WebKit/Source/core/css/parser/CSSParserTokenStreamTest.cpp
deleted file mode 100644
index e675e4d..0000000
--- a/third_party/WebKit/Source/core/css/parser/CSSParserTokenStreamTest.cpp
+++ /dev/null
@@ -1,93 +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 "core/css/parser/CSSParserTokenStream.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-TEST(CSSParserTokenStreamTest, EmptyStream) {
-  CSSTokenizer tokenizer("");
-  CSSParserTokenStream stream(tokenizer);
-  EXPECT_TRUE(stream.Consume().IsEOF());
-  EXPECT_TRUE(stream.Peek().IsEOF());
-  EXPECT_TRUE(stream.AtEnd());
-  EXPECT_TRUE(stream.MakeRangeToEOF().AtEnd());
-}
-
-TEST(CSSParserTokenStreamTest, PeekThenConsume) {
-  CSSTokenizer tokenizer("A");  // kIdent
-  CSSParserTokenStream stream(tokenizer);
-  EXPECT_EQ(kIdentToken, stream.Peek().GetType());
-  EXPECT_EQ(kIdentToken, stream.Consume().GetType());
-  EXPECT_TRUE(stream.AtEnd());
-}
-
-TEST(CSSParserTokenStreamTest, ConsumeThenPeek) {
-  CSSTokenizer tokenizer("A");  // kIdent
-  CSSParserTokenStream stream(tokenizer);
-  EXPECT_EQ(kIdentToken, stream.Consume().GetType());
-  EXPECT_TRUE(stream.AtEnd());
-}
-
-TEST(CSSParserTokenStreamTest, ConsumeMultipleTokens) {
-  CSSTokenizer tokenizer("A 1");  // kIdent kWhitespace kNumber
-  CSSParserTokenStream stream(tokenizer);
-  EXPECT_EQ(kIdentToken, stream.Consume().GetType());
-  EXPECT_EQ(kWhitespaceToken, stream.Consume().GetType());
-  EXPECT_EQ(kNumberToken, stream.Consume().GetType());
-  EXPECT_TRUE(stream.AtEnd());
-}
-
-TEST(CSSParserTokenStreamTest, UncheckedPeekAndConsumeAfterPeek) {
-  CSSTokenizer tokenizer("A");  // kIdent
-  CSSParserTokenStream stream(tokenizer);
-  EXPECT_EQ(kIdentToken, stream.Peek().GetType());
-  EXPECT_EQ(kIdentToken, stream.UncheckedPeek().GetType());
-  EXPECT_EQ(kIdentToken, stream.UncheckedConsume().GetType());
-  EXPECT_TRUE(stream.AtEnd());
-}
-
-TEST(CSSParserTokenStreamTest, UncheckedPeekAndConsumeAfterAtEnd) {
-  CSSTokenizer tokenizer("A");  // kIdent
-  CSSParserTokenStream stream(tokenizer);
-  EXPECT_FALSE(stream.AtEnd());
-  EXPECT_EQ(kIdentToken, stream.UncheckedPeek().GetType());
-  EXPECT_EQ(kIdentToken, stream.UncheckedConsume().GetType());
-  EXPECT_TRUE(stream.AtEnd());
-}
-
-TEST(CSSParserTokenStreamTest, MakeRangeToEOF) {
-  CSSTokenizer tokenizer("A 1");  // kIdent kWhitespace kNumber
-  CSSParserTokenStream stream(tokenizer);
-  EXPECT_EQ(kIdentToken, stream.Consume().GetType());
-
-  auto range = stream.MakeRangeToEOF();
-  EXPECT_FALSE(stream.AtEnd());
-  EXPECT_EQ(kWhitespaceToken, range.Consume().GetType());
-  EXPECT_FALSE(stream.AtEnd());
-  EXPECT_EQ(kNumberToken, range.Consume().GetType());
-  EXPECT_TRUE(range.AtEnd());
-
-  EXPECT_FALSE(stream.AtEnd());
-}
-
-TEST(CSSParserTokenStreamTest, UncheckedConsumeComponentValue) {
-  CSSTokenizer tokenizer("A{1}{2{3}}B");
-  CSSParserTokenStream stream(tokenizer);
-
-  EXPECT_EQ(kIdentToken, stream.Peek().GetType());
-  stream.UncheckedConsumeComponentValue();
-  EXPECT_EQ(kLeftBraceToken, stream.Peek().GetType());
-  stream.UncheckedConsumeComponentValue();
-  EXPECT_EQ(kLeftBraceToken, stream.Peek().GetType());
-  stream.UncheckedConsumeComponentValue();
-  EXPECT_EQ(kIdentToken, stream.Peek().GetType());
-  stream.UncheckedConsumeComponentValue();
-
-  EXPECT_TRUE(stream.AtEnd());
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/Range.cpp b/third_party/WebKit/Source/core/dom/Range.cpp
index 5cfc8ad9..981602e 100644
--- a/third_party/WebKit/Source/core/dom/Range.cpp
+++ b/third_party/WebKit/Source/core/dom/Range.cpp
@@ -1610,27 +1610,32 @@
   return DOMRect::FromFloatRect(BoundingRect());
 }
 
+// https://www.w3.org/TR/cssom-view-1/#dom-range-getclientrects
 void Range::GetBorderAndTextQuads(Vector<FloatQuad>& quads) const {
   Node* start_container = &start_.Container();
   Node* end_container = &end_.Container();
   Node* stop_node = PastLastNode();
 
-  HeapHashSet<Member<Node>> node_set;
+  // Stores the elements selected by the range.
+  HeapHashSet<Member<Node>> selected_elements;
   for (Node* node = FirstNode(); node != stop_node;
        node = NodeTraversal::Next(*node)) {
-    if (node->IsElementNode())
-      node_set.insert(node);
+    if (!node->IsElementNode())
+      continue;
+    if (selected_elements.Contains(node->parentNode()) ||
+        (!node->contains(start_container) && !node->contains(end_container))) {
+      DCHECK_LE(StartPosition(), Position::BeforeNode(*node));
+      DCHECK_GE(EndPosition(), Position::AfterNode(*node));
+      selected_elements.insert(node);
+    }
   }
 
   for (Node* node = FirstNode(); node != stop_node;
        node = NodeTraversal::Next(*node)) {
     if (node->IsElementNode()) {
-      // Exclude start & end container unless the entire corresponding
-      // node is included in the range.
-      if (!node_set.Contains(node->parentNode()) &&
-          (start_container == end_container ||
-           (!node->contains(start_container) &&
-            !node->contains(end_container)))) {
+      // TODO(xiaochengh): Apply early continue style to reduce indentation.
+      if (selected_elements.Contains(node) &&
+          !selected_elements.Contains(node->parentNode())) {
         if (LayoutObject* layout_object = ToElement(node)->GetLayoutObject()) {
           Vector<FloatQuad> element_quads;
           layout_object->AbsoluteQuads(element_quads);
diff --git a/third_party/WebKit/Source/core/dom/RangeTest.cpp b/third_party/WebKit/Source/core/dom/RangeTest.cpp
index 64ca6ff..34d94d7 100644
--- a/third_party/WebKit/Source/core/dom/RangeTest.cpp
+++ b/third_party/WebKit/Source/core/dom/RangeTest.cpp
@@ -296,4 +296,20 @@
   EXPECT_EQ(rect_before, rect_after);
 }
 
+// Regression test for crbug.com/681536
+TEST_F(RangeTest, BorderAndTextQuadsWithInputInBetween) {
+  GetDocument().body()->setInnerHTML("<div>foo <u><input> bar</u></div>");
+  GetDocument().UpdateStyleAndLayout();
+
+  Node* foo = GetDocument().QuerySelector("div")->firstChild();
+  Node* bar = GetDocument().QuerySelector("u")->lastChild();
+  Range* range = Range::Create(GetDocument(), foo, 2, bar, 2);
+
+  Vector<FloatQuad> quads;
+  range->GetBorderAndTextQuads(quads);
+
+  // Should get one quad for "o ", <input> and " b", respectively.
+  ASSERT_EQ(3u, quads.size());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/BUILD.gn b/third_party/WebKit/Source/core/editing/BUILD.gn
index 5434e49d..bbdb5c2 100644
--- a/third_party/WebKit/Source/core/editing/BUILD.gn
+++ b/third_party/WebKit/Source/core/editing/BUILD.gn
@@ -9,10 +9,6 @@
   sources = [
     "CaretDisplayItemClient.cpp",
     "CaretDisplayItemClient.h",
-    "CompositionUnderline.cpp",
-    "CompositionUnderline.h",
-    "CompositionUnderlineVectorBuilder.cpp",
-    "CompositionUnderlineVectorBuilder.h",
     "DOMSelection.cpp",
     "DOMSelection.h",
     "DragCaret.cpp",
@@ -42,6 +38,10 @@
     "FrameSelection.h",
     "GranularityStrategy.cpp",
     "GranularityStrategy.h",
+    "ImeTextSpan.cpp",
+    "ImeTextSpan.h",
+    "ImeTextSpanVectorBuilder.cpp",
+    "ImeTextSpanVectorBuilder.h",
     "InlineBoxTraversal.cpp",
     "InlineBoxTraversal.h",
     "InputMethodController.cpp",
@@ -310,7 +310,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "CompositionUnderlineTest.cpp",
     "EditingCommandTest.cpp",
     "EditingStrategyTest.cpp",
     "EditingStyleTest.cpp",
@@ -322,6 +321,7 @@
     "FrameCaretTest.cpp",
     "FrameSelectionTest.cpp",
     "GranularityStrategyTest.cpp",
+    "ImeTextSpanTest.cpp",
     "InputMethodControllerTest.cpp",
     "LayoutSelectionTest.cpp",
     "PositionIteratorTest.cpp",
diff --git a/third_party/WebKit/Source/core/editing/CompositionUnderline.cpp b/third_party/WebKit/Source/core/editing/CompositionUnderline.cpp
deleted file mode 100644
index 5bd9d7d..0000000
--- a/third_party/WebKit/Source/core/editing/CompositionUnderline.cpp
+++ /dev/null
@@ -1,33 +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 "core/editing/CompositionUnderline.h"
-
-#include <algorithm>
-#include "public/web/WebCompositionUnderline.h"
-
-namespace blink {
-
-CompositionUnderline::CompositionUnderline(unsigned start_offset,
-                                           unsigned end_offset,
-                                           const Color& color,
-                                           bool thick,
-                                           const Color& background_color)
-    : color_(color), thick_(thick), background_color_(background_color) {
-  // Sanitize offsets by ensuring a valid range corresponding to the last
-  // possible position.
-  // TODO(wkorman): Consider replacing with DCHECK_LT(startOffset, endOffset).
-  start_offset_ =
-      std::min(start_offset, std::numeric_limits<unsigned>::max() - 1u);
-  end_offset_ = std::max(start_offset_ + 1u, end_offset);
-}
-
-CompositionUnderline::CompositionUnderline(
-    const WebCompositionUnderline& underline)
-    : CompositionUnderline(underline.start_offset,
-                           underline.end_offset,
-                           Color(underline.color),
-                           underline.thick,
-                           Color(underline.background_color)) {}
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/CompositionUnderlineTest.cpp b/third_party/WebKit/Source/core/editing/CompositionUnderlineTest.cpp
deleted file mode 100644
index ef0a2fb..0000000
--- a/third_party/WebKit/Source/core/editing/CompositionUnderlineTest.cpp
+++ /dev/null
@@ -1,72 +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 "core/editing/CompositionUnderline.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-namespace {
-
-CompositionUnderline CreateCompositionUnderline(unsigned start_offset,
-                                                unsigned end_offset) {
-  return CompositionUnderline(start_offset, end_offset, Color::kTransparent,
-                              false, Color::kTransparent);
-}
-
-TEST(CompositionUnderlineTest, OneChar) {
-  CompositionUnderline underline = CreateCompositionUnderline(0, 1);
-  EXPECT_EQ(0u, underline.StartOffset());
-  EXPECT_EQ(1u, underline.EndOffset());
-}
-
-TEST(CompositionUnderlineTest, MultiChar) {
-  CompositionUnderline underline = CreateCompositionUnderline(0, 5);
-  EXPECT_EQ(0u, underline.StartOffset());
-  EXPECT_EQ(5u, underline.EndOffset());
-}
-
-TEST(CompositionUnderlineTest, ZeroLength) {
-  CompositionUnderline underline = CreateCompositionUnderline(0, 0);
-  EXPECT_EQ(0u, underline.StartOffset());
-  EXPECT_EQ(1u, underline.EndOffset());
-}
-
-TEST(CompositionUnderlineTest, ZeroLengthNonZeroStart) {
-  CompositionUnderline underline = CreateCompositionUnderline(3, 3);
-  EXPECT_EQ(3u, underline.StartOffset());
-  EXPECT_EQ(4u, underline.EndOffset());
-}
-
-TEST(CompositionUnderlineTest, EndBeforeStart) {
-  CompositionUnderline underline = CreateCompositionUnderline(1, 0);
-  EXPECT_EQ(1u, underline.StartOffset());
-  EXPECT_EQ(2u, underline.EndOffset());
-}
-
-TEST(CompositionUnderlineTest, LastChar) {
-  CompositionUnderline underline =
-      CreateCompositionUnderline(std::numeric_limits<unsigned>::max() - 1,
-                                 std::numeric_limits<unsigned>::max());
-  EXPECT_EQ(std::numeric_limits<unsigned>::max() - 1, underline.StartOffset());
-  EXPECT_EQ(std::numeric_limits<unsigned>::max(), underline.EndOffset());
-}
-
-TEST(CompositionUnderlineTest, LastCharEndBeforeStart) {
-  CompositionUnderline underline =
-      CreateCompositionUnderline(std::numeric_limits<unsigned>::max(),
-                                 std::numeric_limits<unsigned>::max() - 1);
-  EXPECT_EQ(std::numeric_limits<unsigned>::max() - 1, underline.StartOffset());
-  EXPECT_EQ(std::numeric_limits<unsigned>::max(), underline.EndOffset());
-}
-
-TEST(CompositionUnderlineTest, LastCharEndBeforeStartZeroEnd) {
-  CompositionUnderline underline =
-      CreateCompositionUnderline(std::numeric_limits<unsigned>::max(), 0);
-  EXPECT_EQ(std::numeric_limits<unsigned>::max() - 1, underline.StartOffset());
-  EXPECT_EQ(std::numeric_limits<unsigned>::max(), underline.EndOffset());
-}
-
-}  // namespace
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/ImeTextSpan.cpp b/third_party/WebKit/Source/core/editing/ImeTextSpan.cpp
new file mode 100644
index 0000000..e337a2e
--- /dev/null
+++ b/third_party/WebKit/Source/core/editing/ImeTextSpan.cpp
@@ -0,0 +1,33 @@
+// 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 "core/editing/ImeTextSpan.h"
+
+#include <algorithm>
+#include "public/web/WebImeTextSpan.h"
+
+namespace blink {
+
+ImeTextSpan::ImeTextSpan(unsigned start_offset,
+                         unsigned end_offset,
+                         const Color& color,
+                         bool thick,
+                         const Color& background_color)
+    : color_(color), thick_(thick), background_color_(background_color) {
+  // Sanitize offsets by ensuring a valid range corresponding to the last
+  // possible position.
+  // TODO(wkorman): Consider replacing with DCHECK_LT(startOffset, endOffset).
+  start_offset_ =
+      std::min(start_offset, std::numeric_limits<unsigned>::max() - 1u);
+  end_offset_ = std::max(start_offset_ + 1u, end_offset);
+}
+
+ImeTextSpan::ImeTextSpan(const WebImeTextSpan& ime_text_span)
+    : ImeTextSpan(ime_text_span.start_offset,
+                  ime_text_span.end_offset,
+                  Color(ime_text_span.color),
+                  ime_text_span.thick,
+                  Color(ime_text_span.background_color)) {}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/CompositionUnderline.h b/third_party/WebKit/Source/core/editing/ImeTextSpan.h
similarity index 81%
rename from third_party/WebKit/Source/core/editing/CompositionUnderline.h
rename to third_party/WebKit/Source/core/editing/ImeTextSpan.h
index cb7d7b4ec..8f50f3b 100644
--- a/third_party/WebKit/Source/core/editing/CompositionUnderline.h
+++ b/third_party/WebKit/Source/core/editing/ImeTextSpan.h
@@ -23,8 +23,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef CompositionUnderline_h
-#define CompositionUnderline_h
+#ifndef ImeTextSpan_h
+#define ImeTextSpan_h
 
 #include "core/CoreExport.h"
 #include "platform/graphics/Color.h"
@@ -32,19 +32,19 @@
 
 namespace blink {
 
-struct WebCompositionUnderline;
+struct WebImeTextSpan;
 
-class CORE_EXPORT CompositionUnderline {
+class CORE_EXPORT ImeTextSpan {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
 
  public:
-  CompositionUnderline(unsigned start_offset,
-                       unsigned end_offset,
-                       const Color&,
-                       bool thick,
-                       const Color& background_color);
+  ImeTextSpan(unsigned start_offset,
+              unsigned end_offset,
+              const Color&,
+              bool thick,
+              const Color& background_color);
 
-  CompositionUnderline(const WebCompositionUnderline&);
+  ImeTextSpan(const WebImeTextSpan&);
 
   unsigned StartOffset() const { return start_offset_; }
   unsigned EndOffset() const { return end_offset_; }
@@ -62,4 +62,4 @@
 
 }  // namespace blink
 
-#endif  // CompositionUnderline_h
+#endif  // ImeTextSpan_h
diff --git a/third_party/WebKit/Source/core/editing/ImeTextSpanTest.cpp b/third_party/WebKit/Source/core/editing/ImeTextSpanTest.cpp
new file mode 100644
index 0000000..858b055
--- /dev/null
+++ b/third_party/WebKit/Source/core/editing/ImeTextSpanTest.cpp
@@ -0,0 +1,74 @@
+// 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 "core/editing/ImeTextSpan.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+ImeTextSpan CreateImeTextSpan(unsigned start_offset, unsigned end_offset) {
+  return ImeTextSpan(start_offset, end_offset, Color::kTransparent, false,
+                     Color::kTransparent);
+}
+
+TEST(ImeTextSpanTest, OneChar) {
+  ImeTextSpan ime_text_span = CreateImeTextSpan(0, 1);
+  EXPECT_EQ(0u, ime_text_span.StartOffset());
+  EXPECT_EQ(1u, ime_text_span.EndOffset());
+}
+
+TEST(ImeTextSpanTest, MultiChar) {
+  ImeTextSpan ime_text_span = CreateImeTextSpan(0, 5);
+  EXPECT_EQ(0u, ime_text_span.StartOffset());
+  EXPECT_EQ(5u, ime_text_span.EndOffset());
+}
+
+TEST(ImeTextSpanTest, ZeroLength) {
+  ImeTextSpan ime_text_span = CreateImeTextSpan(0, 0);
+  EXPECT_EQ(0u, ime_text_span.StartOffset());
+  EXPECT_EQ(1u, ime_text_span.EndOffset());
+}
+
+TEST(ImeTextSpanTest, ZeroLengthNonZeroStart) {
+  ImeTextSpan ime_text_span = CreateImeTextSpan(3, 3);
+  EXPECT_EQ(3u, ime_text_span.StartOffset());
+  EXPECT_EQ(4u, ime_text_span.EndOffset());
+}
+
+TEST(ImeTextSpanTest, EndBeforeStart) {
+  ImeTextSpan ime_text_span = CreateImeTextSpan(1, 0);
+  EXPECT_EQ(1u, ime_text_span.StartOffset());
+  EXPECT_EQ(2u, ime_text_span.EndOffset());
+}
+
+TEST(ImeTextSpanTest, LastChar) {
+  ImeTextSpan ime_text_span =
+      CreateImeTextSpan(std::numeric_limits<unsigned>::max() - 1,
+                        std::numeric_limits<unsigned>::max());
+  EXPECT_EQ(std::numeric_limits<unsigned>::max() - 1,
+            ime_text_span.StartOffset());
+  EXPECT_EQ(std::numeric_limits<unsigned>::max(), ime_text_span.EndOffset());
+}
+
+TEST(ImeTextSpanTest, LastCharEndBeforeStart) {
+  ImeTextSpan ime_text_span =
+      CreateImeTextSpan(std::numeric_limits<unsigned>::max(),
+                        std::numeric_limits<unsigned>::max() - 1);
+  EXPECT_EQ(std::numeric_limits<unsigned>::max() - 1,
+            ime_text_span.StartOffset());
+  EXPECT_EQ(std::numeric_limits<unsigned>::max(), ime_text_span.EndOffset());
+}
+
+TEST(ImeTextSpanTest, LastCharEndBeforeStartZeroEnd) {
+  ImeTextSpan ime_text_span =
+      CreateImeTextSpan(std::numeric_limits<unsigned>::max(), 0);
+  EXPECT_EQ(std::numeric_limits<unsigned>::max() - 1,
+            ime_text_span.StartOffset());
+  EXPECT_EQ(std::numeric_limits<unsigned>::max(), ime_text_span.EndOffset());
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/CompositionUnderlineVectorBuilder.cpp b/third_party/WebKit/Source/core/editing/ImeTextSpanVectorBuilder.cpp
similarity index 84%
rename from third_party/WebKit/Source/core/editing/CompositionUnderlineVectorBuilder.cpp
rename to third_party/WebKit/Source/core/editing/ImeTextSpanVectorBuilder.cpp
index cccfebbb..d312a263 100644
--- a/third_party/WebKit/Source/core/editing/CompositionUnderlineVectorBuilder.cpp
+++ b/third_party/WebKit/Source/core/editing/ImeTextSpanVectorBuilder.cpp
@@ -28,17 +28,17 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "core/editing/CompositionUnderlineVectorBuilder.h"
+#include "core/editing/ImeTextSpanVectorBuilder.h"
 
 namespace blink {
 
-Vector<CompositionUnderline> CompositionUnderlineVectorBuilder::Build(
-    const WebVector<WebCompositionUnderline>& underlines) {
-  Vector<CompositionUnderline> result;
-  size_t size = underlines.size();
+Vector<ImeTextSpan> ImeTextSpanVectorBuilder::Build(
+    const WebVector<WebImeTextSpan>& ime_text_spans) {
+  Vector<ImeTextSpan> result;
+  size_t size = ime_text_spans.size();
   result.ReserveCapacity(size);
   for (size_t i = 0; i < size; ++i)
-    result.push_back(underlines[i]);
+    result.push_back(ime_text_spans[i]);
   return result;
 }
 
diff --git a/third_party/WebKit/Source/core/editing/CompositionUnderlineVectorBuilder.h b/third_party/WebKit/Source/core/editing/ImeTextSpanVectorBuilder.h
similarity index 77%
rename from third_party/WebKit/Source/core/editing/CompositionUnderlineVectorBuilder.h
rename to third_party/WebKit/Source/core/editing/ImeTextSpanVectorBuilder.h
index 9b44790..fc658ec 100644
--- a/third_party/WebKit/Source/core/editing/CompositionUnderlineVectorBuilder.h
+++ b/third_party/WebKit/Source/core/editing/ImeTextSpanVectorBuilder.h
@@ -28,26 +28,26 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef CompositionUnderlineVectorBuilder_h
-#define CompositionUnderlineVectorBuilder_h
+#ifndef ImeTextSpanVectorBuilder_h
+#define ImeTextSpanVectorBuilder_h
 
 #include "core/CoreExport.h"
-#include "core/editing/CompositionUnderline.h"
+#include "core/editing/ImeTextSpan.h"
 #include "platform/wtf/Vector.h"
 #include "public/platform/WebVector.h"
-#include "public/web/WebCompositionUnderline.h"
+#include "public/web/WebImeTextSpan.h"
 
 namespace blink {
 
-// This class is used for converting from WebVector<WebCompositionUnderline>
-// to Vector<CompositionUnderline>.
+// This class is used for converting from WebVector<WebImeTextSpan>
+// to Vector<ImeTextSpan>.
 
-class CompositionUnderlineVectorBuilder {
-  STATIC_ONLY(CompositionUnderlineVectorBuilder);
+class ImeTextSpanVectorBuilder {
+  STATIC_ONLY(ImeTextSpanVectorBuilder);
 
  public:
-  CORE_EXPORT static Vector<CompositionUnderline> Build(
-      const WebVector<WebCompositionUnderline>&);
+  CORE_EXPORT static Vector<ImeTextSpan> Build(
+      const WebVector<WebImeTextSpan>&);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/InputMethodController.cpp b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
index 853f3d59..c78c81c 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
@@ -432,14 +432,14 @@
 
 bool InputMethodController::CommitText(
     const String& text,
-    const Vector<CompositionUnderline>& underlines,
+    const Vector<ImeTextSpan>& ime_text_spans,
     int relative_caret_position) {
   if (HasComposition()) {
     return ReplaceCompositionAndMoveCaret(text, relative_caret_position,
-                                          underlines);
+                                          ime_text_spans);
   }
 
-  return InsertTextAndMoveCaret(text, relative_caret_position, underlines);
+  return InsertTextAndMoveCaret(text, relative_caret_position, ime_text_spans);
 }
 
 bool InputMethodController::ReplaceComposition(const String& text) {
@@ -480,32 +480,34 @@
   return text_start + text_length + relative_caret_position;
 }
 
-void InputMethodController::AddCompositionUnderlines(
-    const Vector<CompositionUnderline>& underlines,
+void InputMethodController::AddImeTextSpans(
+    const Vector<ImeTextSpan>& ime_text_spans,
     ContainerNode* base_element,
     unsigned offset_in_plain_chars) {
-  for (const auto& underline : underlines) {
-    unsigned underline_start = offset_in_plain_chars + underline.StartOffset();
-    unsigned underline_end = offset_in_plain_chars + underline.EndOffset();
+  for (const auto& ime_text_span : ime_text_spans) {
+    unsigned ime_text_span_start =
+        offset_in_plain_chars + ime_text_span.StartOffset();
+    unsigned ime_text_span_end =
+        offset_in_plain_chars + ime_text_span.EndOffset();
 
     EphemeralRange ephemeral_line_range =
-        PlainTextRange(underline_start, underline_end)
+        PlainTextRange(ime_text_span_start, ime_text_span_end)
             .CreateRange(*base_element);
     if (ephemeral_line_range.IsNull())
       continue;
 
     GetDocument().Markers().AddCompositionMarker(
-        ephemeral_line_range, underline.GetColor(),
-        underline.Thick() ? StyleableMarker::Thickness::kThick
-                          : StyleableMarker::Thickness::kThin,
-        underline.BackgroundColor());
+        ephemeral_line_range, ime_text_span.GetColor(),
+        ime_text_span.Thick() ? StyleableMarker::Thickness::kThick
+                              : StyleableMarker::Thickness::kThin,
+        ime_text_span.BackgroundColor());
   }
 }
 
 bool InputMethodController::ReplaceCompositionAndMoveCaret(
     const String& text,
     int relative_caret_position,
-    const Vector<CompositionUnderline>& underlines) {
+    const Vector<ImeTextSpan>& ime_text_spans) {
   Element* root_editable_element =
       GetFrame()
           .Selection()
@@ -527,7 +529,7 @@
   // needs to be audited. see http://crbug.com/590369 for more details.
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
 
-  AddCompositionUnderlines(underlines, root_editable_element, text_start);
+  AddImeTextSpans(ime_text_spans, root_editable_element, text_start);
 
   int absolute_caret_position = ComputeAbsoluteCaretPosition(
       text_start, text.length(), relative_caret_position);
@@ -545,7 +547,7 @@
 bool InputMethodController::InsertTextAndMoveCaret(
     const String& text,
     int relative_caret_position,
-    const Vector<CompositionUnderline>& underlines) {
+    const Vector<ImeTextSpan>& ime_text_spans) {
   PlainTextRange selection_range = GetSelectionOffsets();
   if (selection_range.IsNull())
     return false;
@@ -559,7 +561,7 @@
           .ComputeVisibleSelectionInDOMTreeDeprecated()
           .RootEditableElement();
   if (root_editable_element) {
-    AddCompositionUnderlines(underlines, root_editable_element, text_start);
+    AddImeTextSpans(ime_text_spans, root_editable_element, text_start);
   }
 
   int absolute_caret_position = ComputeAbsoluteCaretPosition(
@@ -598,7 +600,7 @@
 
 void InputMethodController::SetComposition(
     const String& text,
-    const Vector<CompositionUnderline>& underlines,
+    const Vector<ImeTextSpan>& ime_text_spans,
     int selection_start,
     int selection_end) {
   Editor::RevealSelectionScope reveal_selection_scope(&GetEditor());
@@ -719,7 +721,7 @@
   // We shouldn't close typing in the middle of setComposition.
   SetEditableSelectionOffsets(selected_range, TypingContinuation::kContinue);
 
-  if (underlines.IsEmpty()) {
+  if (ime_text_spans.IsEmpty()) {
     GetDocument().Markers().AddCompositionMarker(
         EphemeralRange(composition_range_), Color::kBlack,
         StyleableMarker::Thickness::kThin,
@@ -729,8 +731,8 @@
 
   const PlainTextRange composition_plain_text_range =
       PlainTextRange::Create(*base_node->parentNode(), *composition_range_);
-  AddCompositionUnderlines(underlines, base_node->parentNode(),
-                           composition_plain_text_range.Start());
+  AddImeTextSpans(ime_text_spans, base_node->parentNode(),
+                  composition_plain_text_range.Start());
 }
 
 PlainTextRange InputMethodController::CreateSelectionRangeForSetComposition(
@@ -745,7 +747,7 @@
 }
 
 void InputMethodController::SetCompositionFromExistingText(
-    const Vector<CompositionUnderline>& underlines,
+    const Vector<ImeTextSpan>& ime_text_spans,
     unsigned composition_start,
     unsigned composition_end) {
   Element* editable = GetFrame()
@@ -772,7 +774,7 @@
 
   Clear();
 
-  AddCompositionUnderlines(underlines, editable, composition_start);
+  AddImeTextSpans(ime_text_spans, editable, composition_start);
 
   has_composition_ = true;
   if (!composition_range_)
diff --git a/third_party/WebKit/Source/core/editing/InputMethodController.h b/third_party/WebKit/Source/core/editing/InputMethodController.h
index 88f1f25..40e62b2c 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodController.h
+++ b/third_party/WebKit/Source/core/editing/InputMethodController.h
@@ -29,8 +29,8 @@
 #include "base/gtest_prod_util.h"
 #include "core/CoreExport.h"
 #include "core/dom/SynchronousMutationObserver.h"
-#include "core/editing/CompositionUnderline.h"
 #include "core/editing/EphemeralRange.h"
+#include "core/editing/ImeTextSpan.h"
 #include "core/editing/PlainTextRange.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Vector.h"
@@ -63,10 +63,10 @@
   // international text input composition
   bool HasComposition() const;
   void SetComposition(const String& text,
-                      const Vector<CompositionUnderline>& underlines,
+                      const Vector<ImeTextSpan>& ime_text_spans,
                       int selection_start,
                       int selection_end);
-  void SetCompositionFromExistingText(const Vector<CompositionUnderline>& text,
+  void SetCompositionFromExistingText(const Vector<ImeTextSpan>& ime_text_spans,
                                       unsigned composition_start,
                                       unsigned composition_end);
 
@@ -74,7 +74,7 @@
   // changes the selection according to relativeCaretPosition, which is
   // relative to the end of the inserting text.
   bool CommitText(const String& text,
-                  const Vector<CompositionUnderline>& underlines,
+                  const Vector<ImeTextSpan>& ime_text_spans,
                   int relative_caret_position);
 
   // Inserts ongoing composing text; changes the selection to the end of
@@ -134,14 +134,14 @@
   // Returns true if selection offsets were successfully set.
   bool SetSelectionOffsets(const PlainTextRange&);
 
-  void AddCompositionUnderlines(const Vector<CompositionUnderline>& underlines,
-                                ContainerNode* base_element,
-                                unsigned offset_in_plain_chars);
+  void AddImeTextSpans(const Vector<ImeTextSpan>& ime_text_spans,
+                       ContainerNode* base_element,
+                       unsigned offset_in_plain_chars);
 
   bool InsertText(const String&);
   bool InsertTextAndMoveCaret(const String&,
                               int relative_caret_position,
-                              const Vector<CompositionUnderline>& underlines);
+                              const Vector<ImeTextSpan>& ime_text_spans);
 
   // Inserts the given text string in the place of the existing composition.
   // Returns true if did replace.
@@ -151,7 +151,7 @@
   bool ReplaceCompositionAndMoveCaret(
       const String&,
       int relative_caret_position,
-      const Vector<CompositionUnderline>& underlines);
+      const Vector<ImeTextSpan>& ime_text_spans);
 
   // Returns true if moved caret successfully.
   bool MoveCaret(int new_caret_position);
diff --git a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
index c368cc3..f9b5775 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
@@ -171,9 +171,9 @@
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>hello world</div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 0, 5);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 0, 5);
 
   Range* range = Controller().CompositionRange();
   EXPECT_EQ(0u, range->startOffset());
@@ -189,8 +189,8 @@
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>&#x1f3c6</div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 2, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 2, Color(255, 0, 0), false, 0));
 
   GetDocument().UpdateStyleAndLayout();
   Controller().SetEditableSelectionOffsets(PlainTextRange(2, 2));
@@ -205,25 +205,25 @@
                    .Extent()
                    .ComputeOffsetInContainerNode());
 
-  Controller().SetComposition(String("a"), underlines, 1, 1);
+  Controller().SetComposition(String("a"), ime_text_spans, 1, 1);
   EXPECT_STREQ("\xF0\x9F\x8F\x86\x61", div->innerText().Utf8().data());
 
-  Controller().SetComposition(String("ab"), underlines, 2, 2);
+  Controller().SetComposition(String("ab"), ime_text_spans, 2, 2);
   EXPECT_STREQ("\xF0\x9F\x8F\x86\x61\x62", div->innerText().Utf8().data());
 }
 
 TEST_F(InputMethodControllerTest, SetCompositionWithGraphemeCluster) {
   InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(6, 6, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(6, 6, Color(255, 0, 0), false, 0));
   GetDocument().UpdateStyleAndLayout();
 
   // UTF16 = 0x0939 0x0947 0x0932 0x0932. Note that 0x0932 0x0932 is a grapheme
   // cluster.
   Controller().SetComposition(
       String::FromUTF8("\xE0\xA4\xB9\xE0\xA5\x87\xE0\xA4\xB2\xE0\xA4\xB2"),
-      underlines, 4, 4);
+      ime_text_spans, 4, 4);
   EXPECT_EQ(4u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(4u, Controller().GetSelectionOffsets().End());
 
@@ -231,7 +231,7 @@
   Controller().SetComposition(
       String::FromUTF8("\xE0\xA4\xB9\xE0\xA5\x87\xE0\xA4\xB2\xE0\xA5\x8D\xE0"
                        "\xA4\xB2\xE0\xA5\x8B"),
-      underlines, 6, 6);
+      ime_text_spans, 6, 6);
   EXPECT_EQ(6u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(6u, Controller().GetSelectionOffsets().End());
 }
@@ -241,9 +241,8 @@
   Element* div =
       InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(
-      CompositionUnderline(12, 12, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(12, 12, Color(255, 0, 0), false, 0));
   GetDocument().UpdateStyleAndLayout();
 
   // UTF16 = 0x0939 0x0947 0x0932 0x094D 0x0932 0x094B. 0x0939 0x0947 0x0932 is
@@ -251,9 +250,9 @@
   Controller().CommitText(
       String::FromUTF8("\xE0\xA4\xB9\xE0\xA5\x87\xE0\xA4\xB2\xE0\xA5\x8D\xE0"
                        "\xA4\xB2\xE0\xA5\x8B"),
-      underlines, 1);
-  Controller().CommitText("\nab ", underlines, 1);
-  Controller().SetComposition(String("c"), underlines, 1, 1);
+      ime_text_spans, 1);
+  Controller().CommitText("\nab ", ime_text_spans, 1);
+  Controller().SetComposition(String("c"), ime_text_spans, 1, 1);
   EXPECT_STREQ(
       "\xE0\xA4\xB9\xE0\xA5\x87\xE0\xA4\xB2\xE0\xA5\x8D\xE0\xA4\xB2\xE0\xA5"
       "\x8B\nab c",
@@ -261,7 +260,7 @@
   EXPECT_EQ(11u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(11u, Controller().GetSelectionOffsets().End());
 
-  Controller().SetComposition(String("cd"), underlines, 2, 2);
+  Controller().SetComposition(String("cd"), ime_text_spans, 2, 2);
   EXPECT_STREQ(
       "\xE0\xA4\xB9\xE0\xA5\x87\xE0\xA4\xB2\xE0\xA5\x8D\xE0\xA4\xB2\xE0\xA5"
       "\x8B\nab cd",
@@ -276,26 +275,26 @@
       "contenteditable>abc1<b>2</b>34567<b>8</b>9d<b>e</b>f</div>",
       "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(3, 12, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 3, 12);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(3, 12, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 3, 12);
 
   // Subtract a character.
-  Controller().SetComposition(String("12345789"), underlines, 8, 8);
+  Controller().SetComposition(String("12345789"), ime_text_spans, 8, 8);
   EXPECT_STREQ("abc1<b>2</b>3457<b>8</b>9d<b>e</b>f",
                div->innerHTML().Utf8().data());
   EXPECT_EQ(11u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(11u, Controller().GetSelectionOffsets().End());
 
   // Append a character.
-  Controller().SetComposition(String("123456789"), underlines, 9, 9);
+  Controller().SetComposition(String("123456789"), ime_text_spans, 9, 9);
   EXPECT_STREQ("abc1<b>2</b>34567<b>8</b>9d<b>e</b>f",
                div->innerHTML().Utf8().data());
   EXPECT_EQ(12u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(12u, Controller().GetSelectionOffsets().End());
 
   // Subtract and append characters.
-  Controller().SetComposition(String("123hello789"), underlines, 11, 11);
+  Controller().SetComposition(String("123hello789"), ime_text_spans, 11, 11);
   EXPECT_STREQ("abc1<b>2</b>3hello7<b>8</b>9d<b>e</b>f",
                div->innerHTML().Utf8().data());
 }
@@ -305,19 +304,19 @@
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable><b>&#x1f3e0</b></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 2, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 2, Color(255, 0, 0), false, 0));
 
-  Controller().SetCompositionFromExistingText(underlines, 0, 2);
+  Controller().SetCompositionFromExistingText(ime_text_spans, 0, 2);
 
   // 0xF0 0x9F 0x8F 0xAB is also an emoji character, with the same leading
   // surrogate pair to the previous one.
-  Controller().SetComposition(String::FromUTF8("\xF0\x9F\x8F\xAB"), underlines,
-                              2, 2);
+  Controller().SetComposition(String::FromUTF8("\xF0\x9F\x8F\xAB"),
+                              ime_text_spans, 2, 2);
   EXPECT_STREQ("<b>\xF0\x9F\x8F\xAB</b>", div->innerHTML().Utf8().data());
 
-  Controller().SetComposition(String::FromUTF8("\xF0\x9F\x8F\xA0"), underlines,
-                              2, 2);
+  Controller().SetComposition(String::FromUTF8("\xF0\x9F\x8F\xA0"),
+                              ime_text_spans, 2, 2);
   EXPECT_STREQ("<b>\xF0\x9F\x8F\xA0</b>", div->innerHTML().Utf8().data());
 }
 
@@ -329,19 +328,19 @@
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable><b>&#xc03</b></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 2, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 0, 1);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 2, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 0, 1);
 
   // 0xE0 0xB0 0x83 0xE0 0xB0 0x83, a telugu character with 2 code points in
   // 1 grapheme cluster.
   Controller().SetComposition(String::FromUTF8("\xE0\xB0\x83\xE0\xB0\x83"),
-                              underlines, 2, 2);
+                              ime_text_spans, 2, 2);
   EXPECT_STREQ("<b>\xE0\xB0\x83\xE0\xB0\x83</b>",
                div->innerHTML().Utf8().data());
 
-  Controller().SetComposition(String::FromUTF8("\xE0\xB0\x83"), underlines, 1,
-                              1);
+  Controller().SetComposition(String::FromUTF8("\xE0\xB0\x83"), ime_text_spans,
+                              1, 1);
   EXPECT_STREQ("<b>\xE0\xB0\x83</b>", div->innerHTML().Utf8().data());
 }
 
@@ -351,11 +350,11 @@
       "contenteditable>abc1<b>2</b>34567<b>8</b>9</div>",
       "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(3, 12, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 3, 12);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(3, 12, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 3, 12);
 
-  Controller().SetComposition(String("123hello789"), underlines, 11, 11);
+  Controller().SetComposition(String("123hello789"), ime_text_spans, 11, 11);
   EXPECT_STREQ("abc1<b>2</b>3hello7<b>8</b>9", div->innerHTML().Utf8().data());
 
   Controller().FinishComposingText(InputMethodController::kKeepSelection);
@@ -368,21 +367,21 @@
       "contenteditable>abc1<b>2</b>34567<b>8</b>9</div>",
       "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(3, 12, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 3, 12);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(3, 12, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 3, 12);
 
-  Controller().CommitText(String("123789"), underlines, 0);
+  Controller().CommitText(String("123789"), ime_text_spans, 0);
   EXPECT_STREQ("abc1<b>2</b>37<b>8</b>9", div->innerHTML().Utf8().data());
 }
 
 TEST_F(InputMethodControllerTest, InsertTextWithNewLine) {
   Element* div =
       InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 11, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 11, Color(255, 0, 0), false, 0));
 
-  Controller().CommitText(String("hello\nworld"), underlines, 0);
+  Controller().CommitText(String("hello\nworld"), ime_text_spans, 0);
   EXPECT_STREQ("hello<div>world</div>", div->innerHTML().Utf8().data());
 }
 
@@ -390,12 +389,12 @@
   Element* div =
       InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  Controller().CommitText("a", underlines, 0);
-  Controller().SetComposition("bcd", underlines, 0, 2);
+  Vector<ImeTextSpan> ime_text_spans;
+  Controller().CommitText("a", ime_text_spans, 0);
+  Controller().SetComposition("bcd", ime_text_spans, 0, 2);
   EXPECT_STREQ("abcd", div->innerHTML().Utf8().data());
 
-  Controller().CommitText(String("bcd\nefgh\nijkl"), underlines, 0);
+  Controller().CommitText(String("bcd\nefgh\nijkl"), ime_text_spans, 0);
   EXPECT_STREQ("abcd<div>efgh</div><div>ijkl</div>",
                div->innerHTML().Utf8().data());
 }
@@ -404,9 +403,9 @@
   InsertHTMLElement("<div id='sample' contenteditable>hello world</div>",
                     "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 0, 5);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 0, 5);
 
   Controller().FinishComposingText(InputMethodController::kKeepSelection);
   EXPECT_EQ(0, GetFrame()
@@ -439,11 +438,11 @@
   Controller().ExtendSelectionAndDelete(1, 0);
   EXPECT_STREQ("foo", input->value().Utf8().data());
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 3, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 0, 3);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 3, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 0, 3);
 
-  Controller().SetComposition(String(""), underlines, 0, 3);
+  Controller().SetComposition(String(""), ime_text_spans, 0, 3);
 
   EXPECT_STREQ("", input->value().Utf8().data());
 }
@@ -455,9 +454,9 @@
   Element* div = InsertHTMLElement(
       "<div id='sample' contenteditable>\nhello world</div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 0, 5);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 0, 5);
 
   Range* range = Controller().CompositionRange();
   EXPECT_EQ(1u, range->startOffset());
@@ -472,9 +471,9 @@
        SetCompositionFromExistingTextWithInvalidOffsets) {
   InsertHTMLElement("<div id='sample' contenteditable>test</div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(7, 8, Color(255, 0, 0), false, 0));
-  Controller().SetCompositionFromExistingText(underlines, 7, 8);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(7, 8, Color(255, 0, 0), false, 0));
+  Controller().SetCompositionFromExistingText(ime_text_spans, 7, 8);
 
   EXPECT_FALSE(Controller().CompositionRange());
 }
@@ -483,9 +482,9 @@
   HTMLInputElement* input = toHTMLInputElement(InsertHTMLElement(
       "<input id='sample' type='password' size='24'>", "sample"));
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
-  Controller().SetComposition("foo", underlines, 0, 3);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
+  Controller().SetComposition("foo", ime_text_spans, 0, 3);
   Controller().FinishComposingText(InputMethodController::kKeepSelection);
 
   EXPECT_STREQ("foo", input->value().Utf8().data());
@@ -936,47 +935,47 @@
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().End());
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 2, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 2, Color(255, 0, 0), false, 0));
 
   // The caret exceeds left boundary.
   // "*heABllo", where * stands for caret.
-  Controller().SetComposition("AB", underlines, -100, -100);
+  Controller().SetComposition("AB", ime_text_spans, -100, -100);
   EXPECT_STREQ("heABllo", input->value().Utf8().data());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().End());
 
   // The caret is on left boundary.
   // "*heABllo".
-  Controller().SetComposition("AB", underlines, -2, -2);
+  Controller().SetComposition("AB", ime_text_spans, -2, -2);
   EXPECT_STREQ("heABllo", input->value().Utf8().data());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().End());
 
   // The caret is before the composing text.
   // "he*ABllo".
-  Controller().SetComposition("AB", underlines, 0, 0);
+  Controller().SetComposition("AB", ime_text_spans, 0, 0);
   EXPECT_STREQ("heABllo", input->value().Utf8().data());
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().End());
 
   // The caret is after the composing text.
   // "heAB*llo".
-  Controller().SetComposition("AB", underlines, 2, 2);
+  Controller().SetComposition("AB", ime_text_spans, 2, 2);
   EXPECT_STREQ("heABllo", input->value().Utf8().data());
   EXPECT_EQ(4u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(4u, Controller().GetSelectionOffsets().End());
 
   // The caret is on right boundary.
   // "heABllo*".
-  Controller().SetComposition("AB", underlines, 5, 5);
+  Controller().SetComposition("AB", ime_text_spans, 5, 5);
   EXPECT_STREQ("heABllo", input->value().Utf8().data());
   EXPECT_EQ(7u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(7u, Controller().GetSelectionOffsets().End());
 
   // The caret exceeds right boundary.
   // "heABllo*".
-  Controller().SetComposition("AB", underlines, 100, 100);
+  Controller().SetComposition("AB", ime_text_spans, 100, 100);
   EXPECT_STREQ("heABllo", input->value().Utf8().data());
   EXPECT_EQ(7u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(7u, Controller().GetSelectionOffsets().End());
@@ -1000,82 +999,82 @@
   EXPECT_EQ(17u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(17u, Controller().GetSelectionOffsets().End());
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 2, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 2, Color(255, 0, 0), false, 0));
 
   // The caret exceeds left boundary.
   // "*hello\nworld\n01234AB56789", where * stands for caret.
-  Controller().SetComposition("AB", underlines, -100, -100);
+  Controller().SetComposition("AB", ime_text_spans, -100, -100);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().End());
 
   // The caret is on left boundary.
   // "*hello\nworld\n01234AB56789".
-  Controller().SetComposition("AB", underlines, -17, -17);
+  Controller().SetComposition("AB", ime_text_spans, -17, -17);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(0u, Controller().GetSelectionOffsets().End());
 
   // The caret is in the 1st node.
   // "he*llo\nworld\n01234AB56789".
-  Controller().SetComposition("AB", underlines, -15, -15);
+  Controller().SetComposition("AB", ime_text_spans, -15, -15);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().End());
 
   // The caret is on right boundary of the 1st node.
   // "hello*\nworld\n01234AB56789".
-  Controller().SetComposition("AB", underlines, -12, -12);
+  Controller().SetComposition("AB", ime_text_spans, -12, -12);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(5u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(5u, Controller().GetSelectionOffsets().End());
 
   // The caret is on right boundary of the 2nd node.
   // "hello\n*world\n01234AB56789".
-  Controller().SetComposition("AB", underlines, -11, -11);
+  Controller().SetComposition("AB", ime_text_spans, -11, -11);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(6u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(6u, Controller().GetSelectionOffsets().End());
 
   // The caret is on right boundary of the 3rd node.
   // "hello\nworld*\n01234AB56789".
-  Controller().SetComposition("AB", underlines, -6, -6);
+  Controller().SetComposition("AB", ime_text_spans, -6, -6);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(11u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(11u, Controller().GetSelectionOffsets().End());
 
   // The caret is on right boundary of the 4th node.
   // "hello\nworld\n*01234AB56789".
-  Controller().SetComposition("AB", underlines, -5, -5);
+  Controller().SetComposition("AB", ime_text_spans, -5, -5);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(12u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(12u, Controller().GetSelectionOffsets().End());
 
   // The caret is before the composing text.
   // "hello\nworld\n01234*AB56789".
-  Controller().SetComposition("AB", underlines, 0, 0);
+  Controller().SetComposition("AB", ime_text_spans, 0, 0);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(17u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(17u, Controller().GetSelectionOffsets().End());
 
   // The caret is after the composing text.
   // "hello\nworld\n01234AB*56789".
-  Controller().SetComposition("AB", underlines, 2, 2);
+  Controller().SetComposition("AB", ime_text_spans, 2, 2);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(19u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(19u, Controller().GetSelectionOffsets().End());
 
   // The caret is on right boundary.
   // "hello\nworld\n01234AB56789*".
-  Controller().SetComposition("AB", underlines, 7, 7);
+  Controller().SetComposition("AB", ime_text_spans, 7, 7);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(24u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(24u, Controller().GetSelectionOffsets().End());
 
   // The caret exceeds right boundary.
   // "hello\nworld\n01234AB56789*".
-  Controller().SetComposition("AB", underlines, 100, 100);
+  Controller().SetComposition("AB", ime_text_spans, 100, 100);
   EXPECT_STREQ("hello\nworld\n01234AB56789", div->innerText().Utf8().data());
   EXPECT_EQ(24u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(24u, Controller().GetSelectionOffsets().End());
@@ -1090,20 +1089,20 @@
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(2u, Controller().GetSelectionOffsets().End());
 
-  Vector<CompositionUnderline> underlines0;
-  underlines0.push_back(CompositionUnderline(0, 0, Color(255, 0, 0), false, 0));
-  Vector<CompositionUnderline> underlines2;
-  underlines2.push_back(CompositionUnderline(0, 2, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans0;
+  ime_text_spans0.push_back(ImeTextSpan(0, 0, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans2;
+  ime_text_spans2.push_back(ImeTextSpan(0, 2, Color(255, 0, 0), false, 0));
 
-  Controller().SetComposition("AB", underlines2, 2, 2);
+  Controller().SetComposition("AB", ime_text_spans2, 2, 2);
   // With previous composition.
-  Controller().SetComposition("", underlines0, 2, 2);
+  Controller().SetComposition("", ime_text_spans0, 2, 2);
   EXPECT_STREQ("hello", div->innerText().Utf8().data());
   EXPECT_EQ(4u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(4u, Controller().GetSelectionOffsets().End());
 
   // Without previous composition.
-  Controller().SetComposition("", underlines0, -1, -1);
+  Controller().SetComposition("", ime_text_spans0, -1, -1);
   EXPECT_STREQ("hello", div->innerText().Utf8().data());
   EXPECT_EQ(3u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(3u, Controller().GetSelectionOffsets().End());
@@ -1113,9 +1112,9 @@
   Element* div =
       InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
-  Controller().SetComposition("hello", underlines, 5, 5);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
+  Controller().SetComposition("hello", ime_text_spans, 5, 5);
   EXPECT_STREQ("hello", div->innerText().Utf8().data());
   EXPECT_EQ(5u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(5u, Controller().GetSelectionOffsets().End());
@@ -1130,9 +1129,9 @@
   Element* div =
       InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 2, Color(255, 0, 0), false, 0));
-  Controller().CommitText("hello", underlines, 0);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 2, Color(255, 0, 0), false, 0));
+  Controller().CommitText("hello", ime_text_spans, 0);
   EXPECT_STREQ("hello", div->innerText().Utf8().data());
 
   Controller().SetEditableSelectionOffsets(PlainTextRange(2, 2));
@@ -1161,17 +1160,17 @@
   GetDocument().View()->UpdateAllLifecyclePhases();
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
   editable->focus();
 
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("foo", underlines, 0, 3);
+  Controller().SetComposition("foo", ime_text_spans, 0, 3);
   EXPECT_STREQ("beforeinput.isComposing:true;input.isComposing:true;",
                GetDocument().title().Utf8().data());
 
   GetDocument().setTitle(g_empty_string);
-  Controller().CommitText("bar", underlines, 0);
+  Controller().CommitText("bar", ime_text_spans, 0);
   // Last pair of InputEvent should also be inside composition scope.
   EXPECT_STREQ("beforeinput.isComposing:true;input.isComposing:true;",
                GetDocument().title().Utf8().data());
@@ -1181,17 +1180,17 @@
   CreateHTMLWithCompositionInputEventListeners();
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
 
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("hell", underlines, 4, 4);
+  Controller().SetComposition("hell", ime_text_spans, 4, 4);
   EXPECT_STREQ("beforeinput.data:hell;input.data:hell;",
                GetDocument().title().Utf8().data());
 
   // Replace the existing composition.
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("hello", underlines, 0, 0);
+  Controller().SetComposition("hello", ime_text_spans, 0, 0);
   EXPECT_STREQ("beforeinput.data:hello;input.data:hello;",
                GetDocument().title().Utf8().data());
 }
@@ -1200,11 +1199,11 @@
   CreateHTMLWithCompositionInputEventListeners();
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
 
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("hello", underlines, 5, 5);
+  Controller().SetComposition("hello", ime_text_spans, 5, 5);
   EXPECT_STREQ("beforeinput.data:hello;input.data:hello;",
                GetDocument().title().Utf8().data());
 
@@ -1219,17 +1218,17 @@
   CreateHTMLWithCompositionInputEventListeners();
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
 
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("hello", underlines, 5, 5);
+  Controller().SetComposition("hello", ime_text_spans, 5, 5);
   EXPECT_STREQ("beforeinput.data:hello;input.data:hello;",
                GetDocument().title().Utf8().data());
 
   // Delete the existing composition.
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("", underlines, 0, 0);
+  Controller().SetComposition("", ime_text_spans, 0, 0);
   EXPECT_STREQ("beforeinput.data:;input.data:null;compositionend.data:;",
                GetDocument().title().Utf8().data());
 }
@@ -1238,25 +1237,25 @@
   CreateHTMLWithCompositionInputEventListeners();
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
 
   // Insert new text without previous composition.
   GetDocument().setTitle(g_empty_string);
   GetDocument().UpdateStyleAndLayout();
-  Controller().CommitText("hello", underlines, 0);
+  Controller().CommitText("hello", ime_text_spans, 0);
   EXPECT_STREQ("beforeinput.data:hello;input.data:hello;",
                GetDocument().title().Utf8().data());
 
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("n", underlines, 1, 1);
+  Controller().SetComposition("n", ime_text_spans, 1, 1);
   EXPECT_STREQ("beforeinput.data:n;input.data:n;",
                GetDocument().title().Utf8().data());
 
   // Insert new text with previous composition.
   GetDocument().setTitle(g_empty_string);
   GetDocument().UpdateStyleAndLayout();
-  Controller().CommitText("hello", underlines, 1);
+  Controller().CommitText("hello", ime_text_spans, 1);
   EXPECT_STREQ(
       "beforeinput.data:hello;input.data:hello;compositionend.data:hello;",
       GetDocument().title().Utf8().data());
@@ -1266,24 +1265,24 @@
   CreateHTMLWithCompositionInputEventListeners();
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
 
   // Insert empty text without previous composition.
   GetDocument().setTitle(g_empty_string);
   GetDocument().UpdateStyleAndLayout();
-  Controller().CommitText("", underlines, 0);
+  Controller().CommitText("", ime_text_spans, 0);
   EXPECT_STREQ("beforeinput.data:;", GetDocument().title().Utf8().data());
 
   GetDocument().setTitle(g_empty_string);
-  Controller().SetComposition("n", underlines, 1, 1);
+  Controller().SetComposition("n", ime_text_spans, 1, 1);
   EXPECT_STREQ("beforeinput.data:n;input.data:n;",
                GetDocument().title().Utf8().data());
 
   // Insert empty text with previous composition.
   GetDocument().setTitle(g_empty_string);
   GetDocument().UpdateStyleAndLayout();
-  Controller().CommitText("", underlines, 1);
+  Controller().CommitText("", ime_text_spans, 1);
   EXPECT_STREQ("beforeinput.data:;input.data:null;compositionend.data:;",
                GetDocument().title().Utf8().data());
 }
@@ -1292,10 +1291,10 @@
   CreateHTMLWithCompositionEndEventListener(kNoSelection);
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
 
-  Controller().SetComposition("hello", underlines, 1, 1);
+  Controller().SetComposition("hello", ime_text_spans, 1, 1);
   GetDocument().UpdateStyleAndLayout();
   EXPECT_EQ(1u, Controller().GetSelectionOffsets().Start());
   EXPECT_EQ(1u, Controller().GetSelectionOffsets().End());
@@ -1314,7 +1313,7 @@
   EXPECT_EQ(kWebTextInputTypeText, Controller().TextInputType());
 
   // The test requires non-empty composition.
-  Controller().SetComposition("hello", Vector<CompositionUnderline>(), 5, 5);
+  Controller().SetComposition("hello", Vector<ImeTextSpan>(), 5, 5);
   EXPECT_EQ(kWebTextInputTypeText, Controller().TextInputType());
 
   // Remove element 'a'.
@@ -1331,8 +1330,8 @@
 TEST_F(InputMethodControllerTest, ReflectsSpaceWithoutNbspMangling) {
   InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  Controller().CommitText(String("  "), underlines, 0);
+  Vector<ImeTextSpan> ime_text_spans;
+  Controller().CommitText(String("  "), ime_text_spans, 0);
 
   // In a contenteditable, multiple spaces or a space at the edge needs to be
   // nbsp to affect layout properly, but it confuses some IMEs (particularly
@@ -1342,13 +1341,13 @@
   EXPECT_EQ(' ', Controller().TextInputInfo().value.Ascii()[1]);
 }
 
-TEST_F(InputMethodControllerTest, SetCompositionPlainTextWithUnderline) {
+TEST_F(InputMethodControllerTest, SetCompositionPlainTextWithIme_Span) {
   InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 1, Color(255, 0, 0), false, 0));
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 1, Color(255, 0, 0), false, 0));
 
-  Controller().SetComposition(" ", underlines, 1, 1);
+  Controller().SetComposition(" ", ime_text_spans, 1, 1);
 
   ASSERT_EQ(1u, GetDocument().Markers().Markers().size());
 
@@ -1356,17 +1355,17 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers()[0]->EndOffset());
 }
 
-TEST_F(InputMethodControllerTest, CommitPlainTextWithUnderlineInsert) {
+TEST_F(InputMethodControllerTest, CommitPlainTextWithIme_SpanInsert) {
   InsertHTMLElement("<div id='sample' contenteditable>Initial text.</div>",
                     "sample");
 
-  Vector<CompositionUnderline> underlines;
+  Vector<ImeTextSpan> ime_text_spans;
 
   Controller().SetEditableSelectionOffsets(PlainTextRange(8, 8));
 
-  underlines.push_back(CompositionUnderline(1, 11, Color(255, 0, 0), false, 0));
+  ime_text_spans.push_back(ImeTextSpan(1, 11, Color(255, 0, 0), false, 0));
 
-  Controller().CommitText(String("underlined"), underlines, 0);
+  Controller().CommitText(String("ime_text_spand"), ime_text_spans, 0);
 
   ASSERT_EQ(1u, GetDocument().Markers().Markers().size());
 
@@ -1374,17 +1373,17 @@
   EXPECT_EQ(19u, GetDocument().Markers().Markers()[0]->EndOffset());
 }
 
-TEST_F(InputMethodControllerTest, CommitPlainTextWithUnderlineReplace) {
+TEST_F(InputMethodControllerTest, CommitPlainTextWithIme_SpanReplace) {
   InsertHTMLElement("<div id='sample' contenteditable>Initial text.</div>",
                     "sample");
 
-  Vector<CompositionUnderline> underlines;
+  Vector<ImeTextSpan> ime_text_spans;
 
-  Controller().SetCompositionFromExistingText(underlines, 8, 12);
+  Controller().SetCompositionFromExistingText(ime_text_spans, 8, 12);
 
-  underlines.push_back(CompositionUnderline(1, 11, Color(255, 0, 0), false, 0));
+  ime_text_spans.push_back(ImeTextSpan(1, 11, Color(255, 0, 0), false, 0));
 
-  Controller().CommitText(String("string"), underlines, 0);
+  Controller().CommitText(String("string"), ime_text_spans, 0);
 
   ASSERT_EQ(1u, GetDocument().Markers().Markers().size());
 
@@ -1392,23 +1391,22 @@
   EXPECT_EQ(15u, GetDocument().Markers().Markers()[0]->EndOffset());
 }
 
-TEST_F(InputMethodControllerTest,
-       CompositionUnderlineAppearsCorrectlyAfterNewline) {
+TEST_F(InputMethodControllerTest, ImeTextSpanAppearsCorrectlyAfterNewline) {
   Element* div =
       InsertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
-  Vector<CompositionUnderline> underlines;
-  Controller().SetComposition(String("hello"), underlines, 6, 6);
+  Vector<ImeTextSpan> ime_text_spans;
+  Controller().SetComposition(String("hello"), ime_text_spans, 6, 6);
   Controller().FinishComposingText(InputMethodController::kKeepSelection);
   GetFrame().GetEditor().InsertLineBreak();
 
-  Controller().SetCompositionFromExistingText(underlines, 8, 8);
+  Controller().SetCompositionFromExistingText(ime_text_spans, 8, 8);
 
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
-  Controller().SetComposition(String("world"), underlines, 0, 0);
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
+  Controller().SetComposition(String("world"), ime_text_spans, 0, 0);
   ASSERT_EQ(1u, GetDocument().Markers().Markers().size());
 
-  // Verify composition underline shows up on the second line, not the first
+  // Verify composition marker shows up on the second line, not the first
   ASSERT_FALSE(GetDocument().Markers().MarkerAtPosition(
       PlainTextRange(2).CreateRange(*div).StartPosition(),
       DocumentMarker::AllMarkers()));
@@ -1429,9 +1427,9 @@
   editable->focus();
 
   // Simulate composition in the |contentEditable|.
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 5, Color(255, 0, 0), false, 0));
-  Controller().SetComposition("foo", underlines, 3, 3);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 5, Color(255, 0, 0), false, 0));
+  Controller().SetComposition("foo", ime_text_spans, 3, 3);
 
   EXPECT_TRUE(Controller().HasComposition());
   EXPECT_EQ(0u, Controller().CompositionRange()->startOffset());
@@ -1474,11 +1472,11 @@
   GetDocument().UpdateStyleAndLayout();
   Controller().SetEditableSelectionOffsets(PlainTextRange(4, 4));
 
-  Vector<CompositionUnderline> underlines;
-  underlines.push_back(CompositionUnderline(0, 3, Color(255, 0, 0), false, 0));
-  Controller().SetComposition(String("def"), underlines, 0, 3);
-  Controller().SetComposition(String(""), underlines, 0, 3);
-  Controller().CommitText(String("def"), underlines, 0);
+  Vector<ImeTextSpan> ime_text_spans;
+  ime_text_spans.push_back(ImeTextSpan(0, 3, Color(255, 0, 0), false, 0));
+  Controller().SetComposition(String("def"), ime_text_spans, 0, 3);
+  Controller().SetComposition(String(""), ime_text_spans, 0, 3);
+  Controller().CommitText(String("def"), ime_text_spans, 0);
 
   EXPECT_STREQ("abc\ndef", textarea->value().Utf8().data());
 }
@@ -1488,17 +1486,17 @@
       "<div id='sample' contenteditable>Initial text blah</div>", "sample");
 
   // Delete "Initial"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 7);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // The space at the beginning of the string should have been converted to an
   // nbsp
   EXPECT_STREQ("&nbsp;text blah", div->innerHTML().Utf8().data());
 
   // Delete "blah"
-  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 6, 10);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // The space at the end of the string should have been converted to an nbsp
   EXPECT_STREQ("&nbsp;text&nbsp;", div->innerHTML().Utf8().data());
@@ -1510,13 +1508,13 @@
 
   input->setValue("Abc Def Ghi");
   GetDocument().UpdateStyleAndLayout();
-  Vector<CompositionUnderline> empty_underlines;
+  Vector<ImeTextSpan> empty_ime_text_spans;
   Controller().SetEditableSelectionOffsets(PlainTextRange(4, 8));
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
   EXPECT_STREQ("Abc Ghi", input->value().Utf8().data());
 
   Controller().SetEditableSelectionOffsets(PlainTextRange(4, 7));
-  Controller().CommitText(String("1"), empty_underlines, 0);
+  Controller().CommitText(String("1"), empty_ime_text_spans, 0);
   EXPECT_STREQ("Abc 1", input->value().Utf8().data());
 }
 
@@ -1541,13 +1539,13 @@
       marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
       Color::kBlack);
   // Delete "Initial"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 7);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Delete "blah"
-  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 6, 10);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Check that the marker is still attached to "text" and doesn't include
   // either space around it
@@ -1570,13 +1568,13 @@
       marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
       Color::kBlack);
   // Delete "Initial"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 7);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Delete "blah"
-  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 6, 10);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Check that the marker is still attached to " text" and includes the space
   // before "text" but not the space after
@@ -1599,13 +1597,13 @@
       marker_range, Color::kBlack, StyleableMarker::Thickness::kThin,
       Color::kBlack);
   // Delete "Initial"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 7);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Delete "blah"
-  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 6, 10);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Check that the marker is still attached to "text " and includes the space
   // after "text" but not the space before
@@ -1630,13 +1628,13 @@
       Color::kBlack);
 
   // Delete "Initial"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 7);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Delete "blah"
-  Controller().SetCompositionFromExistingText(empty_underlines, 6, 10);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 6, 10);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Check that the marker is still attached to " text " and includes both the
   // space before "text" and the space after
@@ -1657,9 +1655,9 @@
       marker_range, TextMatchMarker::MatchStatus::kInactive);
 
   // Replace "Initial" with "Original"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
-  Controller().CommitText(String("Original"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 7);
+  Controller().CommitText(String("Original"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1677,9 +1675,9 @@
       Color::kBlack);
 
   // Replace "Initial" with "Original"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 7);
-  Controller().CommitText(String("Original"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 7);
+  Controller().CommitText(String("Original"), empty_ime_text_spans, 0);
 
   // Verify marker is under "Original text"
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
@@ -1701,9 +1699,9 @@
       marker_range, TextMatchMarker::MatchStatus::kInactive);
 
   // Replace "some initial" with "boring"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 20);
-  Controller().CommitText(String("boring"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 20);
+  Controller().CommitText(String("boring"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1722,9 +1720,9 @@
       Color::kBlack);
 
   // Replace "some initial" with "boring"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 20);
-  Controller().CommitText(String("boring"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 20);
+  Controller().CommitText(String("boring"), empty_ime_text_spans, 0);
 
   // Verify marker is under " text"
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
@@ -1744,9 +1742,9 @@
       marker_range, TextMatchMarker::MatchStatus::kInactive);
 
   // Replace "text" with "string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 12);
-  Controller().CommitText(String("string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 12);
+  Controller().CommitText(String("string"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1763,9 +1761,9 @@
       Color::kBlack);
 
   // Replace "text" with "string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 12);
-  Controller().CommitText(String("string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 12);
+  Controller().CommitText(String("string"), empty_ime_text_spans, 0);
 
   // Verify marker is under "Initial string"
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
@@ -1787,9 +1785,9 @@
       marker_range, TextMatchMarker::MatchStatus::kInactive);
 
   // Replace "initial text" with "content"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 13, 25);
-  Controller().CommitText(String("content"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 13, 25);
+  Controller().CommitText(String("content"), empty_ime_text_spans, 0);
 
   EXPECT_STREQ("This is some content", div->innerHTML().Utf8().data());
 
@@ -1810,9 +1808,9 @@
       Color::kBlack);
 
   // Replace "initial text" with "content"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 13, 25);
-  Controller().CommitText(String("content"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 13, 25);
+  Controller().CommitText(String("content"), empty_ime_text_spans, 0);
 
   EXPECT_STREQ("This is some content", div->innerHTML().Utf8().data());
 
@@ -1834,9 +1832,9 @@
       marker_range, TextMatchMarker::MatchStatus::kInactive);
 
   // Replace "text" with "string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 12);
-  Controller().CommitText(String("string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 12);
+  Controller().CommitText(String("string"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1854,9 +1852,9 @@
       Color::kBlack);
 
   // Replace "text" with "string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 12);
-  Controller().CommitText(String("string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 12);
+  Controller().CommitText(String("string"), empty_ime_text_spans, 0);
 
   // Verify marker is under "string"
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
@@ -1879,9 +1877,9 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
   // Replace "Initial text" with "New string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 12);
-  Controller().CommitText(String("New string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 12);
+  Controller().CommitText(String("New string"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1901,9 +1899,9 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
   // Replace "Initial text" with "New string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 12);
-  Controller().CommitText(String("New string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 12);
+  Controller().CommitText(String("New string"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1922,9 +1920,9 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
   // Replace "Initial text" with "New string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 12);
-  Controller().CommitText(String("New string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 12);
+  Controller().CommitText(String("New string"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1944,9 +1942,9 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
   // Replace "Initial text" with "New string"
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 0, 12);
-  Controller().CommitText(String("New string"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 0, 12);
+  Controller().CommitText(String("New string"), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -1980,9 +1978,9 @@
   EXPECT_EQ(5u, GetDocument().Markers().Markers().size());
 
   // Delete third marker and portions of second and fourth
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 17);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 17);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Verify markers were updated correctly
   EXPECT_EQ(2u, GetDocument().Markers().Markers().size());
@@ -2027,9 +2025,9 @@
   EXPECT_EQ(5u, GetDocument().Markers().Markers().size());
 
   // Delete third marker and portions of second and fourth
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 8, 17);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 8, 17);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Verify markers were updated correctly
   EXPECT_EQ(4u, GetDocument().Markers().Markers().size());
@@ -2060,9 +2058,9 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
   // Delete exactly on the marker
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 5, 10);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 5, 10);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
@@ -2080,9 +2078,9 @@
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
   // Delete exactly on the marker
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 5, 10);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 5, 10);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
 }
 
@@ -2096,9 +2094,9 @@
       marker_range, TextMatchMarker::MatchStatus::kInactive);
 
   // Delete middle of marker
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 6, 9);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 6, 9);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   // Verify marker was removed
   EXPECT_EQ(0u, GetDocument().Markers().Markers().size());
@@ -2116,9 +2114,9 @@
       Color::kBlack);
 
   // Delete middle of marker
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetCompositionFromExistingText(empty_underlines, 6, 9);
-  Controller().CommitText(String(""), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetCompositionFromExistingText(empty_ime_text_spans, 6, 9);
+  Controller().CommitText(String(""), empty_ime_text_spans, 0);
 
   EXPECT_EQ(1u, GetDocument().Markers().Markers().size());
 
@@ -2147,9 +2145,9 @@
   EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
 
   // insert in middle of second marker
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetComposition("", empty_underlines, 7, 7);
-  Controller().CommitText(String("66666"), empty_underlines, -7);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetComposition("", empty_ime_text_spans, 7, 7);
+  Controller().CommitText(String("66666"), empty_ime_text_spans, -7);
 
   EXPECT_EQ(2u, GetDocument().Markers().Markers().size());
 
@@ -2184,9 +2182,9 @@
   EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
 
   // insert in middle of second marker
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetComposition("", empty_underlines, 7, 7);
-  Controller().CommitText(String("66666"), empty_underlines, -7);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetComposition("", empty_ime_text_spans, 7, 7);
+  Controller().CommitText(String("66666"), empty_ime_text_spans, -7);
 
   EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
 
@@ -2219,9 +2217,9 @@
 
   EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
 
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetComposition("", empty_underlines, 5, 5);
-  Controller().CommitText(String("77777"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetComposition("", empty_ime_text_spans, 5, 5);
+  Controller().CommitText(String("77777"), empty_ime_text_spans, 0);
 
   EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
 
@@ -2258,9 +2256,9 @@
 
   EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
 
-  Vector<CompositionUnderline> empty_underlines;
-  Controller().SetComposition("", empty_underlines, 5, 5);
-  Controller().CommitText(String("77777"), empty_underlines, 0);
+  Vector<ImeTextSpan> empty_ime_text_spans;
+  Controller().SetComposition("", empty_ime_text_spans, 5, 5);
+  Controller().CommitText(String("77777"), empty_ime_text_spans, 0);
 
   EXPECT_EQ(3u, GetDocument().Markers().Markers().size());
 
@@ -2295,7 +2293,7 @@
 
   EXPECT_EQ(kWebTextInputTypeText, Controller().TextInputType());
 
-  Controller().SetComposition("abcde", Vector<CompositionUnderline>(), 4, 4);
+  Controller().SetComposition("abcde", Vector<ImeTextSpan>(), 4, 4);
   EXPECT_STREQ("abcde", input->value().Utf8().data());
 
   Controller().FinishComposingText(InputMethodController::kKeepSelection);
diff --git a/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.cpp b/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.cpp
index 0a7f545..ec3ab31ac 100644
--- a/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.cpp
@@ -7,11 +7,11 @@
 #include "core/InputTypeNames.h"
 #include "core/dom/Document.h"
 #include "core/dom/UserGestureIndicator.h"
-#include "core/editing/CompositionUnderlineVectorBuilder.h"
 #include "core/editing/EditingUtilities.h"
 #include "core/editing/Editor.h"
 #include "core/editing/EphemeralRange.h"
 #include "core/editing/FrameSelection.h"
+#include "core/editing/ImeTextSpanVectorBuilder.h"
 #include "core/editing/InputMethodController.h"
 #include "core/editing/PlainTextRange.h"
 #include "core/exported/WebPluginContainerImpl.h"
@@ -37,12 +37,12 @@
 
 bool WebInputMethodControllerImpl::SetComposition(
     const WebString& text,
-    const WebVector<WebCompositionUnderline>& underlines,
+    const WebVector<WebImeTextSpan>& ime_text_spans,
     const WebRange& replacement_range,
     int selection_start,
     int selection_end) {
   if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) {
-    return plugin->SetComposition(text, underlines, replacement_range,
+    return plugin->SetComposition(text, ime_text_spans, replacement_range,
                                   selection_start, selection_end);
   }
 
@@ -72,12 +72,8 @@
   UserGestureIndicator gesture_indicator(UserGestureToken::Create(
       GetFrame()->GetDocument(), UserGestureToken::kNewGesture));
 
-  // When the range of composition underlines overlap with the range between
-  // selectionStart and selectionEnd, WebKit somehow won't paint the selection
-  // at all (see InlineTextBox::paint() function in InlineTextBox.cpp).
-  // But the selection range actually takes effect.
   GetInputMethodController().SetComposition(
-      String(text), CompositionUnderlineVectorBuilder::Build(underlines),
+      String(text), ImeTextSpanVectorBuilder::Build(ime_text_spans),
       selection_start, selection_end);
 
   return text.IsEmpty() || GetInputMethodController().HasComposition();
@@ -107,14 +103,14 @@
 
 bool WebInputMethodControllerImpl::CommitText(
     const WebString& text,
-    const WebVector<WebCompositionUnderline>& underlines,
+    const WebVector<WebImeTextSpan>& ime_text_spans,
     const WebRange& replacement_range,
     int relative_caret_position) {
   UserGestureIndicator gesture_indicator(UserGestureToken::Create(
       GetFrame()->GetDocument(), UserGestureToken::kNewGesture));
 
   if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) {
-    return plugin->CommitText(text, underlines, replacement_range,
+    return plugin->CommitText(text, ime_text_spans, replacement_range,
                               relative_caret_position);
   }
 
@@ -127,7 +123,7 @@
   GetFrame()->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets();
 
   return GetInputMethodController().CommitText(
-      text, CompositionUnderlineVectorBuilder::Build(underlines),
+      text, ImeTextSpanVectorBuilder::Build(ime_text_spans),
       relative_caret_position);
 }
 
diff --git a/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.h b/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.h
index a2e4219..001d1c6 100644
--- a/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebInputMethodControllerImpl.h
@@ -8,7 +8,7 @@
 #include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Allocator.h"
-#include "public/web/WebCompositionUnderline.h"
+#include "public/web/WebImeTextSpan.h"
 #include "public/web/WebInputMethodController.h"
 
 namespace blink {
@@ -31,12 +31,12 @@
 
   // WebInputMethodController overrides.
   bool SetComposition(const WebString& text,
-                      const WebVector<WebCompositionUnderline>& underlines,
+                      const WebVector<WebImeTextSpan>& ime_text_spans,
                       const WebRange& replacement_range,
                       int selection_start,
                       int selection_end) override;
   bool CommitText(const WebString& text,
-                  const WebVector<WebCompositionUnderline>& underlines,
+                  const WebVector<WebImeTextSpan>& ime_text_spans,
                   const WebRange& replacement_range,
                   int relative_caret_position) override;
   bool FinishComposingText(
diff --git a/third_party/WebKit/Source/core/exported/WebViewImpl.cpp b/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
index 41353c58..bf69e60 100644
--- a/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
@@ -1853,11 +1853,8 @@
     // apparent position unchanged.
     ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_);
 
-    float browser_controls_viewport_adjustment =
-        GetBrowserControls().LayoutHeight() -
-        GetBrowserControls().ContentOffset();
     visual_viewport.SetBrowserControlsAdjustment(
-        browser_controls_viewport_adjustment);
+        GetBrowserControls().UnreportedSizeAdjustment());
   }
 }
 
diff --git a/third_party/WebKit/Source/core/exported/WebViewTest.cpp b/third_party/WebKit/Source/core/exported/WebViewTest.cpp
index af31524..cb95a65b 100644
--- a/third_party/WebKit/Source/core/exported/WebViewTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebViewTest.cpp
@@ -961,9 +961,9 @@
 
   // The test requires non-empty composition.
   std::string composition_text("hello");
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 5, 5);
 
   // Do arbitrary change to make layout dirty.
@@ -991,9 +991,9 @@
       web_view->MainFrameImpl()
           ->FrameWidget()
           ->GetActiveWebInputMethodController();
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 3, 3);
 
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
@@ -1012,7 +1012,7 @@
   EXPECT_EQ(-1, info.composition_end);
 
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 3, 3);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helhellolo", std::string(info.value.Utf8().data()));
@@ -1040,11 +1040,11 @@
           ->FrameWidget()
           ->GetActiveWebInputMethodController();
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
 
-  active_input_method_controller->CommitText("hello", empty_underlines,
+  active_input_method_controller->CommitText("hello", empty_ime_text_spans,
                                              WebRange(), 0);
-  active_input_method_controller->CommitText("world", empty_underlines,
+  active_input_method_controller->CommitText("world", empty_ime_text_spans,
                                              WebRange(), -5);
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
 
@@ -1059,7 +1059,7 @@
 
   // Caret is on the left of composing text.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 0, 0);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1070,7 +1070,7 @@
 
   // Caret is on the right of composing text.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 3, 3);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1081,7 +1081,7 @@
 
   // Caret is between composing text and left boundary.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), -2, -2);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1092,7 +1092,7 @@
 
   // Caret is between composing text and right boundary.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 5, 5);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1103,7 +1103,7 @@
 
   // Caret is on the left boundary.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), -5, -5);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1114,7 +1114,7 @@
 
   // Caret is on the right boundary.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 8, 8);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1125,7 +1125,7 @@
 
   // Caret exceeds the left boundary.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), -100, -100);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1136,7 +1136,7 @@
 
   // Caret exceeds the right boundary.
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 100, 100);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
@@ -1156,9 +1156,9 @@
           ->FrameWidget()
           ->GetActiveWebInputMethodController();
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
 
-  active_input_method_controller->CommitText("hello", empty_underlines,
+  active_input_method_controller->CommitText("hello", empty_ime_text_spans,
                                              WebRange(), 0);
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
 
@@ -1169,7 +1169,7 @@
   EXPECT_EQ(-1, info.composition_end);
 
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(""), empty_underlines, WebRange(), 0, 0);
+      WebString::FromUTF8(""), empty_ime_text_spans, WebRange(), 0, 0);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
   EXPECT_EQ(5, info.selection_start);
@@ -1178,7 +1178,7 @@
   EXPECT_EQ(-1, info.composition_end);
 
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(""), empty_underlines, WebRange(), -2, -2);
+      WebString::FromUTF8(""), empty_ime_text_spans, WebRange(), -2, -2);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
   EXPECT_EQ(3, info.selection_start);
@@ -1197,11 +1197,11 @@
           ->FrameWidget()
           ->GetActiveWebInputMethodController();
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
 
   // Caret is on the left of composing text.
-  active_input_method_controller->CommitText("ab", empty_underlines, WebRange(),
-                                             -2);
+  active_input_method_controller->CommitText("ab", empty_ime_text_spans,
+                                             WebRange(), -2);
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("ab", std::string(info.value.Utf8().data()));
   EXPECT_EQ(0, info.selection_start);
@@ -1210,8 +1210,8 @@
   EXPECT_EQ(-1, info.composition_end);
 
   // Caret is on the right of composing text.
-  active_input_method_controller->CommitText("c", empty_underlines, WebRange(),
-                                             1);
+  active_input_method_controller->CommitText("c", empty_ime_text_spans,
+                                             WebRange(), 1);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("cab", std::string(info.value.Utf8().data()));
   EXPECT_EQ(2, info.selection_start);
@@ -1220,7 +1220,7 @@
   EXPECT_EQ(-1, info.composition_end);
 
   // Caret is on the left boundary.
-  active_input_method_controller->CommitText("def", empty_underlines,
+  active_input_method_controller->CommitText("def", empty_ime_text_spans,
                                              WebRange(), -5);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("cadefb", std::string(info.value.Utf8().data()));
@@ -1230,8 +1230,8 @@
   EXPECT_EQ(-1, info.composition_end);
 
   // Caret is on the right boundary.
-  active_input_method_controller->CommitText("g", empty_underlines, WebRange(),
-                                             6);
+  active_input_method_controller->CommitText("g", empty_ime_text_spans,
+                                             WebRange(), 6);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("gcadefb", std::string(info.value.Utf8().data()));
   EXPECT_EQ(7, info.selection_start);
@@ -1240,8 +1240,8 @@
   EXPECT_EQ(-1, info.composition_end);
 
   // Caret exceeds the left boundary.
-  active_input_method_controller->CommitText("hi", empty_underlines, WebRange(),
-                                             -100);
+  active_input_method_controller->CommitText("hi", empty_ime_text_spans,
+                                             WebRange(), -100);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("gcadefbhi", std::string(info.value.Utf8().data()));
   EXPECT_EQ(0, info.selection_start);
@@ -1250,8 +1250,8 @@
   EXPECT_EQ(-1, info.composition_end);
 
   // Caret exceeds the right boundary.
-  active_input_method_controller->CommitText("jk", empty_underlines, WebRange(),
-                                             100);
+  active_input_method_controller->CommitText("jk", empty_ime_text_spans,
+                                             WebRange(), 100);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("jkgcadefbhi", std::string(info.value.Utf8().data()));
   EXPECT_EQ(11, info.selection_start);
@@ -1270,9 +1270,9 @@
           ->FrameWidget()
           ->GetActiveWebInputMethodController();
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8("abc"), empty_underlines, WebRange(), 0, 0);
+      WebString::FromUTF8("abc"), empty_ime_text_spans, WebRange(), 0, 0);
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("abc", std::string(info.value.Utf8().data()));
   EXPECT_EQ(0, info.selection_start);
@@ -1282,7 +1282,7 @@
 
   // Deletes ongoing composition, inserts the specified text and moves the
   // caret.
-  active_input_method_controller->CommitText("hello", empty_underlines,
+  active_input_method_controller->CommitText("hello", empty_ime_text_spans,
                                              WebRange(), -2);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
@@ -1292,7 +1292,7 @@
   EXPECT_EQ(-1, info.composition_end);
 
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8("abc"), empty_underlines, WebRange(), 0, 0);
+      WebString::FromUTF8("abc"), empty_ime_text_spans, WebRange(), 0, 0);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helabclo", std::string(info.value.Utf8().data()));
   EXPECT_EQ(3, info.selection_start);
@@ -1301,8 +1301,8 @@
   EXPECT_EQ(6, info.composition_end);
 
   // Deletes ongoing composition and moves the caret.
-  active_input_method_controller->CommitText("", empty_underlines, WebRange(),
-                                             2);
+  active_input_method_controller->CommitText("", empty_ime_text_spans,
+                                             WebRange(), 2);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
   EXPECT_EQ(5, info.selection_start);
@@ -1311,7 +1311,7 @@
   EXPECT_EQ(-1, info.composition_end);
 
   // Inserts the specified text and moves the caret.
-  active_input_method_controller->CommitText("world", empty_underlines,
+  active_input_method_controller->CommitText("world", empty_ime_text_spans,
                                              WebRange(), -5);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloworld", std::string(info.value.Utf8().data()));
@@ -1321,8 +1321,8 @@
   EXPECT_EQ(-1, info.composition_end);
 
   // Only moves the caret.
-  active_input_method_controller->CommitText("", empty_underlines, WebRange(),
-                                             5);
+  active_input_method_controller->CommitText("", empty_ime_text_spans,
+                                             WebRange(), 5);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("helloworld", std::string(info.value.Utf8().data()));
   EXPECT_EQ(10, info.selection_start);
@@ -1341,10 +1341,10 @@
   EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().height);
 
   // Set up a composition from existing text that needs to be committed.
-  Vector<CompositionUnderline> empty_underlines;
+  Vector<ImeTextSpan> empty_ime_text_spans;
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
   frame->GetFrame()->GetInputMethodController().SetCompositionFromExistingText(
-      empty_underlines, 0, 3);
+      empty_ime_text_spans, 0, 3);
 
   // Scroll the input field out of the viewport.
   Element* element = static_cast<Element*>(
@@ -1371,13 +1371,13 @@
       base_url_ + "text_area_populated.html");
   web_view->SetInitialFocus(false);
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
 
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
   WebInputMethodController* active_input_method_controller =
       frame->GetInputMethodController();
   frame->SetEditableSelectionOffsets(4, 4);
-  frame->SetCompositionFromExistingText(8, 12, empty_underlines);
+  frame->SetCompositionFromExistingText(8, 12, empty_ime_text_spans);
 
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz",
@@ -1397,7 +1397,7 @@
 
   std::string composition_text("\n");
   active_input_method_controller->CommitText(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 0);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ(5, info.selection_start);
@@ -1475,20 +1475,20 @@
   WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
       base_url_ + "input_field_populated.html");
   web_view->SetInitialFocus(false);
-  WebVector<WebCompositionUnderline> underlines(static_cast<size_t>(1));
-  underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0);
+  WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
+  ime_text_spans[0] = WebImeTextSpan(0, 4, 0, false, 0);
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
   WebInputMethodController* active_input_method_controller =
       frame->GetInputMethodController();
   frame->SetEditableSelectionOffsets(4, 10);
-  frame->SetCompositionFromExistingText(8, 12, underlines);
+  frame->SetCompositionFromExistingText(8, 12, ime_text_spans);
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ(4, info.selection_start);
   EXPECT_EQ(10, info.selection_end);
   EXPECT_EQ(8, info.composition_start);
   EXPECT_EQ(12, info.composition_end);
-  WebVector<WebCompositionUnderline> empty_underlines;
-  frame->SetCompositionFromExistingText(0, 0, empty_underlines);
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
+  frame->SetCompositionFromExistingText(0, 0, empty_ime_text_spans);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ(4, info.selection_start);
   EXPECT_EQ(10, info.selection_end);
@@ -1501,23 +1501,23 @@
   WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
       base_url_ + "text_area_populated.html");
   web_view->SetInitialFocus(false);
-  WebVector<WebCompositionUnderline> underlines(static_cast<size_t>(1));
-  underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0);
+  WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
+  ime_text_spans[0] = WebImeTextSpan(0, 4, 0, false, 0);
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
   WebInputMethodController* active_input_method_controller =
       frame->FrameWidget()->GetActiveWebInputMethodController();
   frame->SetEditableSelectionOffsets(27, 27);
   std::string new_line_text("\n");
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   active_input_method_controller->CommitText(
-      WebString::FromUTF8(new_line_text.c_str()), empty_underlines, WebRange(),
-      0);
+      WebString::FromUTF8(new_line_text.c_str()), empty_ime_text_spans,
+      WebRange(), 0);
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz",
             std::string(info.value.Utf8().data()));
 
   frame->SetEditableSelectionOffsets(31, 31);
-  frame->SetCompositionFromExistingText(30, 34, underlines);
+  frame->SetCompositionFromExistingText(30, 34, ime_text_spans);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz",
             std::string(info.value.Utf8().data()));
@@ -1528,7 +1528,7 @@
 
   std::string composition_text("yolo");
   active_input_method_controller->CommitText(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 0);
   info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("0123456789abcdefghijklmnopq\nrsyoloxyz",
@@ -1544,13 +1544,13 @@
   WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
       base_url_ + "content_editable_rich_text.html");
   web_view->SetInitialFocus(false);
-  WebVector<WebCompositionUnderline> underlines(static_cast<size_t>(1));
-  underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0);
+  WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
+  ime_text_spans[0] = WebImeTextSpan(0, 4, 0, false, 0);
   WebLocalFrameImpl* frame = web_view->MainFrameImpl();
   frame->SetEditableSelectionOffsets(1, 1);
   WebDocument document = web_view->MainFrameImpl()->GetDocument();
   EXPECT_FALSE(document.GetElementById("bold").IsNull());
-  frame->SetCompositionFromExistingText(0, 4, underlines);
+  frame->SetCompositionFromExistingText(0, 4, ime_text_spans);
   EXPECT_FALSE(document.GetElementById("bold").IsNull());
 }
 
@@ -1562,17 +1562,17 @@
 
   std::string composition_text_first("hello ");
   std::string composition_text_second("world");
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   WebInputMethodController* active_input_method_controller =
       web_view->MainFrameImpl()
           ->FrameWidget()
           ->GetActiveWebInputMethodController();
   active_input_method_controller->CommitText(
-      WebString::FromUTF8(composition_text_first.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text_first.c_str()), empty_ime_text_spans,
       WebRange(), 0);
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text_second.c_str()), empty_underlines,
-      WebRange(), 5, 5);
+      WebString::FromUTF8(composition_text_second.c_str()),
+      empty_ime_text_spans, WebRange(), 5, 5);
 
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
   EXPECT_EQ("hello world", std::string(info.value.Utf8().data()));
@@ -2825,10 +2825,10 @@
   WebInputMethodController* active_input_method_controller =
       frame->FrameWidget()->GetActiveWebInputMethodController();
   EXPECT_TRUE(TapElementById(WebInputEvent::kGestureTap, target));
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   frame->SetEditableSelectionOffsets(8, 8);
   EXPECT_TRUE(active_input_method_controller->SetComposition(
-      "12345", empty_underlines, WebRange(), 8, 13));
+      "12345", empty_ime_text_spans, WebRange(), 8, 13));
   EXPECT_TRUE(frame->GetFrame()->GetInputMethodController().HasComposition());
   EXPECT_EQ("", std::string(frame->SelectionAsText().Utf8().data()));
   EXPECT_FALSE(frame->GetFrame()->Selection().IsHandleVisible());
@@ -3209,9 +3209,9 @@
   web_view->SetInitialFocus(false);
 
   // Set up a composition that needs to be committed.
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   frame->SetEditableSelectionOffsets(4, 10);
-  frame->SetCompositionFromExistingText(8, 12, empty_underlines);
+  frame->SetCompositionFromExistingText(8, 12, empty_ime_text_spans);
   WebTextInputInfo info = frame->GetInputMethodController()->TextInputInfo();
   EXPECT_EQ(4, info.selection_start);
   EXPECT_EQ(10, info.selection_end);
@@ -3253,12 +3253,12 @@
   // Test both input elements.
   for (int i = 0; i < 2; ++i) {
     // Select composition and do sanity check.
-    WebVector<WebCompositionUnderline> empty_underlines;
+    WebVector<WebImeTextSpan> empty_ime_text_spans;
     frame->SetEditableSelectionOffsets(6, 6);
     WebInputMethodController* active_input_method_controller =
         frame->FrameWidget()->GetActiveWebInputMethodController();
     EXPECT_TRUE(active_input_method_controller->SetComposition(
-        "fghij", empty_underlines, WebRange(), 0, 5));
+        "fghij", empty_ime_text_spans, WebRange(), 0, 5));
     frame->SetEditableSelectionOffsets(11, 11);
     VerifySelectionAndComposition(web_view, 11, 11, 6, 11, "initial case");
 
@@ -3273,7 +3273,7 @@
 
     frame->SetEditableSelectionOffsets(6, 6);
     EXPECT_TRUE(active_input_method_controller->SetComposition(
-        "fghi", empty_underlines, WebRange(), 0, 4));
+        "fghi", empty_ime_text_spans, WebRange(), 0, 4));
     frame->SetEditableSelectionOffsets(10, 10);
     VerifySelectionAndComposition(web_view, 10, 10, 6, 10,
                                   "after pressing Backspace");
@@ -3305,9 +3305,9 @@
   // Set up a composition that needs to be committed.
   std::string composition_text("testingtext");
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
   active_input_method_controller->SetComposition(
-      WebString::FromUTF8(composition_text.c_str()), empty_underlines,
+      WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
       WebRange(), 0, composition_text.length());
 
   WebTextInputInfo info = active_input_method_controller->TextInputInfo();
@@ -3338,10 +3338,10 @@
   frame->SetAutofillClient(&client);
   web_view->SetInitialFocus(false);
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
 
   client.ClearChangeCounts();
-  frame->SetCompositionFromExistingText(8, 12, empty_underlines);
+  frame->SetCompositionFromExistingText(8, 12, empty_ime_text_spans);
 
   WebTextInputInfo info = frame->GetInputMethodController()->TextInputInfo();
   EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz",
@@ -4079,7 +4079,7 @@
   EXPECT_TRUE(
       frame->FrameWidget()->GetActiveWebInputMethodController()->SetComposition(
           WebString::FromUTF8(std::string("hello").c_str()),
-          WebVector<WebCompositionUnderline>(), WebRange(), 3, 3));
+          WebVector<WebImeTextSpan>(), WebRange(), 3, 3));
   EXPECT_EQ(1, client.TextChangesFromUserGesture());
   EXPECT_FALSE(UserGestureIndicator::ProcessingUserGesture());
   EXPECT_TRUE(frame->HasMarkedText());
@@ -4482,12 +4482,12 @@
   frame->SetAutofillClient(&client);
   web_view->SetInitialFocus(false);
 
-  WebVector<WebCompositionUnderline> empty_underlines;
+  WebVector<WebImeTextSpan> empty_ime_text_spans;
 
   EXPECT_TRUE(
       frame->FrameWidget()->GetActiveWebInputMethodController()->CommitText(
-          WebString::FromUTF8(std::string("hello").c_str()), empty_underlines,
-          WebRange(), 0));
+          WebString::FromUTF8(std::string("hello").c_str()),
+          empty_ime_text_spans, WebRange(), 0));
   EXPECT_EQ(1, client.TextChangesFromUserGesture());
   EXPECT_FALSE(UserGestureIndicator::ProcessingUserGesture());
   frame->SetAutofillClient(0);
diff --git a/third_party/WebKit/Source/core/frame/BrowserControls.cpp b/third_party/WebKit/Source/core/frame/BrowserControls.cpp
index 46a13e38..241b830c 100644
--- a/third_party/WebKit/Source/core/frame/BrowserControls.cpp
+++ b/third_party/WebKit/Source/core/frame/BrowserControls.cpp
@@ -80,8 +80,8 @@
       top_height_ ? ContentOffset() : BottomContentOffset();
 }
 
-float BrowserControls::LayoutHeight() {
-  return shrink_viewport_ ? top_height_ + bottom_height_ : 0;
+float BrowserControls::UnreportedSizeAdjustment() {
+  return (shrink_viewport_ ? top_height_ : 0) - ContentOffset();
 }
 
 float BrowserControls::ContentOffset() {
diff --git a/third_party/WebKit/Source/core/frame/BrowserControls.h b/third_party/WebKit/Source/core/frame/BrowserControls.h
index 7c61fc2..6dd983c 100644
--- a/third_party/WebKit/Source/core/frame/BrowserControls.h
+++ b/third_party/WebKit/Source/core/frame/BrowserControls.h
@@ -30,12 +30,11 @@
 
   DECLARE_TRACE();
 
-  // The amount that the viewport was shrunk by to accommodate the top
-  // controls.
-  float LayoutHeight();
+  // The height the top controls are hidden; used for viewport adjustments
+  // while the controls are resizing.
+  float UnreportedSizeAdjustment();
   // The amount that browser controls are currently shown.
   float ContentOffset();
-  float BottomContentOffset();
 
   float TopHeight() const { return top_height_; }
   float BottomHeight() const { return bottom_height_; }
@@ -61,6 +60,7 @@
  private:
   explicit BrowserControls(const Page&);
   void ResetBaseline();
+  float BottomContentOffset();
 
   Member<const Page> page_;
 
diff --git a/third_party/WebKit/Source/core/frame/BrowserControlsTest.cpp b/third_party/WebKit/Source/core/frame/BrowserControlsTest.cpp
index 3596e526..04c0027 100644
--- a/third_party/WebKit/Source/core/frame/BrowserControlsTest.cpp
+++ b/third_party/WebKit/Source/core/frame/BrowserControlsTest.cpp
@@ -1132,4 +1132,40 @@
   }
 }
 
+// Test the size adjustment sent to the viewport when top controls exist.
+TEST_F(BrowserControlsTest, MAYBE(TopControlsSizeAdjustment)) {
+  WebViewImpl* web_view = Initialize();
+  web_view->ResizeWithBrowserControls(web_view->Size(), 50.f, 0, false);
+  web_view->GetBrowserControls().SetShownRatio(1);
+  EXPECT_FLOAT_EQ(-50.f,
+                  web_view->GetBrowserControls().UnreportedSizeAdjustment());
+
+  web_view->GetBrowserControls().SetShownRatio(0.5);
+  EXPECT_FLOAT_EQ(-25.f,
+                  web_view->GetBrowserControls().UnreportedSizeAdjustment());
+
+  web_view->GetBrowserControls().SetShownRatio(0.0);
+  EXPECT_FLOAT_EQ(0.f,
+                  web_view->GetBrowserControls().UnreportedSizeAdjustment());
+}
+
+// Test the size adjustment sent to the viewport when bottom controls exist.
+// There should never be an adjustment since the bottom controls do not change
+// the content offset.
+TEST_F(BrowserControlsTest, MAYBE(BottomControlsSizeAdjustment)) {
+  WebViewImpl* web_view = Initialize();
+  web_view->ResizeWithBrowserControls(web_view->Size(), 0, 50.f, false);
+  web_view->GetBrowserControls().SetShownRatio(1);
+  EXPECT_FLOAT_EQ(0.f,
+                  web_view->GetBrowserControls().UnreportedSizeAdjustment());
+
+  web_view->GetBrowserControls().SetShownRatio(0.5);
+  EXPECT_FLOAT_EQ(0.f,
+                  web_view->GetBrowserControls().UnreportedSizeAdjustment());
+
+  web_view->GetBrowserControls().SetShownRatio(0.0);
+  EXPECT_FLOAT_EQ(0.f,
+                  web_view->GetBrowserControls().UnreportedSizeAdjustment());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index d4bff49..fce9db4 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -208,12 +208,14 @@
       needs_paint_property_update_(true),
       current_update_lifecycle_phases_target_state_(
           DocumentLifecycle::kUninitialized),
+      past_layout_lifecycle_update_(false),
       scroll_anchor_(this),
       scrollbar_manager_(*this),
       needs_scrollbars_update_(false),
       suppress_adjust_view_size_(false),
       allows_layout_invalidation_after_layout_clean_(true),
       forcing_layout_parent_view_(false),
+      needs_intersection_observation_(false),
       main_thread_scrolling_reasons_(0),
       paint_frame_count_(0) {
   Init();
@@ -3143,6 +3145,9 @@
     return;
   }
 
+  AutoReset<bool> past_layout_lifecycle_update(&past_layout_lifecycle_update_,
+                                               true);
+
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
     frame_view.PerformScrollAnchoringAdjustments();
   });
@@ -4977,6 +4982,7 @@
        child = child->Tree().NextSibling()) {
     child->View()->UpdateViewportIntersectionsForSubtree(target_state);
   }
+  needs_intersection_observation_ = false;
 }
 
 void LocalFrameView::UpdateRenderThrottlingStatusForTesting() {
@@ -5137,9 +5143,23 @@
                                    total_screens_away));
 }
 
+void LocalFrameView::SetNeedsIntersectionObservation() {
+  needs_intersection_observation_ = true;
+  if (LocalFrameView* parent = ParentFrameView())
+    parent->SetNeedsIntersectionObservation();
+}
+
 bool LocalFrameView::ShouldThrottleRendering() const {
-  return CanThrottleRendering() && frame_->GetDocument() &&
-         Lifecycle().ThrottlingAllowed();
+  bool throttled_for_global_reasons = CanThrottleRendering() &&
+                                      frame_->GetDocument() &&
+                                      Lifecycle().ThrottlingAllowed();
+  if (!throttled_for_global_reasons)
+    return false;
+
+  // Only lifecycle phases up to layout are needed to generate an
+  // intersection observation.
+  return !needs_intersection_observation_ ||
+         GetFrame().LocalFrameRoot().View()->past_layout_lifecycle_update_;
 }
 
 bool LocalFrameView::CanThrottleRendering() const {
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.h b/third_party/WebKit/Source/core/frame/LocalFrameView.h
index 827c27a..75319ca 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.h
@@ -182,6 +182,10 @@
   void SetNeedsUpdateGeometries() { needs_update_geometries_ = true; }
   void UpdateGeometry() override;
 
+  // Marks this frame, and ancestor frames, as needing one intersection
+  // observervation. This overrides throttling for one frame.
+  void SetNeedsIntersectionObservation();
+
   // Methods for getting/setting the size Blink should use to layout the
   // contents.
   // NOTE: Scrollbar exclusion is based on the LocalFrameView's scrollbars. To
@@ -1181,6 +1185,7 @@
   // This is set on the local root frame view only.
   DocumentLifecycle::LifecycleState
       current_update_lifecycle_phases_target_state_;
+  bool past_layout_lifecycle_update_;
 
   ScrollAnchor scroll_anchor_;
   using AnchoringAdjustmentQueue =
@@ -1194,6 +1199,7 @@
   bool suppress_adjust_view_size_;
   bool allows_layout_invalidation_after_layout_clean_;
   bool forcing_layout_parent_view_;
+  bool needs_intersection_observation_;
 
   Member<ElementVisibilityObserver> visibility_observer_;
 
diff --git a/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp
index eab267d..d205f37 100644
--- a/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp
@@ -35,7 +35,6 @@
 #include "build/build_config.h"
 #include "core/animation/CompositorMutatorImpl.h"
 #include "core/dom/UserGestureIndicator.h"
-#include "core/editing/CompositionUnderlineVectorBuilder.h"
 #include "core/editing/EditingUtilities.h"
 #include "core/editing/Editor.h"
 #include "core/editing/FrameSelection.h"
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
index e652c8c5..4aebd03 100644
--- a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
@@ -109,11 +109,11 @@
 #include "core/dom/NodeTraversal.h"
 #include "core/dom/ShadowRoot.h"
 #include "core/dom/UserGestureIndicator.h"
-#include "core/editing/CompositionUnderlineVectorBuilder.h"
 #include "core/editing/EditingUtilities.h"
 #include "core/editing/Editor.h"
 #include "core/editing/FindInPageCoordinates.h"
 #include "core/editing/FrameSelection.h"
+#include "core/editing/ImeTextSpanVectorBuilder.h"
 #include "core/editing/InputMethodController.h"
 #include "core/editing/PlainTextRange.h"
 #include "core/editing/SetSelectionData.h"
@@ -954,7 +954,7 @@
 void WebLocalFrameImpl::SetMarkedText(const WebString& text,
                                       unsigned location,
                                       unsigned length) {
-  Vector<CompositionUnderline> decorations;
+  Vector<ImeTextSpan> decorations;
   GetFrame()->GetInputMethodController().SetComposition(text, decorations,
                                                         location, length);
 }
@@ -1289,7 +1289,7 @@
 bool WebLocalFrameImpl::SetCompositionFromExistingText(
     int composition_start,
     int composition_end,
-    const WebVector<WebCompositionUnderline>& underlines) {
+    const WebVector<WebImeTextSpan>& ime_text_spans) {
   TRACE_EVENT0("blink", "WebLocalFrameImpl::setCompositionFromExistingText");
   if (!GetFrame()->GetEditor().CanEdit())
     return false;
@@ -1302,7 +1302,7 @@
   GetFrame()->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets();
 
   input_method_controller.SetCompositionFromExistingText(
-      CompositionUnderlineVectorBuilder::Build(underlines), composition_start,
+      ImeTextSpanVectorBuilder::Build(ime_text_spans), composition_start,
       composition_end);
 
   return true;
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h
index 9cc9467e..548d045 100644
--- a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h
@@ -203,7 +203,7 @@
   bool SetCompositionFromExistingText(
       int composition_start,
       int composition_end,
-      const WebVector<WebCompositionUnderline>& underlines) override;
+      const WebVector<WebImeTextSpan>& ime_text_spans) override;
   void ExtendSelectionAndDelete(int before, int after) override;
   void DeleteSurroundingText(int before, int after) override;
   void DeleteSurroundingTextInCodePoints(int before, int after) override;
diff --git a/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.cpp b/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.cpp
index cc56c1e..bb40501a 100644
--- a/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.cpp
+++ b/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.cpp
@@ -137,12 +137,8 @@
 BackgroundHTMLParser::~BackgroundHTMLParser() {}
 
 void BackgroundHTMLParser::AppendRawBytesFromMainThread(
-    std::unique_ptr<Vector<char>> buffer,
-    double bytes_received_time) {
+    std::unique_ptr<Vector<char>> buffer) {
   DCHECK(decoder_);
-  DEFINE_STATIC_LOCAL(CustomCountHistogram, queue_delay,
-                      ("Parser.AppendBytesDelay", 1, 5000, 50));
-  queue_delay.Count(MonotonicallyIncreasingTimeMS() - bytes_received_time);
   UpdateDocument(decoder_->Decode(buffer->data(), buffer->size()));
 }
 
diff --git a/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.h b/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.h
index bedb00d2..b67a00a 100644
--- a/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.h
+++ b/third_party/WebKit/Source/core/html/parser/BackgroundHTMLParser.h
@@ -89,8 +89,7 @@
     String unparsed_input;
   };
 
-  void AppendRawBytesFromMainThread(std::unique_ptr<Vector<char>>,
-                                    double bytes_received_time);
+  void AppendRawBytesFromMainThread(std::unique_ptr<Vector<char>>);
   void SetDecoder(std::unique_ptr<TextResourceDecoder>);
   void Flush();
   void ResumeFrom(std::unique_ptr<Checkpoint>);
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
index 4a3dde3..f4243eb 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
@@ -181,18 +181,6 @@
 }
 
 void HTMLDocumentParser::Detach() {
-  if (!IsParsingFragment() && tokenized_chunk_queue_.Get() &&
-      tokenized_chunk_queue_->PeakPendingChunkCount()) {
-    DEFINE_STATIC_LOCAL(CustomCountHistogram, peak_pending_chunk_histogram,
-                        ("Parser.PeakPendingChunkCount", 1, 1000, 50));
-    peak_pending_chunk_histogram.Count(
-        tokenized_chunk_queue_->PeakPendingChunkCount());
-    DEFINE_STATIC_LOCAL(CustomCountHistogram, peak_pending_token_histogram,
-                        ("Parser.PeakPendingTokenCount", 1, 100000, 50));
-    peak_pending_token_histogram.Count(
-        tokenized_chunk_queue_->PeakPendingTokenCount());
-  }
-
   if (have_background_parser_)
     StopBackgroundParser();
   DocumentParser::Detach();
@@ -1186,7 +1174,6 @@
     return;
 
   if (ShouldUseThreading()) {
-    double bytes_received_time = MonotonicallyIncreasingTimeMS();
     if (!have_background_parser_)
       StartBackgroundParser();
 
@@ -1199,8 +1186,7 @@
     loading_task_runner_->PostTask(
         BLINK_FROM_HERE,
         WTF::Bind(&BackgroundHTMLParser::AppendRawBytesFromMainThread,
-                  background_parser_, WTF::Passed(std::move(buffer)),
-                  bytes_received_time));
+                  background_parser_, WTF::Passed(std::move(buffer))));
     return;
   }
 
diff --git a/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.cpp b/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.cpp
index dac3543..528ecd3 100644
--- a/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.cpp
+++ b/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.cpp
@@ -16,20 +16,12 @@
 
 bool TokenizedChunkQueue::Enqueue(
     std::unique_ptr<HTMLDocumentParser::TokenizedChunk> chunk) {
-  pending_token_count_ += chunk->tokens->size();
-  peak_pending_token_count_ =
-      std::max(peak_pending_token_count_, pending_token_count_);
-
   bool was_empty = pending_chunks_.IsEmpty();
   pending_chunks_.push_back(std::move(chunk));
-  peak_pending_chunk_count_ =
-      std::max(peak_pending_chunk_count_, pending_chunks_.size());
-
   return was_empty;
 }
 
 void TokenizedChunkQueue::Clear() {
-  pending_token_count_ = 0;
   pending_chunks_.clear();
 }
 
@@ -39,12 +31,4 @@
   pending_chunks_.swap(vector);
 }
 
-size_t TokenizedChunkQueue::PeakPendingChunkCount() {
-  return peak_pending_chunk_count_;
-}
-
-size_t TokenizedChunkQueue::PeakPendingTokenCount() {
-  return peak_pending_token_count_;
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h b/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h
index e84cd88..b7080a4 100644
--- a/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h
+++ b/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h
@@ -35,16 +35,11 @@
   void Clear();
 
   void TakeAll(Vector<std::unique_ptr<HTMLDocumentParser::TokenizedChunk>>&);
-  size_t PeakPendingChunkCount();
-  size_t PeakPendingTokenCount();
 
  private:
   TokenizedChunkQueue();
 
   Vector<std::unique_ptr<HTMLDocumentParser::TokenizedChunk>> pending_chunks_;
-  size_t peak_pending_chunk_count_ = 0;
-  size_t peak_pending_token_count_ = 0;
-  size_t pending_token_count_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/input/ScrollManager.cpp b/third_party/WebKit/Source/core/input/ScrollManager.cpp
index ec6c58a..218f6c69 100644
--- a/third_party/WebKit/Source/core/input/ScrollManager.cpp
+++ b/third_party/WebKit/Source/core/input/ScrollManager.cpp
@@ -591,9 +591,6 @@
 
   if (scrollbar) {
     bool should_update_capture = false;
-    // scrollbar->gestureEvent always returns true for touchpad based GSB
-    // events. Therefore, while mouse is over a fully scrolled scrollbar, GSB
-    // won't propagate to the next scrollable layer.
     if (scrollbar->GestureEvent(gesture_event, &should_update_capture)) {
       if (should_update_capture)
         scrollbar_handling_scroll_gesture_ = scrollbar;
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index b6ed8c1..1a922071 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -1096,8 +1096,8 @@
                     { "name": "securityState", "$ref": "SecurityState", "description": "Security state representing the severity of the factor being explained." },
                     { "name": "summary", "type": "string", "description": "Short phrase describing the type of factor." },
                     { "name": "description", "type": "string", "description": "Full text explanation of the factor." },
-                    { "name": "hasCertificate", "type": "boolean", "description": "True if the page has a certificate." },
-                    { "name": "mixedContentType", "$ref": "MixedContentType", "description": "The type of mixed content described by the explanation." }
+                    { "name": "mixedContentType", "$ref": "MixedContentType", "description": "The type of mixed content described by the explanation." },
+                    { "name": "certificate", "type": "array", "items": { "type": "string" }, "description": "Page certificate." }
                 ],
                 "description": "An explanation of an factor contributing to the security state."
             },
@@ -1132,10 +1132,6 @@
                 "description": "Disables tracking security state changes."
             },
             {
-                "name": "showCertificateViewer",
-                "description": "Displays native dialog with the certificate details."
-            },
-            {
                 "name": "handleCertificateError",
                 "description": "Handles a certificate error that fired a certificateError event.",
                 "parameters": [
@@ -4970,6 +4966,17 @@
                 ]
             },
             {
+                "name": "getVersion",
+                "description": "Returns version information.",
+                "returns": [
+                    { "name": "protocolVersion", "type": "string", "description": "Protocol version." },
+                    { "name": "product", "type": "string", "description": "Product name." },
+                    { "name": "revision", "type": "string", "description": "Product revision." },
+                    { "name": "userAgent", "type": "string", "description": "User-Agent." },
+                    { "name": "jsVersion", "type": "string", "description": "V8 version." }
+                ]
+            },
+            {
                 "name": "setWindowBounds",
                 "description": "Set position and/or size of the browser window.",
                 "parameters": [
diff --git a/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp b/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp
index e075155..69643fc 100644
--- a/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp
+++ b/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp
@@ -257,8 +257,12 @@
       new IntersectionObservation(*this, *target);
   target->EnsureIntersectionObserverData().AddObservation(*observation);
   observations_.insert(observation);
-  if (LocalFrameView* frame_view = target_frame->View())
+  if (LocalFrameView* frame_view = target_frame->View()) {
+    // The IntersectionObsever spec requires that at least one observation
+    // be recorded afer observe() is called, even if the frame is throttled.
+    frame_view->SetNeedsIntersectionObservation();
     frame_view->ScheduleAnimation();
+  }
 }
 
 void IntersectionObserver::unobserve(Element* target,
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index ca234d0d..68a3782 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -190,8 +190,7 @@
     // to the canvas. Just dirty the entire canvas when our style changes
     // substantially.
     if ((diff.NeedsFullPaintInvalidation() || diff.NeedsLayout()) &&
-        GetNode() &&
-        (isHTMLHtmlElement(*GetNode()) || isHTMLBodyElement(*GetNode()))) {
+        GetNode() && (IsDocumentElement() || isHTMLBodyElement(*GetNode()))) {
       View()->SetShouldDoFullPaintInvalidation();
 
       if (old_style->HasEntirelyFixedBackground() !=
diff --git a/third_party/WebKit/Source/core/layout/LayoutText.cpp b/third_party/WebKit/Source/core/layout/LayoutText.cpp
index cf3b4cf..bb6974e 100644
--- a/third_party/WebKit/Source/core/layout/LayoutText.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutText.cpp
@@ -1634,12 +1634,7 @@
       revealed_text = text_[last_typed_character_offset_to_reveal];
   }
 
-  // Replace all grapheme clusters in the text with the mask character.
-  size_t length = NumGraphemeClusters(text_);
-  CHECK_LE(length, text_.length());
-  text_.Truncate(length);
   text_.Fill(mask);
-
   if (last_typed_character_offset_to_reveal >= 0) {
     text_.replace(last_typed_character_offset_to_reveal, 1,
                   String(&revealed_text, 1));
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index 276b2247..a7d0696 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -36,10 +36,10 @@
   bool should_clear_left = (clear == EClear::kBoth || clear == EClear::kLeft);
   bool should_clear_right = (clear == EClear::kBoth || clear == EClear::kRight);
 
-  if (exclusions.last_left_float && should_clear_left)
+  if (exclusions.float_left_clear_offset && should_clear_left)
     return true;
 
-  if (exclusions.last_right_float && should_clear_right)
+  if (exclusions.float_right_clear_offset && should_clear_right)
     return true;
 
   auto should_clear_pred =
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc
index 444e00eb..dd32629 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.cc
@@ -49,37 +49,27 @@
   return stream << value.ToString();
 }
 
-NGExclusions::NGExclusions()
-    : last_left_float(nullptr), last_right_float(nullptr) {}
-
-NGExclusions::NGExclusions(const NGExclusions& other) {
-  for (const auto& exclusion : other.storage)
-    Add(*exclusion);
-}
-
 void NGExclusions::Add(const NGExclusion& exclusion) {
-  storage.push_back(WTF::MakeUnique<NGExclusion>(exclusion));
-  if (exclusion.type == NGExclusion::kFloatLeft) {
-    last_left_float = storage.rbegin()->get();
-  } else if (exclusion.type == NGExclusion::kFloatRight) {
-    last_right_float = storage.rbegin()->get();
-  }
-}
+  storage.push_back(exclusion);
+  last_float_block_start =
+      std::max(last_float_block_start, exclusion.rect.BlockStartOffset());
 
-inline NGExclusions& NGExclusions::operator=(const NGExclusions& other) {
-  storage.clear();
-  last_left_float = nullptr;
-  last_right_float = nullptr;
-  for (const auto& exclusion : other.storage)
-    Add(*exclusion);
-  return *this;
+  if (exclusion.type == NGExclusion::kFloatLeft) {
+    float_left_clear_offset =
+        std::max(float_left_clear_offset.value_or(LayoutUnit::Min()),
+                 exclusion.rect.BlockEndOffset());
+  } else if (exclusion.type == NGExclusion::kFloatRight) {
+    float_right_clear_offset =
+        std::max(float_right_clear_offset.value_or(LayoutUnit::Min()),
+                 exclusion.rect.BlockEndOffset());
+  }
 }
 
 bool NGExclusions::operator==(const NGExclusions& other) const {
   if (storage.size() != other.storage.size())
     return false;
   for (size_t i = 0; i < storage.size(); ++i) {
-    if (*storage[i] != *other.storage[i])
+    if (storage[i] != other.storage[i])
       return false;
   }
   return true;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h
index 9466dfb..465e1f87 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_exclusion.h
@@ -7,6 +7,7 @@
 
 #include "core/CoreExport.h"
 #include "core/layout/ng/geometry/ng_logical_rect.h"
+#include "platform/wtf/Optional.h"
 #include "platform/wtf/Vector.h"
 
 namespace blink {
@@ -56,17 +57,18 @@
 CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGExclusion&);
 
 struct CORE_EXPORT NGExclusions {
-  NGExclusions();
-  NGExclusions(const NGExclusions& other);
+  Vector<NGExclusion> storage;
 
-  Vector<std::unique_ptr<const NGExclusion>> storage;
+  // This member is used for implementing the "top edge alignment rule" for
+  // floats. Floats can be positioned at negative offsets, hence is initialized
+  // the minimum value.
+  LayoutUnit last_float_block_start = LayoutUnit::Min();
 
-  // Last left/right float exclusions are used to enforce the top edge alignment
-  // rule for floats and for the support of CSS "clear" property.
-  const NGExclusion* last_left_float;   // Owned by storage.
-  const NGExclusion* last_right_float;  // Owned by storage.
+  // These members are used for keeping track of the "lowest" offset for each
+  // type of float. This is used for implementing float clearance.
+  Optional<LayoutUnit> float_left_clear_offset;
+  Optional<LayoutUnit> float_right_clear_offset;
 
-  NGExclusions& operator=(const NGExclusions& other);
   bool operator==(const NGExclusions& other) const;
   bool operator!=(const NGExclusions& other) const { return !(*this == other); }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
index b222f935..10fe94c8 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
@@ -22,15 +22,9 @@
 NGLogicalOffset AdjustToTopEdgeAlignmentRule(const NGConstraintSpace& space,
                                              const NGLogicalOffset& offset) {
   NGLogicalOffset adjusted_offset = offset;
-  LayoutUnit& adjusted_block_offset = adjusted_offset.block_offset;
-  if (space.Exclusions()->last_left_float)
-    adjusted_block_offset =
-        std::max(adjusted_block_offset,
-                 space.Exclusions()->last_left_float->rect.BlockStartOffset());
-  if (space.Exclusions()->last_right_float)
-    adjusted_block_offset =
-        std::max(adjusted_block_offset,
-                 space.Exclusions()->last_right_float->rect.BlockStartOffset());
+  adjusted_offset.block_offset = std::max(
+      adjusted_offset.block_offset, space.Exclusions()->last_float_block_start);
+
   return adjusted_offset;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
index 3ae30d0..5f9ab6d 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
@@ -228,10 +228,9 @@
 }
 
 // Compares exclusions by their top position.
-bool CompareNGExclusionsByTopAsc(
-    const std::unique_ptr<const NGExclusion>& lhs,
-    const std::unique_ptr<const NGExclusion>& rhs) {
-  return rhs->rect.offset.block_offset > lhs->rect.offset.block_offset;
+bool CompareNGExclusionsByTopAsc(const NGExclusion& lhs,
+                                 const NGExclusion& rhs) {
+  return rhs.rect.offset.block_offset > lhs.rect.offset.block_offset;
 }
 
 // Compares Layout Opportunities by Start Point.
@@ -294,14 +293,12 @@
 
   NGLayoutOpportunity initial_opportunity =
       CreateInitialOpportunity(available_size, Offset());
-  opportunity_tree_root_.reset(
-      new NGLayoutOpportunityTreeNode(initial_opportunity));
 
+  NGLayoutOpportunityTreeNode tree(initial_opportunity);
   for (const auto& exclusion : exclusions->storage) {
-    InsertExclusion(MutableOpportunityTreeRoot(), exclusion.get(),
-                    opportunities_);
+    InsertExclusion(&tree, &exclusion, opportunities_);
   }
-  CollectAllOpportunities(OpportunityTreeRoot(), opportunities_);
+  CollectAllOpportunities(&tree, opportunities_);
   std::sort(opportunities_.begin(), opportunities_.end(),
             &CompareNGLayoutOpportunitesByStartPoint);
 
@@ -316,13 +313,4 @@
   return NGLayoutOpportunity(*opportunity);
 }
 
-#ifndef NDEBUG
-void NGLayoutOpportunityIterator::ShowLayoutOpportunityTree() const {
-  StringBuilder string_builder;
-  string_builder.Append("\n.:: LayoutOpportunity Tree ::.\n\nRoot Node: ");
-  AppendNodeToString(opportunity_tree_root_.get(), &string_builder);
-  fprintf(stderr, "%s\n", string_builder.ToString().Utf8().data());
-}
-#endif
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
index 6144fa7..41e19d9 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
@@ -25,6 +25,8 @@
     const NGLogicalSize& size);
 
 class CORE_EXPORT NGLayoutOpportunityIterator final {
+  STACK_ALLOCATED();
+
  public:
   // Default constructor.
   //
@@ -59,19 +61,8 @@
 #endif
 
  private:
-  // Mutable Getters.
-  NGLayoutOpportunityTreeNode* MutableOpportunityTreeRoot() {
-    return opportunity_tree_root_.get();
-  }
-
-  // Read-only Getters.
-  const NGLayoutOpportunityTreeNode* OpportunityTreeRoot() const {
-    return opportunity_tree_root_.get();
-  }
-
   NGLayoutOpportunities opportunities_;
   NGLayoutOpportunities::const_iterator opportunity_iter_;
-  std::unique_ptr<NGLayoutOpportunityTreeNode> opportunity_tree_root_;
   NGLogicalOffset offset_;
 };
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc
index f3cf053..382df0a 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc
@@ -12,50 +12,28 @@
 #include "core/layout/ng/ng_writing_mode.h"
 
 namespace blink {
-namespace {
 
-// Returns max of 2 {@code WTF::Optional} values.
-template <typename T>
-WTF::Optional<T> OptionalMax(const WTF::Optional<T>& value1,
-                             const WTF::Optional<T>& value2) {
-  if (value1 && value2) {
-    return std::max(value1.value(), value2.value());
-  } else if (value1) {
-    return value1;
-  }
-  return value2;
-}
-
-}  // namespace
-
-WTF::Optional<LayoutUnit> GetClearanceOffset(
-    const std::shared_ptr<NGExclusions>& exclusions,
-    EClear clear_type) {
-  const NGExclusion* right_exclusion = exclusions->last_right_float;
-  const NGExclusion* left_exclusion = exclusions->last_left_float;
-
-  WTF::Optional<LayoutUnit> left_offset;
-  if (left_exclusion) {
-    left_offset = left_exclusion->rect.BlockEndOffset();
-  }
-  WTF::Optional<LayoutUnit> right_offset;
-  if (right_exclusion) {
-    right_offset = right_exclusion->rect.BlockEndOffset();
-  }
+LayoutUnit GetClearanceOffset(const std::shared_ptr<NGExclusions>& exclusions,
+                              EClear clear_type) {
+  LayoutUnit float_left_clear_offset =
+      exclusions->float_left_clear_offset.value_or(LayoutUnit::Min());
+  LayoutUnit float_right_clear_offset =
+      exclusions->float_right_clear_offset.value_or(LayoutUnit::Min());
 
   switch (clear_type) {
     case EClear::kNone:
-      return WTF::nullopt;  // nothing to do here.
+      return LayoutUnit::Min();  // nothing to do here.
     case EClear::kLeft:
-      return left_offset;
+      return float_left_clear_offset;
     case EClear::kRight:
-      return right_offset;
+      return float_right_clear_offset;
     case EClear::kBoth:
-      return OptionalMax<LayoutUnit>(left_offset, right_offset);
+      return std::max(float_left_clear_offset, float_right_clear_offset);
     default:
       NOTREACHED();
   }
-  return WTF::nullopt;
+
+  return LayoutUnit::Min();
 }
 
 bool ShouldShrinkToFit(const ComputedStyle& parent_style,
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_space_utils.h b/third_party/WebKit/Source/core/layout/ng/ng_space_utils.h
index c2c9737..1217b4f 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_space_utils.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_space_utils.h
@@ -18,9 +18,9 @@
 
 // Gets the clearance offset based on the provided {@code clear_type} and list
 // of exclusions that represent left/right float.
-CORE_EXPORT WTF::Optional<LayoutUnit> GetClearanceOffset(
-    const std::shared_ptr<NGExclusions>& exclusions,
-    EClear clear_type);
+CORE_EXPORT LayoutUnit
+GetClearanceOffset(const std::shared_ptr<NGExclusions>& exclusions,
+                   EClear clear_type);
 
 // Whether child's constraint space should shrink to its intrinsic width.
 // This is needed for buttons, select, input, floats and orthogonal children.
diff --git a/third_party/WebKit/Source/core/paint/EmbeddedContentPainter.cpp b/third_party/WebKit/Source/core/paint/EmbeddedContentPainter.cpp
index 8ebba762..a260c8e 100644
--- a/third_party/WebKit/Source/core/paint/EmbeddedContentPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/EmbeddedContentPainter.cpp
@@ -62,10 +62,6 @@
     return;
 
   if (layout_embedded_content_.GetEmbeddedContentView()) {
-    // TODO(schenney) crbug.com/93805 Speculative release assert to verify that
-    // the crashes we see in EmbeddedContentView painting are due to a destroyed
-    // LayoutEmbeddedContent object.
-    CHECK(layout_embedded_content_.GetNode());
     Optional<RoundedInnerRectClipper> clipper;
     if (layout_embedded_content_.Style()->HasBorderRadius()) {
       if (border_rect.IsEmpty())
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index abef542..48f1dd9 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -1973,10 +1973,6 @@
     needs_composited_scrolling = false;
   }
 
-  // TODO(schenney) Tests fail if we do not also exclude
-  // layer->layoutObject().style()->hasBorderDecoration() (missing background
-  // behind dashed borders). Resolve this case, or not, and update this check
-  // with the results.
   if (layer->GetLayoutObject().Style()->HasBorderRadius()) {
     non_composited_main_thread_scrolling_reasons_ |=
         MainThreadScrollingReason::kHasBorderRadius;
diff --git a/third_party/WebKit/Source/core/paint/compositing/PaintLayerCompositor.h b/third_party/WebKit/Source/core/paint/compositing/PaintLayerCompositor.h
index bc67cd7..f1cf7baa 100644
--- a/third_party/WebKit/Source/core/paint/compositing/PaintLayerCompositor.h
+++ b/third_party/WebKit/Source/core/paint/compositing/PaintLayerCompositor.h
@@ -27,6 +27,7 @@
 #define PaintLayerCompositor_h
 
 #include <memory>
+#include "base/gtest_prod_util.h"
 #include "core/CoreExport.h"
 #include "core/dom/DocumentLifecycle.h"
 #include "core/paint/compositing/CompositingReasonFinder.h"
@@ -298,6 +299,9 @@
   std::unique_ptr<GraphicsLayer> layer_for_horizontal_scrollbar_;
   std::unique_ptr<GraphicsLayer> layer_for_vertical_scrollbar_;
   std::unique_ptr<GraphicsLayer> layer_for_scroll_corner_;
+
+  FRIEND_TEST_ALL_PREFIXES(FrameThrottlingTest,
+                           IntersectionObservationOverridesThrottling);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/scheduler/FrameThrottlingTest.cpp b/third_party/WebKit/Source/core/scheduler/FrameThrottlingTest.cpp
index 8db9bfb..49c5c1e 100644
--- a/third_party/WebKit/Source/core/scheduler/FrameThrottlingTest.cpp
+++ b/third_party/WebKit/Source/core/scheduler/FrameThrottlingTest.cpp
@@ -189,6 +189,70 @@
   EXPECT_TRUE(inner_frame_document->View()->CanThrottleRendering());
 }
 
+TEST_P(FrameThrottlingTest, IntersectionObservationOverridesThrottling) {
+  // Create a document with doubly nested iframes.
+  SimRequest main_resource("https://example.com/", "text/html");
+  SimRequest frame_resource("https://example.com/iframe.html", "text/html");
+
+  LoadURL("https://example.com/");
+  main_resource.Complete("<iframe id=frame src=iframe.html></iframe>");
+  frame_resource.Complete("<iframe id=innerFrame sandbox></iframe>");
+
+  auto* frame_element =
+      toHTMLIFrameElement(GetDocument().getElementById("frame"));
+  auto* frame_document = frame_element->contentDocument();
+
+  auto* inner_frame_element =
+      toHTMLIFrameElement(frame_document->getElementById("innerFrame"));
+  auto* inner_frame_document = inner_frame_element->contentDocument();
+
+  DocumentLifecycle::AllowThrottlingScope throttling_scope(
+      GetDocument().Lifecycle());
+
+  // Hidden cross origin frames are throttled.
+  frame_element->setAttribute(styleAttr, "transform: translateY(480px)");
+  CompositeFrame();
+  EXPECT_FALSE(GetDocument().View()->CanThrottleRendering());
+  EXPECT_FALSE(frame_document->View()->CanThrottleRendering());
+  EXPECT_TRUE(inner_frame_document->View()->ShouldThrottleRendering());
+
+  // An intersection observation overrides...
+  inner_frame_document->View()->SetNeedsIntersectionObservation();
+  EXPECT_FALSE(inner_frame_document->View()->ShouldThrottleRendering());
+  inner_frame_document->View()->ScheduleAnimation();
+
+  LayoutView* inner_view = inner_frame_document->View()->GetLayoutView();
+
+  inner_view->SetNeedsLayout("test");
+  inner_view->Compositor()->SetNeedsCompositingUpdate(
+      kCompositingUpdateRebuildTree);
+  inner_view->SetShouldDoFullPaintInvalidation(
+      PaintInvalidationReason::kForTesting);
+  inner_view->Layer()->SetNeedsRepaint();
+  EXPECT_FALSE(inner_frame_document->View()
+                   ->GetLayoutView()
+                   ->FullPaintInvalidationReason() ==
+               PaintInvalidationReason::kNone);
+  inner_view->Compositor()->SetNeedsCompositingUpdate(
+      kCompositingUpdateRebuildTree);
+  EXPECT_EQ(kCompositingUpdateRebuildTree,
+            inner_view->Compositor()->pending_update_type_);
+  EXPECT_TRUE(inner_view->Layer()->NeedsRepaint());
+
+  CompositeFrame();
+  // ...but only for one frame.
+  EXPECT_TRUE(inner_frame_document->View()->ShouldThrottleRendering());
+
+  EXPECT_FALSE(inner_view->NeedsLayout());
+  EXPECT_FALSE(inner_frame_document->View()
+                   ->GetLayoutView()
+                   ->FullPaintInvalidationReason() ==
+               PaintInvalidationReason::kNone);
+  EXPECT_EQ(kCompositingUpdateRebuildTree,
+            inner_view->Compositor()->pending_update_type_);
+  EXPECT_TRUE(inner_view->Layer()->NeedsRepaint());
+}
+
 TEST_P(FrameThrottlingTest, HiddenCrossOriginZeroByZeroFramesAreNotThrottled) {
   // Create a document with doubly nested iframes.
   SimRequest main_resource("https://example.com/", "text/html");
diff --git a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.cpp b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.cpp
index 597b765..80eb269 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.cpp
@@ -11,23 +11,23 @@
 namespace blink {
 
 PerformanceServerTiming::PerformanceServerTiming(
-    const String& metric,
-    double value,
+    const String& name,
+    double duration,
     const String& description,
     ShouldAllowTimingDetails shouldAllowTimingDetails)
-    : metric_(metric),
-      value_(value),
+    : name_(name),
+      duration_(duration),
       description_(description),
       shouldAllowTimingDetails_(shouldAllowTimingDetails) {}
 
 PerformanceServerTiming::~PerformanceServerTiming() {}
 
-String PerformanceServerTiming::metric() const {
-  return metric_;
+String PerformanceServerTiming::name() const {
+  return name_;
 }
 
-double PerformanceServerTiming::value() const {
-  return shouldAllowTimingDetails_ == ShouldAllowTimingDetails::Yes ? value_
+double PerformanceServerTiming::duration() const {
+  return shouldAllowTimingDetails_ == ShouldAllowTimingDetails::Yes ? duration_
                                                                     : 0.0;
 }
 
@@ -40,8 +40,8 @@
 ScriptValue PerformanceServerTiming::toJSONForBinding(
     ScriptState* script_state) const {
   V8ObjectBuilder builder(script_state);
-  builder.AddString("metric", metric());
-  builder.AddNumber("value", value());
+  builder.AddString("name", name());
+  builder.AddNumber("duration", duration());
   builder.AddString("description", description());
   return builder.GetScriptValue();
 }
@@ -56,7 +56,7 @@
         response.HttpHeaderField(HTTPNames::Server_Timing));
     for (const auto& header : *headers) {
       entries.push_back(new PerformanceServerTiming(
-          header->metric, header->value, header->description,
+          header->name, header->duration, header->description,
           shouldAllowTimingDetails));
     }
   }
diff --git a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.h b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.h
index 815bb363..9f0a2b49 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.h
+++ b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.h
@@ -28,14 +28,14 @@
     No,
   };
 
-  PerformanceServerTiming(const String& metric,
-                          double value,
+  PerformanceServerTiming(const String& name,
+                          double duration,
                           const String& description,
                           ShouldAllowTimingDetails);
   ~PerformanceServerTiming();
 
-  String metric() const;
-  double value() const;
+  String name() const;
+  double duration() const;
   String description() const;
 
   static PerformanceServerTimingVector ParseServerTiming(
@@ -47,8 +47,8 @@
   DEFINE_INLINE_VIRTUAL_TRACE() {}
 
  private:
-  const String metric_;
-  double value_;
+  const String name_;
+  double duration_;
   const String description_;
   ShouldAllowTimingDetails shouldAllowTimingDetails_;
 };
diff --git a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl
index fd4069e..5026f3a 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl
+++ b/third_party/WebKit/Source/core/timing/PerformanceServerTiming.idl
@@ -7,7 +7,7 @@
 [
     RuntimeEnabled=ServerTiming
 ] interface PerformanceServerTiming {
-    readonly attribute DOMString metric;
-    readonly attribute double value;
+    readonly attribute DOMString name;
+    readonly attribute DOMHighResTimeStamp duration;
     readonly attribute DOMString description;
 };
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index 0ffc52c..34355b7 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -19,6 +19,8 @@
   "front_end/accessibility/axBreadcrumbs.css",
   "front_end/accessibility/AXBreadcrumbsPane.js",
   "front_end/accessibility/module.json",
+  "front_end/accessibility_test_runner/AccessibilityPaneTestRunner.js",
+  "front_end/accessibility_test_runner/module.json",
   "front_end/animation/AnimationGroupPreviewUI.js",
   "front_end/animation/AnimationModel.js",
   "front_end/animation/animationScreenshotPopover.css",
@@ -27,6 +29,13 @@
   "front_end/animation/AnimationTimeline.js",
   "front_end/animation/AnimationUI.js",
   "front_end/animation/module.json",
+  "front_end/application_test_runner/AppcacheTestRunner.js",
+  "front_end/application_test_runner/CacheStorageTestRunner.js",
+  "front_end/application_test_runner/IndexedDBTestRunner.js",
+  "front_end/application_test_runner/module.json",
+  "front_end/application_test_runner/ResourcesTestRunner.js",
+  "front_end/application_test_runner/ResourceTreeTestRunner.js",
+  "front_end/application_test_runner/ServiceWorkersTestRunner.js",
   "front_end/audits/AuditCategories.js",
   "front_end/audits/AuditCategory.js",
   "front_end/audits/AuditController.js",
@@ -54,6 +63,8 @@
   "front_end/audits2/lighthouse/renderer/report-renderer.js",
   "front_end/audits2/lighthouse/renderer/util.js",
   "front_end/audits2/module.json",
+  "front_end/audits_test_runner/AuditsTestRunner.js",
+  "front_end/audits_test_runner/module.json",
   "front_end/bindings/BlackboxManager.js",
   "front_end/bindings/BreakpointManager.js",
   "front_end/bindings/CompilerScriptMapping.js",
@@ -72,6 +83,11 @@
   "front_end/bindings/SASSSourceMapping.js",
   "front_end/bindings/StylesSourceMapping.js",
   "front_end/bindings/TempFile.js",
+  "front_end/bindings_test_runner/AutomappingTestRunner.js",
+  "front_end/bindings_test_runner/BindingsTestRunner.js",
+  "front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js",
+  "front_end/bindings_test_runner/module.json",
+  "front_end/bindings_test_runner/PersistenceTestRunner.js",
   "front_end/changes/ChangesHighlighter.js",
   "front_end/changes/changesView.css",
   "front_end/changes/ChangesView.js",
@@ -163,6 +179,8 @@
   "front_end/coverage/coverageView.css",
   "front_end/coverage/CoverageView.js",
   "front_end/coverage/module.json",
+  "front_end/coverage_test_runner/CoverageTestRunner.js",
+  "front_end/coverage_test_runner/module.json",
   "front_end/data_grid/dataGrid.css",
   "front_end/data_grid/DataGrid.js",
   "front_end/data_grid/module.json",
@@ -171,6 +189,8 @@
   "front_end/data_grid/ViewportDataGrid.js",
   "front_end/data_grid_test_runner/DataGridTestRunner.js",
   "front_end/data_grid_test_runner/module.json",
+  "front_end/device_mode_test_runner/DeviceModeTestRunner.js",
+  "front_end/device_mode_test_runner/module.json",
   "front_end/devices/devicesView.css",
   "front_end/devices/DevicesView.js",
   "front_end/devices/module.json",
@@ -209,6 +229,11 @@
   "front_end/elements/stylesSectionTree.css",
   "front_end/elements/stylesSidebarPane.css",
   "front_end/elements/StylesSidebarPane.js",
+  "front_end/elements_test_runner/EditDOMTestRunner.js",
+  "front_end/elements_test_runner/ElementsPanelShadowSelectionOnRefreshTestRunner.js",
+  "front_end/elements_test_runner/ElementsTestRunner.js",
+  "front_end/elements_test_runner/SetOuterHTMLTestRunner.js",
+  "front_end/elements_test_runner/StylesUpdateLinksTestRunner.js",
   "front_end/emulated_devices/module.json",
   "front_end/emulation/AdvancedApp.js",
   "front_end/emulation/DeviceModeModel.js",
@@ -238,6 +263,9 @@
   "front_end/extensions/ExtensionServer.js",
   "front_end/extensions/ExtensionView.js",
   "front_end/extensions/module.json",
+  "front_end/extensions_test_runner/ExtensionsNetworkTestRunner.js",
+  "front_end/extensions_test_runner/ExtensionsTestRunner.js",
+  "front_end/extensions_test_runner/module.json",
   "front_end/formatter/FormatterWorkerPool.js",
   "front_end/formatter/module.json",
   "front_end/formatter/ScriptFormatter.js",
@@ -303,6 +331,8 @@
   "front_end/integration_test_runner.json",
   "front_end/integration_test_runner/IntegrationTestRunner.js",
   "front_end/integration_test_runner/module.json",
+  "front_end/integration_test_runner/PageMockTestRunner.js",
+  "front_end/integration_test_runner/SyntaxHighlightTestRunner.js",
   "front_end/layer_viewer/layerDetailsView.css",
   "front_end/layer_viewer/LayerDetailsView.js",
   "front_end/layer_viewer/layers3DView.css",
@@ -317,6 +347,8 @@
   "front_end/layers/LayersPanel.js",
   "front_end/layers/LayerTreeModel.js",
   "front_end/layers/module.json",
+  "front_end/layers_test_runner/LayersTestRunner.js",
+  "front_end/layers_test_runner/module.json",
   "front_end/main/ExecutionContextSelector.js",
   "front_end/main/GCActionDelegate.js",
   "front_end/main/Main.js",
@@ -374,6 +406,9 @@
   "front_end/network_log/NetworkLog.js",
   "front_end/network_priorities/module.json",
   "front_end/network_priorities/NetworkPriorities.js",
+  "front_end/network_test_runner/module.json",
+  "front_end/network_test_runner/NetworkTestRunner.js",
+  "front_end/network_test_runner/ProductRegistryTestRunner.js",
   "front_end/object_ui/customPreviewComponent.css",
   "front_end/object_ui/CustomPreviewComponent.js",
   "front_end/object_ui/JavaScriptAutocomplete.js",
@@ -401,6 +436,9 @@
   "front_end/perf_ui/TimelineGrid.js",
   "front_end/perf_ui/timelineOverviewInfo.css",
   "front_end/perf_ui/TimelineOverviewPane.js",
+  "front_end/performance_test_runner/module.json",
+  "front_end/performance_test_runner/TimelineDataTestRunner.js",
+  "front_end/performance_test_runner/TimelineTestRunner.js",
   "front_end/persistence/Automapping.js",
   "front_end/persistence/DefaultMapping.js",
   "front_end/persistence/editFileSystemView.css",
@@ -444,6 +482,9 @@
   "front_end/profiler/ProfileView.js",
   "front_end/profiler/TargetsComboBoxController.js",
   "front_end/profiler/TopDownProfileDataGrid.js",
+  "front_end/profiler_test_runner/HeapSnapshotTestRunner.js",
+  "front_end/profiler_test_runner/module.json",
+  "front_end/profiler_test_runner/ProfilerTestRunner.js",
   "front_end/protocol/InspectorBackend.js",
   "front_end/protocol/module.json",
   "front_end/quick_open/CommandMenu.js",
@@ -485,6 +526,8 @@
   "front_end/sass/SASSProcessor.js",
   "front_end/sass/SASSSourceMapFactory.js",
   "front_end/sass/SASSSupport.js",
+  "front_end/sass_test_runner/module.json",
+  "front_end/sass_test_runner/SASSTestRunner.js",
   "front_end/screencast/InputModel.js",
   "front_end/screencast/module.json",
   "front_end/screencast/ScreencastApp.js",
@@ -540,6 +583,8 @@
   "front_end/security/SecurityModel.js",
   "front_end/security/SecurityPanel.js",
   "front_end/security/sidebar.css",
+  "front_end/security_test_runner/module.json",
+  "front_end/security_test_runner/SecurityTestRunner.js",
   "front_end/services/ServiceManager.js",
   "front_end/settings/frameworkBlackboxSettingsTab.css",
   "front_end/settings/FrameworkBlackboxSettingsTab.js",
@@ -618,6 +663,12 @@
   "front_end/sources/WorkspaceMappingTip.js",
   "front_end/sources/xhrBreakpointsSidebarPane.css",
   "front_end/sources/XHRBreakpointsSidebarPane.js",
+  "front_end/sources_test_runner/AutocompleteTestRunner.js",
+  "front_end/sources_test_runner/BreakpointManagerTestRunner.js",
+  "front_end/sources_test_runner/DebuggerTestRunner.js",
+  "front_end/sources_test_runner/EditorTestRunner.js",
+  "front_end/sources_test_runner/LiveEditTestRunner.js",
+  "front_end/sources_test_runner/SearchTestRunner.js",
   "front_end/sources_test_runner/SourcesTestRunner.js",
   "front_end/sources_test_runner/module.json",
   "front_end/terminal/terminal.css",
diff --git a/third_party/WebKit/Source/devtools/front_end/Runtime.js b/third_party/WebKit/Source/devtools/front_end/Runtime.js
index b3123ed..f8dd1ac 100644
--- a/third_party/WebKit/Source/devtools/front_end/Runtime.js
+++ b/third_party/WebKit/Source/devtools/front_end/Runtime.js
@@ -738,6 +738,7 @@
       'object_ui': 'ObjectUI',
       'perf_ui': 'PerfUI',
       'har_importer': 'HARImporter',
+      'sass_test_runner': 'SASSTestRunner',
     };
     var namespace = specialCases[this._name] || this._name.split('_').map(a => a.substring(0, 1).toUpperCase() + a.substring(1)).join('');
     self[namespace] = self[namespace] || {};
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility_test_runner/AccessibilityPaneTestRunner.js b/third_party/WebKit/Source/devtools/front_end/accessibility_test_runner/AccessibilityPaneTestRunner.js
new file mode 100644
index 0000000..7d19972
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility_test_runner/AccessibilityPaneTestRunner.js
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+AccessibilityTestRunner.accessibilitySidebarPane = function() {
+  return self.runtime.sharedInstance(Accessibility.AccessibilitySidebarView);
+};
+
+AccessibilityTestRunner.selectNodeAndWaitForAccessibility = function(idValue) {
+  return new Promise(resolve => {
+    ElementsTestRunner.selectNodeWithId(idValue, function() {
+      self.runtime.sharedInstance(Accessibility.AccessibilitySidebarView).doUpdate().then(resolve);
+    });
+  });
+};
+
+AccessibilityTestRunner.dumpSelectedElementAccessibilityNode = function() {
+  var sidebarPane = AccessibilityTestRunner.accessibilitySidebarPane();
+
+  if (!sidebarPane) {
+    TestRunner.addResult('No sidebarPane in dumpSelectedElementAccessibilityNode');
+    TestRunner.completeTest();
+    return;
+  }
+
+  AccessibilityTestRunner.dumpAccessibilityNode(sidebarPane._axNodeSubPane._axNode);
+};
+
+AccessibilityTestRunner.dumpAccessibilityNode = function(accessibilityNode) {
+  if (!accessibilityNode) {
+    TestRunner.addResult('<null>');
+    TestRunner.completeTest();
+    return;
+  }
+
+  var builder = [];
+  builder.push(accessibilityNode.role().value);
+  builder.push((accessibilityNode.name() ? '"' + accessibilityNode.name().value + '"' : '<undefined>'));
+
+  if (accessibilityNode.properties()) {
+    for (var property of accessibilityNode.properties()) {
+      if ('value' in property)
+        builder.push(property.name + '="' + property.value.value + '"');
+    }
+  }
+
+  TestRunner.addResult(builder.join(' '));
+};
+
+AccessibilityTestRunner.findARIAAttributeTreeElement = function(attribute) {
+  var sidebarPane = AccessibilityTestRunner.accessibilitySidebarPane();
+
+  if (!sidebarPane) {
+    TestRunner.addResult('Could not get Accessibility sidebar pane.');
+    TestRunner.completeTest();
+    return;
+  }
+
+  var ariaSubPane = sidebarPane._ariaSubPane;
+  var treeOutline = ariaSubPane._treeOutline;
+  var childNodes = treeOutline._rootElement._children;
+
+  for (var treeElement of childNodes) {
+    if (treeElement._attribute.name === attribute)
+      return treeElement;
+  }
+
+  return null;
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/accessibility_test_runner/module.json
new file mode 100644
index 0000000..aac49bd
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility_test_runner/module.json
@@ -0,0 +1,14 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "accessibility",
+    "elements_test_runner"
+  ],
+  "scripts": [
+    "AccessibilityPaneTestRunner.js"
+  ],
+  "skip_compilation": [
+    "AccessibilityPaneTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/application_test_runner/AppcacheTestRunner.js b/third_party/WebKit/Source/devtools/front_end/application_test_runner/AppcacheTestRunner.js
new file mode 100644
index 0000000..17c8274
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/application_test_runner/AppcacheTestRunner.js
@@ -0,0 +1,215 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ApplicationTestRunner.createAndNavigateIFrame = function(url, callback) {
+  TestRunner.addSniffer(SDK.ResourceTreeModel.prototype, '_frameNavigated', frameNavigated);
+  TestRunner.evaluateInPage('createAndNavigateIFrame(unescape(\'' + escape(url) + '\'))');
+
+  function frameNavigated(frame) {
+    callback(frame.id);
+  }
+};
+
+ApplicationTestRunner.navigateIFrame = function(frameId, url, callback) {
+  var frame = TestRunner.resourceTreeModel.frameForId(frameId);
+  TestRunner.evaluateInPage(
+      'navigateIFrame(unescape(\'' + escape(frame.name) + '\'), unescape(\'' + escape(url) + '\'))');
+  TestRunner.addSniffer(SDK.ResourceTreeModel.prototype, '_frameNavigated', frameNavigated);
+
+  function frameNavigated(frame) {
+    callback(frame.id);
+  }
+};
+
+ApplicationTestRunner.removeIFrame = function(frameId, callback) {
+  var frame = TestRunner.resourceTreeModel.frameForId(frameId);
+  TestRunner.evaluateInPage('removeIFrame(unescape(\'' + escape(frame.name) + '\'))');
+  TestRunner.addSniffer(SDK.ResourceTreeModel.prototype, '_frameDetached', frameDetached);
+
+  function frameDetached(frame) {
+    callback(frame.id);
+  }
+};
+
+ApplicationTestRunner.swapFrameCache = function(frameId) {
+  var frame = TestRunner.resourceTreeModel.frameForId(frameId);
+  TestRunner.evaluateInPage('swapFrameCache(unescape(\'' + escape(frame.name) + '\'))');
+};
+
+ApplicationTestRunner.dumpApplicationCache = function() {
+  ApplicationTestRunner.dumpApplicationCacheTree();
+  ApplicationTestRunner.dumpApplicationCacheModel();
+  TestRunner.addResult('');
+};
+
+ApplicationTestRunner.dumpApplicationCacheTree = function() {
+  TestRunner.addResult('Dumping application cache tree:');
+  var applicationCacheTreeElement = UI.panels.resources._sidebar.applicationCacheListTreeElement;
+
+  if (!applicationCacheTreeElement.childCount()) {
+    TestRunner.addResult('    (empty)');
+    return;
+  }
+
+  for (var i = 0; i < applicationCacheTreeElement.childCount(); ++i) {
+    var manifestTreeElement = applicationCacheTreeElement.childAt(i);
+    TestRunner.addResult('    Manifest URL: ' + manifestTreeElement.manifestURL);
+
+    if (!manifestTreeElement.childCount()) {
+      TestRunner.addResult('    (no frames)');
+      continue;
+    }
+
+    for (var j = 0; j < manifestTreeElement.childCount(); ++j) {
+      var frameTreeElement = manifestTreeElement.childAt(j);
+      TestRunner.addResult('        Frame: ' + frameTreeElement.title);
+    }
+  }
+};
+
+ApplicationTestRunner.frameIdToString = function(frameId) {
+  if (!ApplicationTestRunner.framesByFrameId)
+    ApplicationTestRunner.framesByFrameId = {};
+
+  var frame = TestRunner.resourceTreeModel.frameForId(frameId);
+
+  if (!frame)
+    frame = ApplicationTestRunner.framesByFrameId[frameId];
+
+  ApplicationTestRunner.framesByFrameId[frameId] = frame;
+  return frame.name;
+};
+
+ApplicationTestRunner.applicationCacheStatusToString = function(status) {
+  var statusInformation = {};
+  statusInformation[applicationCache.UNCACHED] = 'UNCACHED';
+  statusInformation[applicationCache.IDLE] = 'IDLE';
+  statusInformation[applicationCache.CHECKING] = 'CHECKING';
+  statusInformation[applicationCache.DOWNLOADING] = 'DOWNLOADING';
+  statusInformation[applicationCache.UPDATEREADY] = 'UPDATEREADY';
+  statusInformation[applicationCache.OBSOLETE] = 'OBSOLETE';
+  return statusInformation[status] || statusInformation[applicationCache.UNCACHED];
+};
+
+ApplicationTestRunner.dumpApplicationCacheModel = function() {
+  TestRunner.addResult('Dumping application cache model:');
+  var model = UI.panels.resources._sidebar._applicationCacheModel;
+  var frameIds = [];
+
+  for (var frameId in model._manifestURLsByFrame)
+    frameIds.push(frameId);
+
+  function compareFunc(a, b) {
+    return ApplicationTestRunner.frameIdToString(a).localeCompare(ApplicationTestRunner.frameIdToString(b));
+  }
+
+  frameIds.sort(compareFunc);
+
+  if (!frameIds.length) {
+    TestRunner.addResult('    (empty)');
+    return;
+  }
+
+  for (var i = 0; i < frameIds.length; ++i) {
+    var frameId = frameIds[i];
+    var manifestURL = model.frameManifestURL(frameId);
+    var status = model.frameManifestStatus(frameId);
+    TestRunner.addResult('    Frame: ' + ApplicationTestRunner.frameIdToString(frameId));
+    TestRunner.addResult('        manifest url: ' + manifestURL);
+    TestRunner.addResult('        status:       ' + ApplicationTestRunner.applicationCacheStatusToString(status));
+  }
+};
+
+ApplicationTestRunner.waitForFrameManifestURLAndStatus = function(frameId, manifestURL, status, callback) {
+  var frameManifestStatus = UI.panels.resources._sidebar._applicationCacheModel.frameManifestStatus(frameId);
+  var frameManifestURL = UI.panels.resources._sidebar._applicationCacheModel.frameManifestURL(frameId);
+
+  if (frameManifestStatus === status && frameManifestURL.indexOf(manifestURL) !== -1) {
+    callback();
+    return;
+  }
+
+  var handler =
+      ApplicationTestRunner.waitForFrameManifestURLAndStatus.bind(this, frameId, manifestURL, status, callback);
+  TestRunner.addSniffer(Resources.ApplicationCacheModel.prototype, '_frameManifestUpdated', handler);
+};
+
+ApplicationTestRunner.startApplicationCacheStatusesRecording = function() {
+  if (ApplicationTestRunner.applicationCacheStatusesRecords) {
+    ApplicationTestRunner.applicationCacheStatusesRecords = {};
+    return;
+  }
+
+  ApplicationTestRunner.applicationCacheStatusesRecords = {};
+
+  function addRecord(frameId, manifestURL, status) {
+    var record = {};
+    record.manifestURL = manifestURL;
+    record.status = status;
+
+    if (!ApplicationTestRunner.applicationCacheStatusesRecords[frameId])
+      ApplicationTestRunner.applicationCacheStatusesRecords[frameId] = [];
+
+    ApplicationTestRunner.applicationCacheStatusesRecords[frameId].push(record);
+
+    if (ApplicationTestRunner.awaitedFrameStatusEventsCount &&
+        ApplicationTestRunner.awaitedFrameStatusEventsCount[frameId]) {
+      ApplicationTestRunner.awaitedFrameStatusEventsCount[frameId].count--;
+
+      if (!ApplicationTestRunner.awaitedFrameStatusEventsCount[frameId].count)
+        ApplicationTestRunner.awaitedFrameStatusEventsCount[frameId].callback();
+    }
+  }
+
+  TestRunner.addSniffer(Resources.ApplicationCacheModel.prototype, '_frameManifestUpdated', addRecord, true);
+};
+
+ApplicationTestRunner.ensureFrameStatusEventsReceived = function(frameId, count, callback) {
+  var records = ApplicationTestRunner.applicationCacheStatusesRecords[frameId] || [];
+  var eventsLeft = count - records.length;
+
+  if (!eventsLeft) {
+    callback();
+    return;
+  }
+
+  if (!ApplicationTestRunner.awaitedFrameStatusEventsCount)
+    ApplicationTestRunner.awaitedFrameStatusEventsCount = {};
+
+  ApplicationTestRunner.awaitedFrameStatusEventsCount[frameId] = {count: eventsLeft, callback: callback};
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    var framesCount = 0;
+
+    function createAndNavigateIFrame(url) {
+      var iframe = document.createElement('iframe');
+      iframe.src = url;
+      iframe.name = 'frame' + ++framesCount;
+      iframe.id = iframe.name;
+      document.body.appendChild(iframe);
+    }
+
+    function removeIFrame(name) {
+      var iframe = document.querySelector('#' + name);
+      iframe.parentElement.removeChild(iframe);
+    }
+
+    function navigateIFrame(name, url) {
+      var iframe = document.querySelector('#' + name);
+      iframe.src = url;
+    }
+
+    function swapFrameCache(name) {
+      var iframe = document.querySelector('#' + name);
+      iframe.contentWindow.applicationCache.swapCache();
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/application_test_runner/CacheStorageTestRunner.js b/third_party/WebKit/Source/devtools/front_end/application_test_runner/CacheStorageTestRunner.js
new file mode 100644
index 0000000..2a290155
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/application_test_runner/CacheStorageTestRunner.js
@@ -0,0 +1,173 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ApplicationTestRunner.dumpCacheTree = async function() {
+  UI.panels.resources._sidebar.cacheStorageListTreeElement.expand();
+  TestRunner.addResult('Dumping CacheStorage tree:');
+  var cachesTreeElement = UI.panels.resources._sidebar.cacheStorageListTreeElement;
+  var promise = TestRunner.addSnifferPromise(SDK.ServiceWorkerCacheModel.prototype, '_updateCacheNames');
+  UI.panels.resources._sidebar.cacheStorageListTreeElement._refreshCaches();
+  await promise;
+
+  if (!cachesTreeElement.childCount()) {
+    TestRunner.addResult('    (empty)');
+    return;
+  }
+
+  for (var i = 0; i < cachesTreeElement.childCount(); ++i) {
+    var cacheTreeElement = cachesTreeElement.childAt(i);
+    TestRunner.addResult('    cache: ' + cacheTreeElement.title);
+    var view = cacheTreeElement._view;
+    promise = TestRunner.addSnifferPromise(Resources.ServiceWorkerCacheView.prototype, '_updateDataCallback');
+
+    if (!view)
+      cacheTreeElement.onselect(false);
+    else
+      view._updateData(true);
+
+    view = cacheTreeElement._view;
+    await promise;
+
+    if (view._entries.length === 0) {
+      TestRunner.addResult('        (cache empty)');
+      continue;
+    }
+
+    var dataGrid = view._dataGrid;
+
+    for (var node of dataGrid.rootNode().children) {
+      var children = Array.from(node.element().children).filter(function(element) {
+        return !element.classList.contains('responseTime-column');
+      });
+
+      var entries = Array.from(children, td => td.textContent).filter(text => text);
+      TestRunner.addResult('        ' + entries.join(', '));
+    }
+  }
+};
+
+ApplicationTestRunner.deleteCacheFromInspector = async function(cacheName, optionalEntry) {
+  UI.panels.resources._sidebar.cacheStorageListTreeElement.expand();
+
+  if (optionalEntry)
+    TestRunner.addResult('Deleting CacheStorage entry ' + optionalEntry + ' in cache ' + cacheName);
+  else
+    TestRunner.addResult('Deleting CacheStorage cache ' + cacheName);
+
+  var cachesTreeElement = UI.panels.resources._sidebar.cacheStorageListTreeElement;
+  var promise = TestRunner.addSnifferPromise(SDK.ServiceWorkerCacheModel.prototype, '_updateCacheNames');
+  UI.panels.resources._sidebar.cacheStorageListTreeElement._refreshCaches();
+  await promise;
+
+  if (!cachesTreeElement.childCount())
+    throw 'Error: Could not find CacheStorage cache ' + cacheName;
+
+
+  for (var i = 0; i < cachesTreeElement.childCount(); i++) {
+    var cacheTreeElement = cachesTreeElement.childAt(i);
+    var title = cacheTreeElement.title;
+    var elementCacheName = title.substring(0, title.lastIndexOf(' - '));
+
+    if (elementCacheName !== cacheName)
+      continue;
+
+    if (!optionalEntry) {
+      promise = TestRunner.addSnifferPromise(SDK.ServiceWorkerCacheModel.prototype, '_cacheRemoved');
+      cacheTreeElement._clearCache();
+      await promise;
+      return;
+    }
+
+    promise = TestRunner.addSnifferPromise(Resources.ServiceWorkerCacheView.prototype, '_updateDataCallback');
+    var view = cacheTreeElement._view;
+
+    if (!view)
+      cacheTreeElement.onselect(false);
+    else
+      view._updateData(true);
+
+    view = cacheTreeElement._view;
+    await promise;
+    var entry = view._entries.find(entry => entry.request === optionalEntry);
+
+    if (!entry)
+      throw 'Error: Could not find cache entry to delete: ' + optionalEntry;
+
+    await view._model.deleteCacheEntry(view._cache, entry.request);
+    return;
+  }
+
+  throw 'Error: Could not find CacheStorage cache ' + cacheName;
+};
+
+ApplicationTestRunner.waitForCacheRefresh = function(callback) {
+  TestRunner.addSniffer(SDK.ServiceWorkerCacheModel.prototype, '_updateCacheNames', callback, false);
+};
+
+ApplicationTestRunner.createCache = function(cacheName) {
+  return TestRunner.callFunctionInPageAsync('createCache', [cacheName]);
+};
+
+ApplicationTestRunner.addCacheEntry = function(cacheName, requestUrl, responseText) {
+  return TestRunner.callFunctionInPageAsync('addCacheEntry', [cacheName, requestUrl, responseText]);
+};
+
+ApplicationTestRunner.deleteCache = function(cacheName) {
+  return TestRunner.callFunctionInPageAsync('deleteCache', [cacheName]);
+};
+
+ApplicationTestRunner.deleteCacheEntry = function(cacheName, requestUrl) {
+  return TestRunner.callFunctionInPageAsync('deleteCacheEntry', [cacheName, requestUrl]);
+};
+
+ApplicationTestRunner.clearAllCaches = function() {
+  return TestRunner.callFunctionInPageAsync('clearAllCaches');
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    function onCacheStorageError(e) {
+      console.error('CacheStorage error: ' + e);
+    }
+
+    function createCache(cacheName) {
+      return caches.open(cacheName).catch(onCacheStorageError);
+    }
+
+    function addCacheEntry(cacheName, requestUrl, responseText) {
+      return caches.open(cacheName).then(function(cache) {
+        var request = new Request(requestUrl);
+        var myBlob = new Blob();
+
+        var init = {
+          'status': 200,
+          'statusText': responseText
+        };
+
+        var response = new Response(myBlob, init);
+        return cache.put(request, response);
+      }).catch(onCacheStorageError);
+    }
+
+    function deleteCache(cacheName) {
+      return caches.delete(cacheName).then(function(success) {
+        if (!success)
+          onCacheStorageError('Could not find cache ' + cacheName);
+      }).catch(onCacheStorageError);
+    }
+
+    function deleteCacheEntry(cacheName, requestUrl) {
+      return caches.open(cacheName).then(cache => cache.delete(new Request(requestUrl))).catch(onCacheStorageError);
+    }
+
+    function clearAllCaches() {
+      return caches.keys().then(keys => Promise.all(keys.map(key => caches.delete(key)))).catch(onCacheStorageError.bind(this, undefined));
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/application_test_runner/IndexedDBTestRunner.js b/third_party/WebKit/Source/devtools/front_end/application_test_runner/IndexedDBTestRunner.js
new file mode 100644
index 0000000..696bf91
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/application_test_runner/IndexedDBTestRunner.js
@@ -0,0 +1,356 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ApplicationTestRunner.dumpIndexedDBTree = function() {
+  TestRunner.addResult('Dumping IndexedDB tree:');
+  var indexedDBTreeElement = UI.panels.resources._sidebar.indexedDBListTreeElement;
+
+  if (!indexedDBTreeElement.childCount()) {
+    TestRunner.addResult('    (empty)');
+    return;
+  }
+
+  for (var i = 0; i < indexedDBTreeElement.childCount(); ++i) {
+    var databaseTreeElement = indexedDBTreeElement.childAt(i);
+    TestRunner.addResult('    database: ' + databaseTreeElement.title);
+
+    if (!databaseTreeElement.childCount()) {
+      TestRunner.addResult('        (no object stores)');
+      continue;
+    }
+
+    for (var j = 0; j < databaseTreeElement.childCount(); ++j) {
+      var objectStoreTreeElement = databaseTreeElement.childAt(j);
+      TestRunner.addResult('        Object store: ' + objectStoreTreeElement.title);
+
+      if (!objectStoreTreeElement.childCount()) {
+        TestRunner.addResult('            (no indexes)');
+        continue;
+      }
+
+      for (var k = 0; k < objectStoreTreeElement.childCount(); ++k) {
+        var indexTreeElement = objectStoreTreeElement.childAt(k);
+        TestRunner.addResult('            Index: ' + indexTreeElement.title);
+      }
+    }
+  }
+};
+
+var lastCallbackId = 0;
+var callbacks = {};
+var callbackIdPrefix = 'InspectorTest.IndexedDB_callback';
+
+ApplicationTestRunner.evaluateWithCallback = function(frameId, methodName, parameters, callback) {
+  ApplicationTestRunner._installIndexedDBSniffer();
+  var callbackId = ++lastCallbackId;
+  callbacks[callbackId] = callback;
+  var parametersString = 'dispatchCallback.bind(this, "' + callbackIdPrefix + callbackId + '")';
+
+  for (var i = 0; i < parameters.length; ++i)
+    parametersString += ', ' + JSON.stringify(parameters[i]);
+
+  var requestString = methodName + '(' + parametersString + ')';
+  TestRunner.evaluateInPage(requestString);
+};
+
+ApplicationTestRunner._installIndexedDBSniffer = function() {
+  ConsoleTestRunner.addConsoleSniffer(consoleMessageOverride, false);
+
+  function consoleMessageOverride(msg) {
+    var text = msg.messageText;
+
+    if (!text.startsWith(callbackIdPrefix)) {
+      ConsoleTestRunner.addConsoleSniffer(consoleMessageOverride, false);
+      return;
+    }
+
+    var callbackId = text.substring(callbackIdPrefix.length);
+    callbacks[callbackId].call();
+    delete callbacks[callbackId];
+  }
+};
+
+ApplicationTestRunner.createDatabase = function(frameId, databaseName, callback) {
+  ApplicationTestRunner.evaluateWithCallback(frameId, 'createDatabase', [databaseName], callback);
+};
+
+ApplicationTestRunner.deleteDatabase = function(frameId, databaseName, callback) {
+  ApplicationTestRunner.evaluateWithCallback(frameId, 'deleteDatabase', [databaseName], callback);
+};
+
+ApplicationTestRunner.createObjectStore = function(
+    frameId, databaseName, objectStoreName, keyPath, autoIncrement, callback) {
+  ApplicationTestRunner.evaluateWithCallback(
+      frameId, 'createObjectStore', [databaseName, objectStoreName, keyPath, autoIncrement], callback);
+};
+
+ApplicationTestRunner.deleteObjectStore = function(frameId, databaseName, objectStoreName, callback) {
+  ApplicationTestRunner.evaluateWithCallback(frameId, 'deleteObjectStore', [databaseName, objectStoreName], callback);
+};
+
+ApplicationTestRunner.createObjectStoreIndex = function(
+    frameId, databaseName, objectStoreName, objectStoreIndexName, keyPath, unique, multiEntry, callback) {
+  ApplicationTestRunner.evaluateWithCallback(
+      frameId, 'createObjectStoreIndex',
+      [databaseName, objectStoreName, objectStoreIndexName, keyPath, unique, multiEntry], callback);
+};
+
+ApplicationTestRunner.deleteObjectStoreIndex = function(
+    frameId, databaseName, objectStoreName, objectStoreIndexName, callback) {
+  ApplicationTestRunner.evaluateWithCallback(
+      frameId, 'deleteObjectStoreIndex', [databaseName, objectStoreName, objectStoreIndexName], callback);
+};
+
+ApplicationTestRunner.addIDBValue = function(frameId, databaseName, objectStoreName, value, key, callback) {
+  ApplicationTestRunner.evaluateWithCallback(
+      frameId, 'addIDBValue', [databaseName, objectStoreName, value, key], callback);
+};
+
+ApplicationTestRunner.createIndexedDBModel = function() {
+  var indexedDBModel = new Resources.IndexedDBModel(SDK.targetManager.mainTarget(), TestRunner.securityOriginManager);
+  indexedDBModel.enable();
+  return indexedDBModel;
+};
+
+ApplicationTestRunner.createDatabaseAsync = function(databaseName) {
+  return TestRunner.evaluateInPageAsync('createDatabaseAsync(\'' + databaseName + '\')');
+};
+
+ApplicationTestRunner.createObjectStoreAsync = function(databaseName, objectStoreName, indexName, keyPath) {
+  return TestRunner.evaluateInPageAsync(
+      'createObjectStoreAsync(\'' + databaseName + '\', \'' + objectStoreName + '\', \'' + indexName + '\', \'' +
+      keyPath + '\')');
+};
+
+ApplicationTestRunner.addIDBValueAsync = function(databaseName, objectStoreName, key, value) {
+  return TestRunner.evaluateInPageAsync(
+      'addIDBValueAsync(\'' + databaseName + '\', \'' + objectStoreName + '\', \'' + key + '\', \'' + value + '\')');
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    function dispatchCallback(callbackId) {
+      console.log(callbackId);
+    }
+
+    function onIndexedDBError(e) {
+      console.error('IndexedDB error: ' + e);
+    }
+
+    function onIndexedDBBlocked(e) {
+      console.error('IndexedDB blocked: ' + e);
+    }
+
+    function doWithDatabase(databaseName, callback) {
+      function innerCallback() {
+        var db = request.result;
+        callback(db);
+      }
+
+      var request = indexedDB.open(databaseName);
+      request.onblocked = onIndexedDBBlocked;
+      request.onerror = onIndexedDBError;
+      request.onsuccess = innerCallback;
+    }
+
+    function doWithVersionTransaction(databaseName, callback, commitCallback) {
+      doWithDatabase(databaseName, step2);
+
+      function step2(db) {
+        var version = db.version;
+        db.close();
+        request = indexedDB.open(databaseName, version + 1);
+        request.onerror = onIndexedDBError;
+        request.onupgradeneeded = onUpgradeNeeded;
+        request.onsuccess = onOpened;
+
+        function onUpgradeNeeded(e) {
+          var db = e.target.result;
+          var trans = e.target.transaction;
+          callback(db, trans);
+        }
+
+        function onOpened(e) {
+          var db = e.target.result;
+          db.close();
+          commitCallback();
+        }
+      }
+    }
+
+    function doWithReadWriteTransaction(databaseName, objectStoreName, callback, commitCallback) {
+      doWithDatabase(databaseName, step2);
+
+      function step2(db) {
+        var transaction = db.transaction([objectStoreName], 'readwrite');
+        var objectStore = transaction.objectStore(objectStoreName);
+        callback(objectStore, innerCommitCallback);
+
+        function innerCommitCallback() {
+          db.close();
+          commitCallback();
+        }
+      }
+    }
+
+    function createDatabase(callback, databaseName) {
+      var request = indexedDB.open(databaseName);
+      request.onerror = onIndexedDBError;
+      request.onsuccess = closeDatabase;
+
+      function closeDatabase() {
+        request.result.close();
+        callback();
+      }
+    }
+
+    function deleteDatabase(callback, databaseName) {
+      var request = indexedDB.deleteDatabase(databaseName);
+      request.onerror = onIndexedDBError;
+      request.onsuccess = callback;
+    }
+
+    function createObjectStore(callback, databaseName, objectStoreName, keyPath, autoIncrement) {
+      doWithVersionTransaction(databaseName, withTransactionCallback, callback);
+
+      function withTransactionCallback(db, transaction) {
+        var store = db.createObjectStore(objectStoreName, {
+          keyPath: keyPath,
+          autoIncrement: autoIncrement
+        });
+      }
+    }
+
+    function deleteObjectStore(callback, databaseName, objectStoreName) {
+      doWithVersionTransaction(databaseName, withTransactionCallback, callback);
+
+      function withTransactionCallback(db, transaction) {
+        var store = db.deleteObjectStore(objectStoreName);
+      }
+    }
+
+    function createObjectStoreIndex(callback, databaseName, objectStoreName, objectStoreIndexName, keyPath, unique, multiEntry) {
+      doWithVersionTransaction(databaseName, withTransactionCallback, callback);
+
+      function withTransactionCallback(db, transaction) {
+        var objectStore = transaction.objectStore(objectStoreName);
+
+        objectStore.createIndex(objectStoreIndexName, keyPath, {
+          unique: unique,
+          multiEntry: multiEntry
+        });
+      }
+    }
+
+    function deleteObjectStoreIndex(callback, databaseName, objectStoreName, objectStoreIndexName) {
+      doWithVersionTransaction(databaseName, withTransactionCallback, callback);
+
+      function withTransactionCallback(db, transaction) {
+        var objectStore = transaction.objectStore(objectStoreName);
+        objectStore.deleteIndex(objectStoreIndexName);
+      }
+    }
+
+    function addIDBValue(callback, databaseName, objectStoreName, value, key) {
+      doWithReadWriteTransaction(databaseName, objectStoreName, withTransactionCallback, callback);
+
+      function withTransactionCallback(objectStore, commitCallback) {
+        var request;
+
+        if (key)
+          request = objectStore.add(value, key);
+        else
+          request = objectStore.add(value);
+
+        request.onerror = onIndexedDBError;
+        request.onsuccess = commitCallback;
+      }
+    }
+
+    function createDatabaseAsync(databaseName) {
+      var callback;
+      var promise = new Promise(fulfill => callback = fulfill);
+      var request = indexedDB.open(databaseName);
+      request.onerror = onIndexedDBError;
+
+      request.onsuccess = function(event) {
+        request.result.close();
+        callback();
+      };
+
+      return promise;
+    }
+
+    function createObjectStoreAsync(databaseName, objectStoreName, indexName, keyPath) {
+      var callback;
+      var promise = new Promise(fulfill => callback = fulfill);
+      var request = indexedDB.open(databaseName);
+      request.onerror = onIndexedDBError;
+
+      request.onsuccess = function(event) {
+        var db = request.result;
+        var version = db.version;
+        db.close();
+        var upgradeRequest = indexedDB.open(databaseName, version + 1);
+        upgradeRequest.onerror = onIndexedDBError;
+
+        upgradeRequest.onupgradeneeded = function(e) {
+          var upgradeDb = e.target.result;
+
+          var store = upgradeDb.createObjectStore(objectStoreName, {
+            keyPath: 'test',
+            autoIncrement: false
+          });
+
+          store.createIndex(indexName, 'test', {
+            unique: false,
+            multiEntry: false
+          });
+
+          callback();
+        };
+
+        upgradeRequest.onsuccess = function(e) {
+          var upgradeDb = e.target.result;
+          upgradeDb.close();
+          callback();
+        };
+      };
+
+      return promise;
+    }
+
+    function addIDBValueAsync(databaseName, objectStoreName, key, value) {
+      var callback;
+      var promise = new Promise(fulfill => callback = fulfill);
+      var request = indexedDB.open(databaseName);
+      request.onerror = onIndexedDBError;
+
+      request.onsuccess = function(event) {
+        var db = request.result;
+        var transaction = db.transaction(objectStoreName, 'readwrite');
+        var store = transaction.objectStore(objectStoreName);
+
+        store.put({
+          test: key,
+          testValue: value
+        });
+
+        transaction.onerror = onIndexedDBError;
+
+        transaction.oncomplete = function() {
+          db.close();
+          callback();
+        };
+      };
+
+      return promise;
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/application_test_runner/ResourceTreeTestRunner.js b/third_party/WebKit/Source/devtools/front_end/application_test_runner/ResourceTreeTestRunner.js
new file mode 100644
index 0000000..941d976
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/application_test_runner/ResourceTreeTestRunner.js
@@ -0,0 +1,92 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ApplicationTestRunner.dumpResources = function(formatter) {
+  var results = [];
+
+  function formatterWrapper(resource) {
+    if (formatter)
+      results.push({resource: resource, text: formatter(resource)});
+    else
+      results.push({resource: resource, text: resource.url});
+  }
+
+  TestRunner.resourceTreeModel.forAllResources(formatterWrapper);
+
+  function comparator(result1, result2) {
+    return result1.resource.url.localeCompare(result2.resource.url);
+  }
+
+  results.sort(comparator);
+
+  for (var i = 0; i < results.length; ++i)
+    TestRunner.addResult(results[i].text);
+};
+
+ApplicationTestRunner.dumpResourcesURLMap = function() {
+  var results = [];
+  TestRunner.resourceTreeModel.forAllResources(collect);
+
+  function collect(resource) {
+    results.push({url: resource.url, resource: TestRunner.resourceTreeModel.resourceForURL(resource.url)});
+  }
+
+  function comparator(result1, result2) {
+    if (result1.url > result2.url)
+      return 1;
+
+    if (result2.url > result1.url)
+      return -1;
+
+    return 0;
+  }
+
+  results.sort(comparator);
+
+  for (var i = 0; i < results.length; ++i)
+    TestRunner.addResult(results[i].url + ' == ' + results[i].resource.url);
+};
+
+ApplicationTestRunner.dumpResourcesTree = function() {
+  function dump(treeItem, prefix) {
+    if (typeof treeItem._resetBubble === 'function')
+      treeItem._resetBubble();
+
+    TestRunner.addResult(prefix + treeItem.listItemElement.textContent);
+    treeItem.expand();
+    var children = treeItem.children();
+
+    for (var i = 0; children && i < children.length; ++i)
+      dump(children[i], prefix + '    ');
+  }
+
+  dump(UI.panels.resources._sidebar._resourcesSection._treeElement, '');
+
+  if (!ApplicationTestRunner._testSourceNavigator) {
+    ApplicationTestRunner._testSourceNavigator = new Sources.SourcesNavigatorView();
+    ApplicationTestRunner._testSourceNavigator.show(UI.inspectorView.element);
+  }
+
+  SourcesTestRunner.dumpNavigatorViewInAllModes(ApplicationTestRunner._testSourceNavigator);
+};
+
+ApplicationTestRunner.dumpResourceTreeEverything = function() {
+  function format(resource) {
+    return resource.resourceType().name() + ' ' + resource.url;
+  }
+
+  TestRunner.addResult('Resources:');
+  ApplicationTestRunner.dumpResources(format);
+  TestRunner.addResult('');
+  TestRunner.addResult('Resources URL Map:');
+  ApplicationTestRunner.dumpResourcesURLMap();
+  TestRunner.addResult('');
+  TestRunner.addResult('Resources Tree:');
+  ApplicationTestRunner.dumpResourcesTree();
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/application_test_runner/ResourcesTestRunner.js b/third_party/WebKit/Source/devtools/front_end/application_test_runner/ResourcesTestRunner.js
new file mode 100644
index 0000000..7c620763
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/application_test_runner/ResourcesTestRunner.js
@@ -0,0 +1,109 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ApplicationTestRunner.createWebSQLDatabase = function(name) {
+  return TestRunner.evaluateInPageAsync(`_openWebSQLDatabase("${name}")`);
+};
+
+ApplicationTestRunner.requestURLComparer = function(r1, r2) {
+  return r1.request.url.localeCompare(r2.request.url);
+};
+
+ApplicationTestRunner.runAfterCachedResourcesProcessed = function(callback) {
+  if (!TestRunner.resourceTreeModel._cachedResourcesProcessed)
+    TestRunner.resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.CachedResourcesLoaded, callback);
+  else
+    callback();
+};
+
+ApplicationTestRunner.runAfterResourcesAreFinished = function(resourceURLs, callback) {
+  var resourceURLsMap = new Set(resourceURLs);
+
+  function checkResources() {
+    for (var url of resourceURLsMap) {
+      var resource = ApplicationTestRunner.resourceMatchingURL(url);
+
+      if (resource)
+        resourceURLsMap.delete(url);
+    }
+
+    if (!resourceURLsMap.size) {
+      TestRunner.resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.ResourceAdded, checkResources);
+      callback();
+    }
+  }
+
+  checkResources();
+
+  if (resourceURLsMap.size)
+    TestRunner.resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.ResourceAdded, checkResources);
+};
+
+ApplicationTestRunner.showResource = function(resourceURL, callback) {
+  var reported = false;
+
+  function callbackWrapper(sourceFrame) {
+    if (reported)
+      return;
+
+    callback(sourceFrame);
+    reported = true;
+  }
+
+  function showResourceCallback() {
+    var resource = ApplicationTestRunner.resourceMatchingURL(resourceURL);
+
+    if (!resource)
+      return;
+
+    UI.panels.resources.showResource(resource, 1);
+    var sourceFrame = UI.panels.resources._resourceViewForResource(resource);
+
+    if (sourceFrame.loaded)
+      callbackWrapper(sourceFrame);
+    else
+      TestRunner.addSniffer(sourceFrame, 'onTextEditorContentSet', callbackWrapper.bind(null, sourceFrame));
+  }
+
+  ApplicationTestRunner.runAfterResourcesAreFinished([resourceURL], showResourceCallback);
+};
+
+ApplicationTestRunner.resourceMatchingURL = function(resourceURL) {
+  var result = null;
+  TestRunner.resourceTreeModel.forAllResources(visit);
+
+  function visit(resource) {
+    if (resource.url.indexOf(resourceURL) !== -1) {
+      result = resource;
+      return true;
+    }
+  }
+
+  return result;
+};
+
+ApplicationTestRunner.databaseModel = function() {
+  return TestRunner.mainTarget.model(Resources.DatabaseModel);
+};
+
+ApplicationTestRunner.domStorageModel = function() {
+  return TestRunner.mainTarget.model(Resources.DOMStorageModel);
+};
+
+ApplicationTestRunner.indexedDBModel = function() {
+  return TestRunner.mainTarget.model(Resources.IndexedDBModel);
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    function _openWebSQLDatabase(name) {
+      return new Promise(resolve => openDatabase(name, '1.0', '', 1024 * 1024, resolve));
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/application_test_runner/ServiceWorkersTestRunner.js b/third_party/WebKit/Source/devtools/front_end/application_test_runner/ServiceWorkersTestRunner.js
new file mode 100644
index 0000000..51158e46
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/application_test_runner/ServiceWorkersTestRunner.js
@@ -0,0 +1,104 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ApplicationTestRunner.registerServiceWorker = function(script, scope) {
+  return TestRunner.callFunctionInPageAsync('registerServiceWorker', [script, scope]);
+};
+
+ApplicationTestRunner.unregisterServiceWorker = function(scope) {
+  return TestRunner.callFunctionInPageAsync('unregisterServiceWorker', [scope]);
+};
+
+ApplicationTestRunner.postToServiceWorker = function(scope, message) {
+  return TestRunner.evaluateInPagePromise('postToServiceWorker("' + scope + '","' + message + '")');
+};
+
+ApplicationTestRunner.waitForServiceWorker = function(callback) {
+  function isRightTarget(target) {
+    return TestRunner.isDedicatedWorker(target) && TestRunner.isServiceWorker(target.parentTarget());
+  }
+
+  SDK.targetManager.observeTargets({
+    targetAdded: function(target) {
+      if (isRightTarget(target) && callback) {
+        setTimeout(callback.bind(null, target), 0);
+        callback = null;
+      }
+    },
+
+    targetRemoved: function(target) {}
+  });
+};
+
+ApplicationTestRunner.dumpServiceWorkersView = function() {
+  var swView = UI.panels.resources.visibleView;
+
+  return swView._reportView._sectionList.childTextNodes()
+      .map(function(node) {
+        return node.textContent.replace(/Received.*/, 'Received').replace(/#\d+/, '#N');
+      })
+      .join('\n');
+};
+
+ApplicationTestRunner.deleteServiceWorkerRegistration = function(scope) {
+  TestRunner.serviceWorkerManager.registrations().valuesArray().map(function(registration) {
+    if (registration.scopeURL === scope)
+      TestRunner.serviceWorkerManager.deleteRegistration(registration.id);
+  });
+};
+
+ApplicationTestRunner.makeFetchInServiceWorker = function(scope, url, requestInitializer, callback) {
+  TestRunner.callFunctionInPageAsync('makeFetchInServiceWorker', [scope, url, requestInitializer]).then(callback);
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    var registrations = {};
+
+    function registerServiceWorker(script, scope) {
+      return navigator.serviceWorker.register(script, {
+        scope: scope
+      }).then(reg => registrations[scope] = reg);
+    }
+
+    function postToServiceWorker(scope, message) {
+      registrations[scope].active.postMessage(message);
+    }
+
+    function unregisterServiceWorker(scope) {
+      var registration = registrations[scope];
+
+      if (!registration)
+        return Promise.reject('ServiceWorker for ' + scope + ' is not registered');
+
+      return registration.unregister().then(() => delete registrations[scope]);
+    }
+
+    function makeFetchInServiceWorker(scope, url, requestInitializer) {
+      let script = 'resources/network-fetch-worker.js';
+
+      return navigator.serviceWorker.register(script, {
+        scope: scope
+      }).then(registration => {
+        let worker = registration.installing;
+
+        return new Promise(resolve => {
+          navigator.serviceWorker.onmessage = e => {
+            resolve(e.data);
+          };
+
+          worker.postMessage({
+            url: url,
+            init: requestInitializer
+          });
+        });
+      });
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/application_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/application_test_runner/module.json
new file mode 100644
index 0000000..ccb40aca
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/application_test_runner/module.json
@@ -0,0 +1,26 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "resources",
+    "console_test_runner",
+    "sources",
+    "sources_test_runner"
+  ],
+  "scripts": [
+    "AppcacheTestRunner.js",
+    "CacheStorageTestRunner.js",
+    "IndexedDBTestRunner.js",
+    "ResourceTreeTestRunner.js",
+    "ResourcesTestRunner.js",
+    "ServiceWorkersTestRunner.js"
+  ],
+  "skip_compilation": [
+    "AppcacheTestRunner.js",
+    "CacheStorageTestRunner.js",
+    "IndexedDBTestRunner.js",
+    "ResourceTreeTestRunner.js",
+    "ResourcesTestRunner.js",
+    "ServiceWorkersTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/audits_test_runner/AuditsTestRunner.js b/third_party/WebKit/Source/devtools/front_end/audits_test_runner/AuditsTestRunner.js
new file mode 100644
index 0000000..649f1c40
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/audits_test_runner/AuditsTestRunner.js
@@ -0,0 +1,66 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+AuditsTestRunner.collectAuditResults = function(callback) {
+  UI.panels.audits.showResults(UI.panels.audits._auditResultsTreeElement.firstChild().results);
+  var trees = UI.panels.audits.visibleView.element.querySelectorAll('.audit-result-tree');
+
+  for (var i = 0; i < trees.length; ++i) {
+    var liElements = trees[i].shadowRoot.querySelectorAll('li');
+
+    for (var j = 0; j < liElements.length; ++j) {
+      if (liElements[j].treeElement)
+        liElements[j].treeElement.expand();
+    }
+  }
+
+  TestRunner.deprecatedRunAfterPendingDispatches(function() {
+    AuditsTestRunner.collectTextContent(UI.panels.audits.visibleView.element, '');
+    callback();
+  });
+};
+
+AuditsTestRunner.launchAllAudits = function(shouldReload, callback) {
+  TestRunner.addSniffer(Audits.AuditController.prototype, '_auditFinishedCallback', callback);
+  var launcherView = UI.panels.audits._launcherView;
+  launcherView._selectAllClicked(true);
+  launcherView._auditPresentStateElement.checked = !shouldReload;
+  launcherView._launchButtonClicked();
+};
+
+AuditsTestRunner.collectTextContent = function(element, indent) {
+  var nodeOutput = '';
+  var child = element.shadowRoot || element.firstChild;
+
+  var nonTextTags = {'STYLE': 1, 'SCRIPT': 1};
+
+  while (child) {
+    if (child.nodeName === 'CONTENT') {
+      AuditsTestRunner.collectTextContent(child.getDistributedNodes()[0], indent);
+    } else if (child.nodeType === Node.TEXT_NODE) {
+      if (!nonTextTags[child.parentElement.nodeName])
+        nodeOutput += child.nodeValue.replace('​', '');
+    } else if (child.nodeType === Node.ELEMENT_NODE || child.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+      if (nodeOutput !== '') {
+        TestRunner.addResult(indent + nodeOutput);
+        nodeOutput = '';
+      }
+
+      if (!child.firstChild && child.classList.contains('severity'))
+        nodeOutput = '[' + child.className + '] ';
+      else
+        AuditsTestRunner.collectTextContent(child, indent + ' ');
+    }
+
+    child = child.nextSibling;
+  }
+
+  if (nodeOutput !== '')
+    TestRunner.addResult(indent + nodeOutput);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/audits_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/audits_test_runner/module.json
new file mode 100644
index 0000000..be3a2642
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/audits_test_runner/module.json
@@ -0,0 +1,13 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "audits"
+  ],
+  "scripts": [
+    "AuditsTestRunner.js"
+  ],
+  "skip_compilation": [
+    "AuditsTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/AutomappingTestRunner.js b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/AutomappingTestRunner.js
new file mode 100644
index 0000000..56d89996
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/AutomappingTestRunner.js
@@ -0,0 +1,121 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+BindingsTestRunner.addFiles = function(testFileSystem, files) {
+  for (var filePath in files) {
+    var file = files[filePath];
+    testFileSystem.addFile(filePath, file.content, (file.time ? file.time.getTime() : 0));
+  }
+};
+
+var timeOverrides;
+var originalRequestMetadata;
+
+BindingsTestRunner.overrideNetworkModificationTime = function(urlToTime) {
+  if (!timeOverrides) {
+    timeOverrides = new Map();
+    originalRequestMetadata =
+        TestRunner.override(Bindings.ContentProviderBasedProject.prototype, 'requestMetadata', overrideTime, true);
+  }
+
+  for (var url in urlToTime)
+    timeOverrides.set(url, urlToTime[url]);
+
+  function overrideTime(uiSourceCode) {
+    if (!timeOverrides.has(uiSourceCode.url()))
+      return originalRequestMetadata.call(this, uiSourceCode);
+
+    var override = timeOverrides.get(uiSourceCode.url());
+    return originalRequestMetadata.call(this, uiSourceCode).then(onOriginalMetadata.bind(null, override));
+  }
+
+  function onOriginalMetadata(timeOverride, metadata) {
+    if (!timeOverride && !metadata)
+      return null;
+
+    return new Workspace.UISourceCodeMetadata(timeOverride, (metadata ? metadata.contentSize : null));
+  }
+};
+
+BindingsTestRunner.AutomappingTest = function(workspace) {
+  this._workspace = workspace;
+  this._networkProject = new Bindings.ContentProviderBasedProject(
+      this._workspace, 'AUTOMAPPING', Workspace.projectTypes.Network, 'simple website');
+
+  if (workspace !== Workspace.workspace)
+    new Persistence.FileSystemWorkspaceBinding(Persistence.isolatedFileSystemManager, this._workspace);
+
+  this._failedBindingsCount = 0;
+  this._automapping =
+      new Persistence.Automapping(this._workspace, this._onBindingAdded.bind(this), this._onBindingRemoved.bind(this));
+  TestRunner.addSniffer(this._automapping, '_onBindingFailedForTest', this._onBindingFailed.bind(this), true);
+  TestRunner.addSniffer(this._automapping, '_onSweepHappenedForTest', this._onSweepHappened.bind(this), true);
+};
+
+BindingsTestRunner.AutomappingTest.prototype = {
+  removeResources: function(urls) {
+    for (var url of urls)
+      this._networkProject.removeFile(url);
+  },
+
+  addNetworkResources: function(assets) {
+    for (var url in assets) {
+      var asset = assets[url];
+      var contentType = asset.contentType || Common.resourceTypes.Script;
+      var contentProvider = new Common.StaticContentProvider(url, contentType, Promise.resolve(asset.content));
+      var metadata =
+          (typeof asset.content === 'string' || asset.time ?
+               new Workspace.UISourceCodeMetadata(asset.time, asset.content.length) :
+               null);
+      var uiSourceCode = this._networkProject.createUISourceCode(url, contentType);
+      this._networkProject.addUISourceCodeWithProvider(uiSourceCode, contentProvider, metadata);
+    }
+  },
+
+  waitUntilMappingIsStabilized: function() {
+    var promise = new Promise(x => this._stabilizedCallback = x);
+    this._checkStabilized();
+    return promise;
+  },
+
+  _onSweepHappened: function() {
+    this._failedBindingsCount = 0;
+    this._checkStabilized();
+  },
+
+  _onBindingAdded: function(binding) {
+    TestRunner.addResult('Binding created: ' + binding);
+    this._checkStabilized();
+  },
+
+  _onBindingFailed: function() {
+    ++this._failedBindingsCount;
+    this._checkStabilized();
+  },
+
+  _onBindingRemoved: function(binding) {
+    TestRunner.addResult('Binding removed: ' + binding);
+    this._checkStabilized();
+  },
+
+  _checkStabilized: function() {
+    if (!this._stabilizedCallback || this._automapping._sweepThrottler._process)
+      return;
+
+    var networkUISourceCodes = this._workspace.uiSourceCodesForProjectType(Workspace.projectTypes.Network);
+    var stabilized = this._failedBindingsCount + this._automapping._bindings.size === networkUISourceCodes.length;
+
+    if (stabilized) {
+      TestRunner.addResult('Mapping has stabilized.');
+      var callback = this._stabilizedCallback;
+      delete this._stabilizedCallback;
+      callback.call(null);
+    }
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/BindingsTestRunner.js b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/BindingsTestRunner.js
new file mode 100644
index 0000000..ec92bcd
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/BindingsTestRunner.js
@@ -0,0 +1,219 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+BindingsTestRunner.dumpWorkspace = function(previousSnapshot) {
+  var uiSourceCodes = Workspace.workspace.uiSourceCodes().slice();
+  var urls = uiSourceCodes.map(code => code.url());
+
+  urls = urls.map(url => {
+    if (!url.startsWith('debugger://'))
+      return url;
+
+    return url.replace(/VM\d+/g, 'VM[XXX]');
+  });
+
+  urls.sort(String.caseInsensetiveComparator);
+  var isAdded = new Array(urls.length).fill(false);
+  var removedLines = [];
+
+  if (previousSnapshot) {
+    var diff = Diff.Diff.lineDiff(previousSnapshot, urls);
+    var removedEntries = diff.filter(entry => entry[0] === Diff.Diff.Operation.Delete).map(entry => entry[1]);
+    removedLines = [].concat.apply([], removedEntries);
+    var index = 0;
+
+    for (var entry of diff) {
+      if (entry[0] === Diff.Diff.Operation.Delete)
+        continue;
+
+      if (entry[0] === Diff.Diff.Operation.Equal) {
+        index += entry[1].length;
+        continue;
+      }
+
+      // eslint-disable-next-line no-unused-vars
+      for (var line of entry[1])
+        isAdded[index++] = true;
+    }
+
+    var addedEntries = diff.filter(entry => entry[0] === Diff.Diff.Operation.Insert).map(entry => entry[1]);
+    addedLines = [].concat.apply([], addedEntries);
+  }
+
+  TestRunner.addResult(`Removed: ${removedLines.length} uiSourceCodes`);
+
+  for (var url of removedLines)
+    TestRunner.addResult('[-] ' + url);
+
+  TestRunner.addResult(`Workspace: ${urls.length} uiSourceCodes.`);
+
+  for (var i = 0; i < urls.length; ++i) {
+    var url = urls[i];
+    var prefix = (isAdded[i] ? '[+] ' : '    ');
+    TestRunner.addResult(prefix + url);
+  }
+
+  return urls;
+};
+
+BindingsTestRunner.attachFrame = function(frameId, url, evalSourceURL) {
+  var evalSource = `(${attachFrame.toString()})('${frameId}', '${url}')`;
+
+  if (evalSourceURL)
+    evalSource += '//# sourceURL=' + evalSourceURL;
+
+  return TestRunner.evaluateInPageAsync(evalSource);
+
+  function attachFrame(frameId, url) {
+    var frame = document.createElement('iframe');
+    frame.src = url;
+    frame.id = frameId;
+    document.body.appendChild(frame);
+    return new Promise(x => frame.onload = x);
+  }
+};
+
+BindingsTestRunner.detachFrame = function(frameId, evalSourceURL) {
+  var evalSource = `(${detachFrame.toString()})('${frameId}')`;
+
+  if (evalSourceURL)
+    evalSource += '//# sourceURL=' + evalSourceURL;
+
+  return TestRunner.evaluateInPagePromise(evalSource);
+
+  function detachFrame(frameId) {
+    var frame = document.getElementById(frameId);
+    frame.remove();
+  }
+};
+
+BindingsTestRunner.navigateFrame = function(frameId, navigateURL, evalSourceURL) {
+  var evalSource = `(${navigateFrame.toString()})('${frameId}', '${navigateURL}')`;
+
+  if (evalSourceURL)
+    evalSource += '//# sourceURL=' + evalSourceURL;
+
+  return TestRunner.evaluateInPageAsync(evalSource);
+
+  function navigateFrame(frameId, url) {
+    var frame = document.getElementById(frameId);
+    frame.src = url;
+    return new Promise(x => frame.onload = x);
+  }
+};
+
+BindingsTestRunner.attachShadowDOM = function(id, templateSelector, evalSourceURL) {
+  var evalSource = `(${createShadowDOM.toString()})('${id}', '${templateSelector}')`;
+
+  if (evalSourceURL)
+    evalSource += '//# sourceURL=' + evalSourceURL;
+
+  return TestRunner.evaluateInPagePromise(evalSource);
+
+  function createShadowDOM(id, templateSelector) {
+    var shadowHost = document.createElement('div');
+    shadowHost.setAttribute('id', id);
+
+    let shadowRoot = shadowHost.attachShadow({mode: 'open'});
+
+    var t = document.querySelector(templateSelector);
+    var instance = t.content.cloneNode(true);
+    shadowRoot.appendChild(instance);
+    document.body.appendChild(shadowHost);
+  }
+};
+
+BindingsTestRunner.detachShadowDOM = function(id, evalSourceURL) {
+  var evalSource = `(${removeShadowDOM.toString()})('${id}')`;
+
+  if (evalSourceURL)
+    evalSource += '//# sourceURL=' + evalSourceURL;
+
+  return TestRunner.evaluateInPagePromise(evalSource);
+
+  function removeShadowDOM(id) {
+    document.querySelector('#' + id).remove();
+  }
+};
+
+BindingsTestRunner.waitForStyleSheetRemoved = function(urlSuffix) {
+  var fulfill;
+  var promise = new Promise(x => fulfill = x);
+  TestRunner.cssModel.addEventListener(SDK.CSSModel.Events.StyleSheetRemoved, onStyleSheetRemoved);
+  return promise;
+
+  function onStyleSheetRemoved(event) {
+    var styleSheetHeader = event.data;
+
+    if (!styleSheetHeader.resourceURL().endsWith(urlSuffix))
+      return;
+
+    TestRunner.cssModel.removeEventListener(SDK.CSSModel.Events.StyleSheetRemoved, onStyleSheetRemoved);
+    fulfill();
+  }
+};
+
+TestRunner.addSniffer(Bindings.CompilerScriptMapping.prototype, '_sourceMapAttachedForTest', onSourceMap, true);
+TestRunner.addSniffer(Bindings.SASSSourceMapping.prototype, '_sourceMapAttachedForTest', onSourceMap, true);
+var sourceMapCallbacks = new Map();
+
+function onSourceMap(sourceMap) {
+  for (var urlSuffix of sourceMapCallbacks.keys()) {
+    if (sourceMap.url().endsWith(urlSuffix)) {
+      var callback = sourceMapCallbacks.get(urlSuffix);
+      callback.call(null);
+      sourceMapCallbacks.delete(urlSuffix);
+    }
+  }
+}
+
+BindingsTestRunner.waitForSourceMap = function(sourceMapURLSuffix) {
+  var fulfill;
+  var promise = new Promise(x => fulfill = x);
+  sourceMapCallbacks.set(sourceMapURLSuffix, fulfill);
+  return promise;
+};
+
+var locationPool = new Bindings.LiveLocationPool();
+var nameSymbol = Symbol('LiveLocationNameForTest');
+var createdSymbol = Symbol('LiveLocationCreated');
+
+BindingsTestRunner.createDebuggerLiveLocation = function(name, urlSuffix, lineNumber, columnNumber) {
+  var script = TestRunner.debuggerModel.scripts().find(script => script.sourceURL.endsWith(urlSuffix));
+  var rawLocation = TestRunner.debuggerModel.createRawLocation(script, lineNumber || 0, columnNumber || 0);
+  return Bindings.debuggerWorkspaceBinding.createLiveLocation(
+      rawLocation, updateDelegate.bind(null, name), locationPool);
+};
+
+BindingsTestRunner.createCSSLiveLocation = function(name, urlSuffix, lineNumber, columnNumber) {
+  var header = TestRunner.cssModel.styleSheetHeaders().find(header => header.resourceURL().endsWith(urlSuffix));
+  var rawLocation = new SDK.CSSLocation(header, lineNumber || 0, columnNumber || 0);
+  return Bindings.cssWorkspaceBinding.createLiveLocation(rawLocation, updateDelegate.bind(null, name), locationPool);
+};
+
+function updateDelegate(name, liveLocation) {
+  liveLocation[nameSymbol] = name;
+  var hint = (liveLocation[createdSymbol] ? '[ UPDATE ]' : '[ CREATE ]');
+  liveLocation[createdSymbol] = true;
+  BindingsTestRunner.dumpLocation(liveLocation, hint);
+}
+
+BindingsTestRunner.dumpLocation = function(liveLocation, hint) {
+  hint = hint || '[  GET   ]';
+  var prefix = `${hint}  LiveLocation-${liveLocation[nameSymbol]}: `;
+  var uiLocation = liveLocation.uiLocation();
+
+  if (!uiLocation) {
+    TestRunner.addResult(prefix + 'null');
+    return;
+  }
+
+  TestRunner.addResult(
+      prefix + uiLocation.uiSourceCode.url() + ':' + uiLocation.lineNumber + ':' + uiLocation.columnNumber);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js
new file mode 100644
index 0000000..6c77c419
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js
@@ -0,0 +1,292 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+InspectorFrontendHost.isolatedFileSystem = function(name) {
+  return BindingsTestRunner.TestFileSystem._instances[name];
+};
+
+BindingsTestRunner.TestFileSystem = function(fileSystemPath) {
+  this.root = new BindingsTestRunner.TestFileSystem.Entry(this, '', true, null);
+  this.fileSystemPath = fileSystemPath;
+};
+
+BindingsTestRunner.TestFileSystem._instances = {};
+
+BindingsTestRunner.TestFileSystem.prototype = {
+  dumpAsText: function() {
+    var result = [];
+    dfs(this.root, '');
+    result[0] = this.fileSystemPath;
+    return result.join('\n');
+
+    function dfs(node, indent) {
+      result.push(indent + node.name);
+      var newIndent = indent + '    ';
+
+      for (var child of node._children)
+        dfs(child, newIndent);
+    }
+  },
+
+  reportCreatedPromise: function() {
+    return new Promise(fulfill => this.reportCreated(fulfill));
+  },
+
+  reportCreated: function(callback) {
+    var fileSystemPath = this.fileSystemPath;
+    BindingsTestRunner.TestFileSystem._instances[this.fileSystemPath] = this;
+
+    InspectorFrontendHost.events.dispatchEventToListeners(
+        InspectorFrontendHostAPI.Events.FileSystemAdded,
+        {fileSystem: {fileSystemPath: this.fileSystemPath, fileSystemName: this.fileSystemPath}});
+
+    Persistence.isolatedFileSystemManager.addEventListener(
+        Persistence.IsolatedFileSystemManager.Events.FileSystemAdded, created);
+
+    function created(event) {
+      var fileSystem = event.data;
+
+      if (fileSystem.path() !== fileSystemPath)
+        return;
+
+      Persistence.isolatedFileSystemManager.removeEventListener(
+          Persistence.IsolatedFileSystemManager.Events.FileSystemAdded, created);
+      callback(fileSystem);
+    }
+  },
+
+  reportRemoved: function() {
+    delete BindingsTestRunner.TestFileSystem._instances[this.fileSystemPath];
+    InspectorFrontendHost.events.dispatchEventToListeners(
+        InspectorFrontendHostAPI.Events.FileSystemRemoved, this.fileSystemPath);
+  },
+
+  addFileMapping: function(urlPrefix, pathPrefix) {
+    var fileSystemMapping = new Persistence.FileSystemMapping(Persistence.isolatedFileSystemManager);
+    fileSystemMapping.addFileSystem(this.fileSystemPath);
+    fileSystemMapping.addFileMapping(this.fileSystemPath, urlPrefix, pathPrefix);
+    fileSystemMapping.dispose();
+    Persistence.fileSystemMapping._loadFromSettings();
+  },
+
+  addFile: function(path, content, lastModified) {
+    var pathTokens = path.split('/');
+    var node = this.root;
+    var folders = pathTokens.slice(0, pathTokens.length - 1);
+    var fileName = pathTokens.peekLast();
+
+    for (var folder of folders) {
+      var dir = node._childrenMap[folder];
+
+      if (!dir)
+        dir = node.mkdir(folder);
+
+      node = dir;
+    }
+
+    var file = node.addFile(fileName, content);
+
+    if (lastModified)
+      file._timestamp = lastModified;
+
+    return file;
+  }
+};
+
+BindingsTestRunner.TestFileSystem.Entry = function(fileSystem, name, isDirectory, parent) {
+  this._fileSystem = fileSystem;
+  this.name = name;
+  this._children = [];
+  this._childrenMap = {};
+  this.isDirectory = isDirectory;
+  this._timestamp = 1000000;
+  this._parent = parent;
+};
+
+BindingsTestRunner.TestFileSystem.Entry.prototype = {
+  get fullPath() {
+    return (this.parent ? this.parent.fullPath + '/' + this.name : '');
+  },
+
+  remove: function(success, failure) {
+    this._parent._removeChild(this, success, failure);
+  },
+
+  _removeChild: function(child, success, failure) {
+    var index = this._children.indexOf(child);
+
+    if (index === -1) {
+      failure('Failed to remove file: file not found.');
+      return;
+    }
+
+    var fullPath = this._fileSystem.fileSystemPath + child.fullPath;
+    this._children.splice(index, 1);
+    delete this._childrenMap[child.name];
+    child.parent = null;
+
+    InspectorFrontendHost.events.dispatchEventToListeners(
+        InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
+        {changed: [], added: [], removed: [fullPath]});
+
+    success();
+  },
+
+  mkdir: function(name) {
+    var child = new BindingsTestRunner.TestFileSystem.Entry(this._fileSystem, name, true, this);
+    this._childrenMap[name] = child;
+    this._children.push(child);
+    child.parent = this;
+    return child;
+  },
+
+  addFile: function(name, content) {
+    var child = new BindingsTestRunner.TestFileSystem.Entry(this._fileSystem, name, false, this);
+    this._childrenMap[name] = child;
+    this._children.push(child);
+    child.parent = this;
+
+    child.content = new Blob([content], {type: 'text/plain'});
+
+    var fullPath = this._fileSystem.fileSystemPath + child.fullPath;
+
+    InspectorFrontendHost.events.dispatchEventToListeners(
+        InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
+        {changed: [], added: [fullPath], removed: []});
+
+    return child;
+  },
+
+  setContent: function(content) {
+    this.content = new Blob([content], {type: 'text/plain'});
+
+    this._timestamp += 1000;
+    var fullPath = this._fileSystem.fileSystemPath + this.fullPath;
+
+    InspectorFrontendHost.events.dispatchEventToListeners(
+        InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
+        {changed: [fullPath], added: [], removed: []});
+  },
+
+  createReader: function() {
+    return new BindingsTestRunner.TestFileSystem.Reader(this._children);
+  },
+
+  createWriter: function(success, failure) {
+    success(new BindingsTestRunner.TestFileSystem.Writer(this));
+  },
+
+  file: function(callback) {
+    callback(this.content);
+  },
+
+  getDirectory: function(path, noop, callback, errorCallback) {
+    this.getEntry(path, noop, callback, errorCallback);
+  },
+
+  getFile: function(path, noop, callback, errorCallback) {
+    this.getEntry(path, noop, callback, errorCallback);
+  },
+
+  _createEntry: function(path, options, callback, errorCallback) {
+    var tokens = path.split('/');
+    var name = tokens.pop();
+    var parentEntry = this;
+
+    for (var token of tokens)
+      parentEntry = parentEntry._childrenMap[token];
+
+    var entry = parentEntry._childrenMap[name];
+
+    if (entry && options.exclusive) {
+      errorCallback(new DOMException('File exists: ' + path, 'InvalidModificationError'));
+      return;
+    }
+
+    if (!entry)
+      entry = parentEntry.addFile(name, '');
+
+    callback(entry);
+  },
+
+  getEntry: function(path, options, callback, errorCallback) {
+    if (path.startsWith('/'))
+      path = path.substring(1);
+
+    if (options && options.create) {
+      this._createEntry(path, options, callback, errorCallback);
+      return;
+    }
+
+    if (!path) {
+      callback(this);
+      return;
+    }
+
+    var entry = this;
+
+    for (var token of path.split('/'))
+      entry = entry._childrenMap[token];
+
+    (entry ? callback(entry) : errorCallback(new DOMException('Path not found: ' + path, 'NotFoundError')));
+  },
+
+  getMetadata: function(success, failure) {
+    success({modificationTime: new Date(this._timestamp), size: (this.isDirectory ? 0 : this.content.size)});
+  },
+
+  moveTo: function(parent, newName, callback, errorCallback) {
+    this._parent._children.remove(this);
+    delete this._parent._childrenMap[this.name];
+    this._parent = parent;
+    this._parent._children.push(this);
+    this.name = newName;
+    this._parent._childrenMap[this.name] = this;
+    callback(this);
+  },
+
+  getParent: function(callback, errorCallback) {
+    callback(this._parent);
+  }
+};
+
+BindingsTestRunner.TestFileSystem.Reader = function(children) {
+  this._children = children;
+};
+
+BindingsTestRunner.TestFileSystem.Reader.prototype = {
+  readEntries: function(callback) {
+    var children = this._children;
+    this._children = [];
+    callback(children);
+  }
+};
+
+BindingsTestRunner.TestFileSystem.Writer = function(entry) {
+  this._entry = entry;
+  this._modificationTimesDelta = 500;
+};
+
+BindingsTestRunner.TestFileSystem.Writer.prototype = {
+  write: function(blob) {
+    this._entry._timestamp += this._modificationTimesDelta;
+    this._entry.content = blob;
+
+    if (this.onwriteend)
+      this.onwriteend();
+  },
+
+  truncate: function(num) {
+    this._entry._timestamp += this._modificationTimesDelta;
+    this._entry.content = this._entry.content.slice(0, num);
+
+    if (this.onwriteend)
+      this.onwriteend();
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/PersistenceTestRunner.js b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/PersistenceTestRunner.js
new file mode 100644
index 0000000..6ec92a4
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/PersistenceTestRunner.js
@@ -0,0 +1,113 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+Runtime.experiments.enableForTest('persistenceValidation');
+
+Persistence.PersistenceBinding.prototype.toString = function() {
+  var lines = [
+    '{', '       network: ' + this.network.url(), '    fileSystem: ' + this.fileSystem.url(),
+    '    exactMatch: ' + this.exactMatch, '}'
+  ];
+
+  return lines.join('\n');
+};
+
+BindingsTestRunner.waitForBinding = function(fileName) {
+  var uiSourceCodes = Workspace.workspace.uiSourceCodes();
+
+  for (var uiSourceCode of uiSourceCodes) {
+    var binding = Persistence.persistence.binding(uiSourceCode);
+
+    if (!binding)
+      continue;
+
+    if (uiSourceCode.name() === fileName)
+      return Promise.resolve(binding);
+  }
+
+  return TestRunner.waitForEvent(
+      Persistence.Persistence.Events.BindingCreated, Persistence.persistence,
+      binding => binding.network.name() === fileName || binding.fileSystem.name() === fileName);
+};
+
+BindingsTestRunner.addFooJSFile = function(fs) {
+  return fs.root.mkdir('inspector')
+      .mkdir('persistence')
+      .mkdir('resources')
+      .addFile('foo.js', '\n\nwindow.foo = ()=>\'foo\';');
+};
+
+BindingsTestRunner.forceUseDefaultMapping = function() {
+  Persistence.persistence._setMappingForTest((bindingCreated, bindingRemoved) => {
+    return new Persistence.DefaultMapping(
+        Workspace.workspace, Persistence.fileSystemMapping, bindingCreated, bindingRemoved);
+  });
+};
+
+BindingsTestRunner.initializeTestMapping = function() {
+  var testMapping;
+
+  Persistence.persistence._setMappingForTest((bindingCreated, bindingRemoved) => {
+    testMapping = new TestMapping(bindingCreated, bindingRemoved);
+    return testMapping;
+  });
+
+  return testMapping;
+};
+
+class TestMapping {
+  constructor(onBindingAdded, onBindingRemoved) {
+    this._onBindingAdded = onBindingAdded;
+    this._onBindingRemoved = onBindingRemoved;
+    this._bindings = new Set();
+  }
+
+  async addBinding(urlSuffix) {
+    if (this._findBinding(urlSuffix)) {
+      TestRunner.addResult(`FAILED TO ADD BINDING: binding already exists for ${urlSuffix}`);
+      TestRunner.completeTest();
+      return;
+    }
+
+    var networkUISourceCode = await TestRunner.waitForUISourceCode(urlSuffix, Workspace.projectTypes.Network);
+    var fileSystemUISourceCode = await TestRunner.waitForUISourceCode(urlSuffix, Workspace.projectTypes.FileSystem);
+    var binding = new Persistence.PersistenceBinding(networkUISourceCode, fileSystemUISourceCode, false);
+    this._bindings.add(binding);
+    this._onBindingAdded.call(null, binding);
+  }
+
+  _findBinding(urlSuffix) {
+    for (var binding of this._bindings) {
+      if (binding.network.url().endsWith(urlSuffix))
+        return binding;
+    }
+
+    return null;
+  }
+
+  async removeBinding(urlSuffix) {
+    var binding = this._findBinding(urlSuffix);
+
+    if (!binding) {
+      TestRunner.addResult(`FAILED TO REMOVE BINDING: binding does not exist for ${urlSuffix}`);
+      TestRunner.completeTest();
+      return;
+    }
+
+    this._bindings.delete(binding);
+    this._onBindingRemoved.call(null, binding);
+  }
+
+  dispose() {
+    for (var binding of this._bindings)
+      this._onBindingRemoved.call(null, binding);
+
+    this._bindings.clear();
+  }
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/module.json
new file mode 100644
index 0000000..82d789a
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/bindings_test_runner/module.json
@@ -0,0 +1,22 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "workspace",
+    "diff",
+    "bindings",
+    "persistence"
+  ],
+  "scripts": [
+    "BindingsTestRunner.js",
+    "IsolatedFilesystemTestRunner.js",
+    "AutomappingTestRunner.js",
+    "PersistenceTestRunner.js"
+  ],
+  "skip_compilation": [
+    "BindingsTestRunner.js",
+    "IsolatedFilesystemTestRunner.js",
+    "AutomappingTestRunner.js",
+    "PersistenceTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/coverage_test_runner/CoverageTestRunner.js b/third_party/WebKit/Source/devtools/front_end/coverage_test_runner/CoverageTestRunner.js
new file mode 100644
index 0000000..185834f8
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/coverage_test_runner/CoverageTestRunner.js
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+CoverageTestRunner.startCoverage = function() {
+  UI.viewManager.showView('coverage');
+  var coverageView = self.runtime.sharedInstance(Coverage.CoverageView);
+  coverageView._startRecording();
+};
+
+CoverageTestRunner.stopCoverage = function() {
+  var coverageView = self.runtime.sharedInstance(Coverage.CoverageView);
+  return coverageView._stopRecording();
+};
+
+CoverageTestRunner.sourceDecorated = async function(source) {
+  await UI.inspectorView.showPanel('sources');
+  var decoratePromise = TestRunner.addSnifferPromise(Coverage.CoverageView.LineDecorator.prototype, '_innerDecorate');
+  var sourceFrame = await new Promise(fulfill => SourcesTestRunner.showScriptSource(source, fulfill));
+  await decoratePromise;
+  return sourceFrame;
+};
+
+CoverageTestRunner.dumpDecorations = async function(source) {
+  var sourceFrame = await CoverageTestRunner.sourceDecorated(source);
+  CoverageTestRunner.dumpDecorationsInSourceFrame(sourceFrame);
+};
+
+CoverageTestRunner.findCoverageNodeForURL = function(url) {
+  var coverageListView = self.runtime.sharedInstance(Coverage.CoverageView)._listView;
+  var rootNode = coverageListView._dataGrid.rootNode();
+
+  for (var child of rootNode.children) {
+    if (child._coverageInfo.url().endsWith(url))
+      return child;
+  }
+
+  return null;
+};
+
+CoverageTestRunner.dumpDecorationsInSourceFrame = function(sourceFrame) {
+  var markerMap = new Map([['used', '+'], ['unused', '-']]);
+  var codeMirror = sourceFrame.textEditor.codeMirror();
+
+  for (var line = 0; line < codeMirror.lineCount(); ++line) {
+    var text = codeMirror.getLine(line);
+    var markerType = ' ';
+    var lineInfo = codeMirror.lineInfo(line);
+
+    if (!lineInfo)
+      continue;
+
+    var gutterElement = lineInfo.gutterMarkers && lineInfo.gutterMarkers['CodeMirror-gutter-coverage'];
+
+    if (gutterElement) {
+      var markerClass = /^text-editor-coverage-(\w*)-marker$/.exec(gutterElement.classList)[1];
+      markerType = markerMap.get(markerClass) || gutterElement.classList;
+    }
+
+    TestRunner.addResult(`${line}: ${markerType} ${text}`);
+  }
+};
+
+CoverageTestRunner.dumpCoverageListView = function() {
+  var coverageListView = self.runtime.sharedInstance(Coverage.CoverageView)._listView;
+  var dataGrid = coverageListView._dataGrid;
+  dataGrid.updateInstantly();
+
+  for (var child of dataGrid.rootNode().children) {
+    var data = child._coverageInfo;
+    var url = TestRunner.formatters.formatAsURL(data.url());
+
+    if (url.endsWith('-test.js') || url.endsWith('.html'))
+      continue;
+
+    var type = Coverage.CoverageListView._typeToString(data.type());
+    TestRunner.addResult(`${url} ${type} used: ${data.usedSize()} unused: ${data.unusedSize()} total: ${data.size()}`);
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/coverage_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/coverage_test_runner/module.json
new file mode 100644
index 0000000..4f200a4
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/coverage_test_runner/module.json
@@ -0,0 +1,14 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "coverage",
+    "sources_test_runner"
+  ],
+  "scripts": [
+    "CoverageTestRunner.js"
+  ],
+  "skip_compilation": [
+    "CoverageTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js b/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
index b891fc2..6500f66 100644
--- a/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
+++ b/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
@@ -125,6 +125,23 @@
   }
 
   /**
+   * @param {!Element} element
+   * @param {string} newText
+   * @param {boolean} longText
+   */
+  static setElementText(element, newText, longText) {
+    if (longText && newText.length > 1000) {
+      element.textContent = newText.trimEnd(1000);
+      element.title = newText;
+      element[DataGrid.DataGrid._longTextSymbol] = newText;
+    } else {
+      element.textContent = newText;
+      element.title = '';
+      element[DataGrid.DataGrid._longTextSymbol] = undefined;
+    }
+  }
+
+  /**
    * @param {boolean} isStriped
    */
   setStriped(isStriped) {
@@ -351,6 +368,8 @@
     }
 
     this._editing = true;
+    if (element[DataGrid.DataGrid._longTextSymbol])
+      element.textContent = element[DataGrid.DataGrid._longTextSymbol];
     UI.InplaceEditor.startEditing(element, this._startEditingConfig(element));
 
     element.getComponentSelection().selectAllChildren(element);
@@ -368,8 +387,7 @@
    * @return {!UI.InplaceEditor.Config}
    */
   _startEditingConfig(element) {
-    return new UI.InplaceEditor.Config(
-        this._editingCommitted.bind(this), this._editingCancelled.bind(this), element.textContent);
+    return new UI.InplaceEditor.Config(this._editingCommitted.bind(this), this._editingCancelled.bind(this));
   }
 
   /**
@@ -437,6 +455,9 @@
       }
     }
 
+    // Show trimmed text after editing.
+    DataGrid.DataGrid.setElementText(element, newText, !!column.longText);
+
     if (textBeforeEditing === newText) {
       this._editingCancelled(element);
       moveToNextIfNeeded.call(this, false);
@@ -1216,6 +1237,7 @@
 DataGrid.DataGrid._preferredWidthSymbol = Symbol('preferredWidth');
 DataGrid.DataGrid._columnIdSymbol = Symbol('columnId');
 DataGrid.DataGrid._sortIconSymbol = Symbol('sortIcon');
+DataGrid.DataGrid._longTextSymbol = Symbol('longText');
 
 DataGrid.DataGrid.ColumnResizePadding = 24;
 DataGrid.DataGrid.CenterResizerOverBorderAdjustment = 3;
@@ -1581,13 +1603,10 @@
     var cell = this.createTD(columnId);
 
     var data = this.data[columnId];
-    if (data instanceof Node) {
+    if (data instanceof Node)
       cell.appendChild(data);
-    } else if (data !== null) {
-      cell.textContent = data;
-      if (this.dataGrid._columns[columnId].longText)
-        cell.title = data;
-    }
+    else if (data !== null)
+      DataGrid.DataGrid.setElementText(cell, /** @type {string} */ (data), !!this.dataGrid._columns[columnId].longText);
 
     return cell;
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/DataGridTestRunner.js b/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/DataGridTestRunner.js
index bff22d7..ad3e492 100644
--- a/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/DataGridTestRunner.js
+++ b/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/DataGridTestRunner.js
@@ -78,3 +78,43 @@
   }
   return output.join('\n');
 };
+
+DataGridTestRunner.validateDataGrid = function(root) {
+  var children = root.children;
+
+  for (var i = 0; i < children.length; ++i) {
+    var child = children[i];
+
+    if (child.parent !== root)
+      throw 'Wrong parent for child ' + child.data.id + ' of ' + root.data.id;
+
+    if (child.nextSibling !== ((i + 1 === children.length ? null : children[i + 1])))
+      throw 'Wrong child.nextSibling for ' + child.data.id + ' (' + i + ' of ' + children.length + ') ';
+
+    if (child.previousSibling !== ((i ? children[i - 1] : null)))
+      throw 'Wrong child.previousSibling for ' + child.data.id + ' (' + i + ' of ' + children.length + ') ';
+
+    if (child.parent && !child.parent._isRoot && child.depth !== root.depth + 1)
+      throw 'Wrong depth for ' + child.data.id + ' expected ' + (root.depth + 1) + ' but got ' + child.depth;
+
+    DataGridTestRunner.validateDataGrid(child);
+  }
+
+  var selectedNode = root.dataGrid.selectedNode;
+
+  if (!root.parent && selectedNode) {
+    if (!selectedNode.selectable)
+      throw 'Selected node is not selectable';
+
+    for (var node = selectedNode; node && node !== root; node = node.parent) {
+    }
+
+    if (!node)
+      throw 'Selected node (' + selectedNode.data.id + ') is not within the DataGrid';
+  }
+};
+
+DataGridTestRunner.dumpAndValidateDataGrid = function(root) {
+  DataGridTestRunner.dumpDataGrid(root);
+  DataGridTestRunner.validateDataGrid(root);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/module.json
index 89ab830..8a1da51 100644
--- a/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/data_grid_test_runner/module.json
@@ -5,5 +5,8 @@
   ],
   "scripts": [
     "DataGridTestRunner.js"
+  ],
+  "skip_compilation": [
+    "DataGridTestRunner.js"
   ]
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/device_mode_test_runner/DeviceModeTestRunner.js b/third_party/WebKit/Source/devtools/front_end/device_mode_test_runner/DeviceModeTestRunner.js
new file mode 100644
index 0000000..017b5d59
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/device_mode_test_runner/DeviceModeTestRunner.js
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+DeviceModeTestRunner.buildFakePhone = function(overrides) {
+  var StandardPhoneJSON = {
+    'show-by-default': false,
+    'title': 'Fake Phone 1',
+
+    'screen': {
+      'horizontal': {'width': 480, 'height': 320},
+
+      'device-pixel-ratio': 2,
+
+      'vertical': {'width': 320, 'height': 480}
+    },
+
+    'capabilities': ['touch', 'mobile'],
+    'user-agent': 'fakeUserAgent',
+    'type': 'phone',
+
+    'modes': [
+      {
+        'title': 'default',
+        'orientation': 'vertical',
+
+        'insets': {'left': 0, 'top': 0, 'right': 0, 'bottom': 0}
+      },
+      {
+        'title': 'default',
+        'orientation': 'horizontal',
+
+        'insets': {'left': 0, 'top': 0, 'right': 0, 'bottom': 0}
+      }
+    ]
+  };
+
+  var json = Object.assign(StandardPhoneJSON, overrides || {});
+  return Emulation.EmulatedDevice.fromJSONV1(json);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/device_mode_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/device_mode_test_runner/module.json
new file mode 100644
index 0000000..dde42c2a
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/device_mode_test_runner/module.json
@@ -0,0 +1,13 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "emulation"
+  ],
+  "scripts": [
+    "DeviceModeTestRunner.js"
+  ],
+  "skip_compilation": [
+    "DeviceModeTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/EditDOMTestRunner.js b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/EditDOMTestRunner.js
new file mode 100644
index 0000000..e2bb707
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/EditDOMTestRunner.js
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ElementsTestRunner.doAddAttribute = function(testName, dataNodeId, attributeText, next) {
+  ElementsTestRunner.domActionTestForNodeId(testName, dataNodeId, testBody, next);
+
+  function testBody(node, done) {
+    ElementsTestRunner.editNodePart(node, 'webkit-html-attribute');
+    eventSender.keyDown('Tab');
+    TestRunner.deprecatedRunAfterPendingDispatches(testContinuation);
+
+    function testContinuation() {
+      var editorElement = UI.panels.elements._treeOutlines[0]._shadowRoot.getSelection().anchorNode.parentElement;
+      editorElement.textContent = attributeText;
+      editorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+      TestRunner.addSniffer(Elements.ElementsTreeOutline.prototype, '_updateModifiedNodes', done);
+    }
+  }
+};
+
+ElementsTestRunner.domActionTestForNodeId = function(testName, dataNodeId, testBody, next) {
+  function callback(testNode, continuation) {
+    ElementsTestRunner.selectNodeWithId(dataNodeId, continuation);
+  }
+
+  ElementsTestRunner.domActionTest(testName, callback, testBody, next);
+};
+
+ElementsTestRunner.domActionTest = function(testName, dataNodeSelectionCallback, testBody, next) {
+  var testNode = ElementsTestRunner.expandedNodeWithId(testName);
+  TestRunner.addResult('==== before ====');
+  ElementsTestRunner.dumpElementsTree(testNode);
+  dataNodeSelectionCallback(testNode, step0);
+
+  function step0(node) {
+    TestRunner.deprecatedRunAfterPendingDispatches(step1.bind(null, node));
+  }
+
+  function step1(node) {
+    testBody(node, step2);
+  }
+
+  function step2() {
+    TestRunner.addResult('==== after ====');
+    ElementsTestRunner.dumpElementsTree(testNode);
+    next();
+  }
+};
+
+ElementsTestRunner.editNodePart = function(node, className) {
+  var treeElement = ElementsTestRunner.firstElementsTreeOutline().findTreeElement(node);
+  var textElement = treeElement.listItemElement.getElementsByClassName(className)[0];
+
+  if (!textElement && treeElement.childrenListElement)
+    textElement = treeElement.childrenListElement.getElementsByClassName(className)[0];
+
+  treeElement._startEditingTarget(textElement);
+  return textElement;
+};
+
+ElementsTestRunner.editNodePartAndRun = function(node, className, newValue, step2, useSniffer) {
+  var editorElement = ElementsTestRunner.editNodePart(node, className);
+  editorElement.textContent = newValue;
+  editorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+
+  if (useSniffer)
+    TestRunner.addSniffer(Elements.ElementsTreeOutline.prototype, '_updateModifiedNodes', step2);
+  else
+    TestRunner.deprecatedRunAfterPendingDispatches(step2);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/ElementsPanelShadowSelectionOnRefreshTestRunner.js b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/ElementsPanelShadowSelectionOnRefreshTestRunner.js
new file mode 100644
index 0000000..ceea077
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/ElementsPanelShadowSelectionOnRefreshTestRunner.js
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ElementsTestRunner.selectReloadAndDump = function(next, node) {
+  ElementsTestRunner.selectNode(node).then(onSelected);
+  var reloaded = false;
+  var selected = false;
+
+  function onSelected() {
+    TestRunner.reloadPage(onReloaded);
+    TestRunner.addSniffer(Elements.ElementsPanel.prototype, '_lastSelectedNodeSelectedForTest', onReSelected);
+  }
+
+  function onReloaded() {
+    reloaded = true;
+    maybeDumpSelectedNode();
+  }
+
+  function onReSelected() {
+    selected = true;
+    maybeDumpSelectedNode();
+  }
+
+  function maybeDumpSelectedNode() {
+    if (!reloaded || !selected)
+      return;
+
+    var selectedElement = ElementsTestRunner.firstElementsTreeOutline().selectedTreeElement;
+    var nodeName = (selectedElement ? selectedElement.node().nodeNameInCorrectCase() : 'null');
+    TestRunner.addResult('Selected node: \'' + nodeName + '\'');
+    next();
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/ElementsTestRunner.js b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/ElementsTestRunner.js
index 502cd8c..6f70ac2 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/ElementsTestRunner.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/ElementsTestRunner.js
@@ -121,3 +121,962 @@
         EventListeners.EventListenersView.prototype, '_eventListenersArrivedForTest', listenersArrived);
   }
 };
+
+ElementsTestRunner.inlineStyleSection = function() {
+  return UI.panels.elements._stylesWidget._sectionBlocks[0].sections[0];
+};
+
+ElementsTestRunner.computedStyleWidget = function() {
+  return UI.panels.elements._computedStyleWidget;
+};
+
+ElementsTestRunner.dumpComputedStyle = function(doNotAutoExpand) {
+  var computed = ElementsTestRunner.computedStyleWidget();
+  var treeOutline = computed._propertiesOutline;
+  var children = treeOutline.rootElement().children();
+
+  for (var treeElement of children) {
+    var property = treeElement[Elements.ComputedStyleWidget._propertySymbol];
+
+    if (property.name === 'width' || property.name === 'height')
+      continue;
+
+    var dumpText = '';
+    dumpText += treeElement.title.querySelector('.property-name').textContent;
+    dumpText += ' ';
+    dumpText += treeElement.title.querySelector('.property-value').textContent;
+    TestRunner.addResult(dumpText);
+
+    if (doNotAutoExpand && !treeElement.expanded)
+      continue;
+
+    for (var trace of treeElement.children()) {
+      var title = trace.title;
+      var dumpText = '';
+
+      if (trace.title.classList.contains('property-trace-inactive'))
+        dumpText += 'OVERLOADED ';
+
+      dumpText += title.querySelector('.property-trace-value').textContent;
+      dumpText += ' - ';
+      dumpText += title.querySelector('.property-trace-selector').textContent;
+      var link = title.querySelector('.trace-link');
+
+      if (link)
+        dumpText += ' ' + extractLinkText(link);
+
+      TestRunner.addResult('    ' + dumpText);
+    }
+  }
+};
+
+ElementsTestRunner.findComputedPropertyWithName = function(name) {
+  var computed = ElementsTestRunner.computedStyleWidget();
+  var treeOutline = computed._propertiesOutline;
+  var children = treeOutline.rootElement().children();
+
+  for (var treeElement of children) {
+    var property = treeElement[Elements.ComputedStyleWidget._propertySymbol];
+
+    if (property.name === name)
+      return treeElement;
+  }
+
+  return null;
+};
+
+ElementsTestRunner.firstMatchedStyleSection = function() {
+  return UI.panels.elements._stylesWidget._sectionBlocks[0].sections[1];
+};
+
+ElementsTestRunner.firstMediaTextElementInSection = function(section) {
+  return section.element.querySelector('.media-text');
+};
+
+ElementsTestRunner.querySelector = async function(selector, callback) {
+  var doc = await TestRunner.domModel.requestDocumentPromise();
+  var nodeId = await TestRunner.domModel.querySelector(doc.id, selector);
+  callback(TestRunner.domModel.nodeForId(nodeId));
+};
+
+ElementsTestRunner.shadowRootByHostId = function(idValue, callback) {
+  function shadowRootMatches(node) {
+    return node.isShadowRoot() && node.parentNode.getAttribute('id') === idValue;
+  }
+
+  ElementsTestRunner.findNode(shadowRootMatches, callback);
+};
+
+ElementsTestRunner.nodeWithClass = function(classValue, callback) {
+  function nodeClassMatches(node) {
+    var classAttr = node.getAttribute('class');
+    return classAttr && classAttr.indexOf(classValue) > -1;
+  }
+
+  ElementsTestRunner.findNode(nodeClassMatches, callback);
+};
+
+ElementsTestRunner.expandedNodeWithId = function(idValue) {
+  var result;
+  ElementsTestRunner.nodeWithId(idValue, node => result = node);
+  return result;
+};
+
+function waitForStylesRebuild(matchFunction, callback, requireRebuild) {
+  (function sniff(node, rebuild) {
+    if ((rebuild || !requireRebuild) && node && matchFunction(node)) {
+      callback();
+      return;
+    }
+
+    TestRunner.addSniffer(Elements.StylesSidebarPane.prototype, '_nodeStylesUpdatedForTest', sniff);
+  })(null);
+}
+
+ElementsTestRunner.waitForStyles = function(idValue, callback, requireRebuild) {
+  callback = TestRunner.safeWrap(callback);
+
+  function nodeWithId(node) {
+    return node.getAttribute('id') === idValue;
+  }
+
+  waitForStylesRebuild(nodeWithId, callback, requireRebuild);
+};
+
+ElementsTestRunner.waitForStylesForClass = function(classValue, callback, requireRebuild) {
+  callback = TestRunner.safeWrap(callback);
+
+  function nodeWithClass(node) {
+    var classAttr = node.getAttribute('class');
+    return classAttr && classAttr.indexOf(classValue) > -1;
+  }
+
+  waitForStylesRebuild(nodeWithClass, callback, requireRebuild);
+};
+
+ElementsTestRunner.waitForSelectorCommitted = function(callback) {
+  TestRunner.addSniffer(Elements.StylePropertiesSection.prototype, '_editingSelectorCommittedForTest', callback);
+};
+
+ElementsTestRunner.waitForMediaTextCommitted = function(callback) {
+  TestRunner.addSniffer(Elements.StylePropertiesSection.prototype, '_editingMediaTextCommittedForTest', callback);
+};
+
+ElementsTestRunner.waitForStyleApplied = function(callback) {
+  TestRunner.addSniffer(Elements.StylePropertyTreeElement.prototype, 'styleTextAppliedForTest', callback);
+};
+
+ElementsTestRunner.selectNodeAndWaitForStyles = function(idValue, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var targetNode;
+  ElementsTestRunner.waitForStyles(idValue, stylesUpdated, true);
+  ElementsTestRunner.selectNodeWithId(idValue, nodeSelected);
+
+  function nodeSelected(node) {
+    targetNode = node;
+  }
+
+  function stylesUpdated() {
+    callback(targetNode);
+  }
+};
+
+ElementsTestRunner.selectPseudoElementAndWaitForStyles = function(parentId, pseudoType, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var targetNode;
+  waitForStylesRebuild(isPseudoElement, stylesUpdated, true);
+  ElementsTestRunner.findNode(isPseudoElement, nodeFound);
+
+  function nodeFound(node) {
+    targetNode = node;
+    Common.Revealer.reveal(node);
+  }
+
+  function stylesUpdated() {
+    callback(targetNode);
+  }
+
+  function isPseudoElement(node) {
+    return node.parentNode && node.parentNode.getAttribute('id') === parentId && node.pseudoType() === pseudoType;
+  }
+};
+
+ElementsTestRunner.selectNodeAndWaitForStylesWithComputed = function(idValue, callback) {
+  callback = TestRunner.safeWrap(callback);
+  ElementsTestRunner.selectNodeAndWaitForStyles(idValue, onSidebarRendered);
+
+  function onSidebarRendered(node) {
+    ElementsTestRunner.computedStyleWidget().doUpdate().then(callback.bind(null, node));
+  }
+};
+
+ElementsTestRunner.firstElementsTreeOutline = function() {
+  return UI.panels.elements._treeOutlines[0];
+};
+
+ElementsTestRunner.filterMatchedStyles = function(text) {
+  var regex = (text ? new RegExp(text, 'i') : null);
+  TestRunner.addResult('Filtering styles by: ' + text);
+  UI.panels.elements._stylesWidget._onFilterChanged(regex);
+};
+
+ElementsTestRunner.dumpRenderedMatchedStyles = function() {
+  var sectionBlocks = UI.panels.elements._stylesWidget._sectionBlocks;
+
+  for (var block of sectionBlocks) {
+    for (var section of block.sections) {
+      if (section.element.classList.contains('hidden'))
+        continue;
+
+      dumpRenderedSection(section);
+    }
+  }
+
+  function dumpRenderedSection(section) {
+    TestRunner.addResult(section._selectorElement.textContent + ' {');
+    var rootElement = section.propertiesTreeOutline.rootElement();
+
+    for (var i = 0; i < rootElement.childCount(); ++i)
+      dumpRenderedProperty(rootElement.childAt(i));
+
+    TestRunner.addResult('}');
+  }
+
+  function dumpRenderedProperty(property) {
+    var text = new Array(4).join(' ');
+    text += property.nameElement.textContent;
+    text += ':';
+
+    if (property.isExpandable())
+      text += (property.expanded ? 'v' : '>');
+    else
+      text += ' ';
+
+    text += property.valueElement.textContent;
+
+    if (property.listItemElement.classList.contains('filter-match'))
+      text = 'F' + text.substring(1);
+
+    TestRunner.addResult(text);
+
+    if (!property.expanded)
+      return;
+
+    var indent = new Array(8).join(' ');
+
+    for (var i = 0; i < property.childCount(); ++i) {
+      var childProperty = property.childAt(i);
+      var text = indent;
+      text += String.sprintf('%s: %s', childProperty.nameElement.textContent, childProperty.valueElement.textContent);
+
+      if (childProperty.listItemElement.classList.contains('filter-match'))
+        text = 'F' + text.substring(1);
+
+      TestRunner.addResult(text);
+    }
+  }
+};
+
+ElementsTestRunner.dumpSelectedElementStyles = function(
+    excludeComputed, excludeMatched, omitLonghands, includeSelectorGroupMarks) {
+  var sectionBlocks = UI.panels.elements._stylesWidget._sectionBlocks;
+
+  if (!excludeComputed)
+    ElementsTestRunner.dumpComputedStyle();
+
+  for (var block of sectionBlocks) {
+    for (var section of block.sections) {
+      if (section.style().parentRule && excludeMatched)
+        continue;
+
+      if (section.element.previousSibling && section.element.previousSibling.className === 'sidebar-separator') {
+        var nodeDescription = '';
+
+        if (section.element.previousSibling.firstElementChild)
+          nodeDescription = section.element.previousSibling.firstElementChild.shadowRoot.lastChild.textContent;
+
+        TestRunner.addResult('======== ' + section.element.previousSibling.textContent + nodeDescription + ' ========');
+      }
+
+      printStyleSection(section, omitLonghands, includeSelectorGroupMarks);
+    }
+  }
+};
+
+function printStyleSection(section, omitLonghands, includeSelectorGroupMarks) {
+  if (!section)
+    return;
+
+  TestRunner.addResult(
+      '[expanded] ' + ((section.propertiesTreeOutline.element.classList.contains('no-affect') ? '[no-affect] ' : '')));
+  var medias = section._titleElement.querySelectorAll('.media-list .media');
+
+  for (var i = 0; i < medias.length; ++i) {
+    var media = medias[i];
+    TestRunner.addResult(media.textContent);
+  }
+
+  var selector =
+      section._titleElement.querySelector('.selector') || section._titleElement.querySelector('.keyframe-key');
+  var selectorText = (includeSelectorGroupMarks ? buildMarkedSelectors(selector) : selector.textContent);
+  selectorText += selector.nextSibling.textContent;
+  var anchor = section._titleElement.querySelector('.styles-section-subtitle');
+
+  if (anchor) {
+    var anchorText = extractLinkText(anchor);
+    selectorText += String.sprintf(' (%s)', anchorText);
+  }
+
+  TestRunner.addResult(selectorText);
+  ElementsTestRunner.dumpStyleTreeOutline(section.propertiesTreeOutline, (omitLonghands ? 1 : 2));
+  TestRunner.addResult('');
+}
+
+function extractLinkText(element) {
+  var anchor = element.querySelector('.devtools-link');
+
+  if (!anchor)
+    return element.textContent;
+
+  var anchorText = anchor.textContent;
+  var info = Components.Linkifier._linkInfo(anchor);
+  var uiLocation = info && info.uiLocation;
+  var anchorTarget =
+      (uiLocation ?
+           uiLocation.uiSourceCode.name() + ':' + (uiLocation.lineNumber + 1) + ':' + (uiLocation.columnNumber + 1) :
+           '');
+  return anchorText + ' -> ' + anchorTarget;
+}
+
+function buildMarkedSelectors(element) {
+  var result = '';
+
+  for (var node = element.firstChild; node; node = node.nextSibling) {
+    if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains('selector-matches'))
+      result += '[$' + node.textContent + '$]';
+    else
+      result += node.textContent;
+  }
+
+  return result;
+}
+
+ElementsTestRunner.toggleStyleProperty = function(propertyName, checked) {
+  var treeItem = ElementsTestRunner.getElementStylePropertyTreeItem(propertyName);
+
+  treeItem._toggleEnabled({
+    target: {checked: checked},
+
+    consume: function() {}
+  });
+};
+
+ElementsTestRunner.toggleMatchedStyleProperty = function(propertyName, checked) {
+  var treeItem = ElementsTestRunner.getMatchedStylePropertyTreeItem(propertyName);
+
+  treeItem._toggleEnabled({
+    target: {checked: checked},
+
+    consume: function() {}
+  });
+};
+
+ElementsTestRunner.eventListenersWidget = function() {
+  UI.viewManager.showView('elements.eventListeners');
+  return self.runtime.sharedInstance(Elements.EventListenersWidget);
+};
+
+ElementsTestRunner.showEventListenersWidget = function() {
+  return UI.viewManager.showView('elements.eventListeners');
+};
+
+ElementsTestRunner.expandAndDumpSelectedElementEventListeners = function(callback, force) {
+  ElementsTestRunner.expandAndDumpEventListeners(
+      ElementsTestRunner.eventListenersWidget()._eventListenersView, callback, force);
+};
+
+ElementsTestRunner.removeFirstEventListener = function() {
+  var treeOutline = ElementsTestRunner.eventListenersWidget()._eventListenersView._treeOutline;
+  var listenerTypes = treeOutline.rootElement().children();
+
+  for (var i = 0; i < listenerTypes.length; i++) {
+    var listeners = listenerTypes[i].children();
+
+    if (listeners.length && !listenerTypes[i].hidden) {
+      listeners[0].eventListener().remove();
+      listeners[0]._removeListenerBar();
+      break;
+    }
+  }
+};
+
+ElementsTestRunner.dumpObjectPropertySectionDeep = function(section) {
+  function domNodeToString(node) {
+    if (node)
+      return '\'' + node.textContent + '\'';
+    else
+      return 'null';
+  }
+
+  function dumpTreeElementRecursively(treeElement, prefix) {
+    if ('nameElement' in treeElement) {
+      TestRunner.addResult(
+          prefix + domNodeToString(treeElement.nameElement) + ' => ' + domNodeToString(treeElement.valueElement));
+    } else {
+      TestRunner.addResult(prefix + treeElement.title);
+    }
+
+    for (var i = 0; i < treeElement.childCount(); i++)
+      dumpTreeElementRecursively(treeElement.childAt(i), prefix + '    ');
+  }
+
+  var childNodes = section.propertiesTreeOutline.rootElement().children();
+
+  for (var i = 0; i < childNodes.length; i++)
+    dumpTreeElementRecursively(childNodes[i], '');
+
+};
+
+ElementsTestRunner.getElementStylePropertyTreeItem = function(propertyName) {
+  return ElementsTestRunner.getFirstPropertyTreeItemForSection(ElementsTestRunner.inlineStyleSection(), propertyName);
+};
+
+ElementsTestRunner.getMatchedStylePropertyTreeItem = function(propertyName) {
+  var sectionBlocks = UI.panels.elements._stylesWidget._sectionBlocks;
+
+  for (var block of sectionBlocks) {
+    for (var section of block.sections) {
+      var treeItem = ElementsTestRunner.getFirstPropertyTreeItemForSection(section, propertyName);
+
+      if (treeItem)
+        return treeItem;
+    }
+  }
+
+  return null;
+};
+
+ElementsTestRunner.getFirstPropertyTreeItemForSection = function(section, propertyName) {
+  var outline = section.propertiesTreeOutline.rootElement();
+
+  for (var i = 0; i < outline.childCount(); ++i) {
+    var treeItem = outline.childAt(i);
+
+    if (treeItem.name === propertyName)
+      return treeItem;
+  }
+
+  return null;
+};
+
+ElementsTestRunner.dumpStyleTreeOutline = function(treeItem, depth) {
+  var children = treeItem.rootElement().children();
+
+  for (var i = 0; i < children.length; ++i)
+    ElementsTestRunner.dumpStyleTreeItem(children[i], '', depth || 2);
+};
+
+ElementsTestRunner.dumpStyleTreeItem = function(treeItem, prefix, depth) {
+  if (treeItem.listItemElement.textContent.indexOf(' width:') !== -1 ||
+      treeItem.listItemElement.textContent.indexOf(' height:') !== -1)
+    return;
+
+  if (treeItem.listItemElement.classList.contains('inherited'))
+    return;
+
+  var typePrefix = '';
+
+  if (treeItem.listItemElement.classList.contains('overloaded') ||
+      treeItem.listItemElement.classList.contains('inactive') ||
+      treeItem.listItemElement.classList.contains('not-parsed-ok'))
+    typePrefix += '/-- overloaded --/ ';
+
+  if (treeItem.listItemElement.classList.contains('disabled'))
+    typePrefix += '/-- disabled --/ ';
+
+  var textContent = treeItem.listItemElement.textContent;
+  TestRunner.addResult(prefix + typePrefix + textContent);
+
+  if (--depth) {
+    treeItem.expand();
+    var children = treeItem.children();
+
+    for (var i = 0; children && i < children.length; ++i)
+      ElementsTestRunner.dumpStyleTreeItem(children[i], prefix + '    ', depth);
+  }
+};
+
+ElementsTestRunner.dumpElementsTree = function(rootNode, depth, resultsArray) {
+  function beautify(element) {
+    return element.innerText.replace(/\u200b/g, '').replace(/\n/g, '\\n').trim();
+  }
+
+  function dumpMap(name, map) {
+    var result = [];
+
+    for (var id of map.keys())
+      result.push(id + '=' + map.get(id));
+
+    if (!result.length)
+      return '';
+
+    return name + ':[' + result.join(',') + ']';
+  }
+
+  function markersDataDump(treeItem) {
+    if (treeItem._elementCloseTag)
+      return '';
+
+    var markers = '';
+    var node = treeItem._node;
+
+    if (node) {
+      markers += dumpMap('markers', node._markers);
+      var dump = (node._subtreeMarkerCount ? 'subtreeMarkerCount:' + node._subtreeMarkerCount : '');
+
+      if (dump) {
+        if (markers)
+          markers += ', ';
+
+        markers += dump;
+      }
+
+      if (markers)
+        markers = ' [' + markers + ']';
+    }
+
+    return markers;
+  }
+
+  function print(treeItem, prefix, depth) {
+    if (!treeItem.root) {
+      var expander;
+
+      if (treeItem.isExpandable()) {
+        if (treeItem.expanded)
+          expander = '- ';
+        else
+          expander = '+ ';
+      } else {
+        expander = '  ';
+      }
+
+      var markers = markersDataDump(treeItem);
+      var value = prefix + expander + beautify(treeItem.listItemElement) + markers;
+
+      if (treeItem.shadowHostToolbar) {
+        value = prefix + expander + 'shadow-root ';
+
+        for (var i = 0; i < treeItem.shadowHostToolbar.children.length; ++i) {
+          var button = treeItem.shadowHostToolbar.children[i];
+          var toggled = button.disabled;
+          var name = ((toggled ? '<' : '')) + button.textContent + ((toggled ? '>' : ''));
+          value += name + ' ';
+        }
+      }
+
+      if (resultsArray)
+        resultsArray.push(value);
+      else
+        TestRunner.addResult(value);
+    }
+
+    if (!treeItem.expanded)
+      return;
+
+    var children = treeItem.children();
+    var newPrefix = (treeItem.root ? '' : prefix + '    ');
+
+    for (var i = 0; depth && children && i < children.length; ++i) {
+      if (!children[i]._elementCloseTag)
+        print(children[i], newPrefix, depth - 1);
+      else
+        print(children[i], prefix, depth);
+    }
+  }
+
+  var treeOutline = ElementsTestRunner.firstElementsTreeOutline();
+  treeOutline.runPendingUpdates();
+  print((rootNode ? treeOutline.findTreeElement(rootNode) : treeOutline.rootElement()), '', depth || 10000);
+};
+
+ElementsTestRunner.dumpDOMUpdateHighlights = function(rootNode, callback, depth) {
+  var hasHighlights = false;
+  TestRunner.addSniffer(Elements.ElementsTreeOutline.prototype, '_updateModifiedNodes', didUpdate);
+
+  function didUpdate() {
+    var treeOutline = ElementsTestRunner.firstElementsTreeOutline();
+    print((rootNode ? treeOutline.findTreeElement(rootNode) : treeOutline.rootElement()), '', depth || 10000);
+
+    if (!hasHighlights)
+      TestRunner.addResult('<No highlights>');
+
+    if (callback)
+      callback();
+  }
+
+  function print(treeItem, prefix, depth) {
+    if (!treeItem.root) {
+      var elementXPath = Components.DOMPresentationUtils.xPath(treeItem.node(), true);
+      var highlightedElements = treeItem.listItemElement.querySelectorAll('.dom-update-highlight');
+
+      for (var i = 0; i < highlightedElements.length; ++i) {
+        var element = highlightedElements[i];
+        var classList = element.classList;
+        var xpath = elementXPath;
+
+        if (classList.contains('webkit-html-attribute-name')) {
+          xpath += '/@' + element.textContent + ' (empty)';
+        } else if (classList.contains('webkit-html-attribute-value')) {
+          name = element.parentElement.querySelector('.webkit-html-attribute-name').textContent;
+          xpath += '/@' + name + ' ' + element.textContent;
+        } else if (classList.contains('webkit-html-text-node')) {
+          xpath += '/text() "' + element.textContent + '"';
+        }
+
+        TestRunner.addResult(prefix + xpath);
+        hasHighlights = true;
+      }
+    }
+
+    if (!treeItem.expanded)
+      return;
+
+    var children = treeItem.children();
+    var newPrefix = (treeItem.root ? '' : prefix + '    ');
+
+    for (var i = 0; depth && children && i < children.length; ++i) {
+      if (!children[i]._elementCloseTag)
+        print(children[i], newPrefix, depth - 1);
+    }
+  }
+};
+
+ElementsTestRunner.expandElementsTree = function(callback) {
+  var expandedSomething = false;
+  callback = TestRunner.safeWrap(callback);
+
+  function expand(treeItem) {
+    var children = treeItem.children();
+
+    for (var i = 0; children && i < children.length; ++i) {
+      var child = children[i];
+
+      if (child.isExpandable() && !child.expanded) {
+        child.expand();
+        expandedSomething = true;
+      }
+
+      expand(child);
+    }
+  }
+
+  function onAllNodesAvailable() {
+    ElementsTestRunner.firstElementsTreeOutline().runPendingUpdates();
+    expand(ElementsTestRunner.firstElementsTreeOutline().rootElement());
+    setTimeout(callback.bind(null, expandedSomething));
+  }
+
+  ElementsTestRunner.findNode(function() {
+    return false;
+  }, onAllNodesAvailable);
+};
+
+ElementsTestRunner.dumpDOMAgentTree = function(node) {
+  if (!TestRunner.domModel._document)
+    return;
+
+  function dump(node, prefix) {
+    TestRunner.addResult(prefix + node.nodeName());
+    prefix = prefix + '    ';
+
+    if (node.templateContent())
+      dump(node.templateContent(), prefix);
+
+    if (node.importedDocument())
+      dump(node.importedDocument(), prefix);
+
+    var shadowRoots = node.shadowRoots();
+
+    for (var i = 0; i < shadowRoots.length; ++i)
+      dump(shadowRoots[i], prefix);
+
+    var children = node.children();
+
+    for (var i = 0; children && i < children.length; ++i)
+      dump(children[i], prefix);
+  }
+
+  dump(node, '');
+};
+
+ElementsTestRunner.rangeText = function(range) {
+  if (!range)
+    return '[undefined-undefined]';
+
+  return '[' + range.startLine + ':' + range.startColumn + '-' + range.endLine + ':' + range.endColumn + ']';
+};
+
+ElementsTestRunner.generateUndoTest = function(testBody) {
+  function result(next) {
+    var testNode = ElementsTestRunner.expandedNodeWithId(/function\s([^(]*)/.exec(testBody)[1]);
+    TestRunner.addResult('Initial:');
+    ElementsTestRunner.dumpElementsTree(testNode);
+    testBody(undo);
+
+    function undo() {
+      TestRunner.addResult('Post-action:');
+      ElementsTestRunner.dumpElementsTree(testNode);
+      ElementsTestRunner.expandElementsTree(expandedCallback);
+
+      function expandedCallback(expandedSomething) {
+        if (expandedSomething) {
+          TestRunner.addResult('== Expanded: ==');
+          ElementsTestRunner.dumpElementsTree(testNode);
+        }
+
+        TestRunner.domModel.undo().then(redo);
+      }
+    }
+
+    function redo() {
+      TestRunner.addResult('Post-undo (initial):');
+      ElementsTestRunner.dumpElementsTree(testNode);
+      ElementsTestRunner.expandElementsTree(expandedCallback);
+
+      function expandedCallback(expandedSomething) {
+        if (expandedSomething) {
+          TestRunner.addResult('== Expanded: ==');
+          ElementsTestRunner.dumpElementsTree(testNode);
+        }
+
+        TestRunner.domModel.redo().then(done);
+      }
+    }
+
+    function done() {
+      TestRunner.addResult('Post-redo (action):');
+      ElementsTestRunner.dumpElementsTree(testNode);
+      ElementsTestRunner.expandElementsTree(expandedCallback);
+
+      function expandedCallback(expandedSomething) {
+        if (expandedSomething) {
+          TestRunner.addResult('== Expanded: ==');
+          ElementsTestRunner.dumpElementsTree(testNode);
+        }
+
+        next();
+      }
+    }
+  }
+
+  result.toString = function() {
+    return testBody.toString();
+  };
+
+  return result;
+};
+
+const indent = '    ';
+
+ElementsTestRunner.dumpRulesArray = function(rules, currentIndent) {
+  if (!rules)
+    return;
+
+  currentIndent = currentIndent || '';
+
+  for (var i = 0; i < rules.length; ++i)
+    ElementsTestRunner.dumpRule(rules[i], currentIndent);
+};
+
+ElementsTestRunner.dumpRuleMatchesArray = function(matches, currentIndent) {
+  if (!matches)
+    return;
+
+  currentIndent = currentIndent || '';
+
+  for (var i = 0; i < matches.length; ++i)
+    ElementsTestRunner.dumpRule(matches[i].rule, currentIndent);
+};
+
+ElementsTestRunner.dumpRule = function(rule, currentIndent) {
+  function selectorRange() {
+    var selectors = rule.selectorList.selectors;
+
+    if (!selectors || !selectors[0].range)
+      return '';
+
+    var ranges = [];
+
+    for (var i = 0; i < selectors.length; ++i) {
+      var range = selectors[i].range;
+      ranges.push(range.startLine + ':' + range.startColumn + '-' + range.endLine + ':' + range.endColumn);
+    }
+
+    return ', ' + ranges.join('; ');
+  }
+
+  currentIndent = currentIndent || '';
+
+  if (!rule.type || rule.type === 'style') {
+    TestRunner.addResult(currentIndent + rule.selectorList.text + ': [' + rule.origin + selectorRange() + '] {');
+    ElementsTestRunner.dumpStyle(rule.style, currentIndent + indent);
+    TestRunner.addResult(currentIndent + '}');
+    return;
+  }
+
+  if (rule.type === 'media') {
+    TestRunner.addResult(currentIndent + '@media ' + rule.mediaText + ' {');
+    ElementsTestRunner.dumpRulesArray(rule.childRules, currentIndent + indent);
+    TestRunner.addResult(currentIndent + '}');
+    return;
+  }
+
+  if (rule.type === 'import') {
+    TestRunner.addResult(
+        currentIndent + '@import: header=' + ElementsTestRunner.rangeText(rule.headerRange) +
+        ', body=' + ElementsTestRunner.rangeText(rule.bodyRange));
+
+    return;
+  }
+
+  if (rule.type === 'page' || rule.type === 'font-face') {
+    if (rule.type === 'page') {
+      TestRunner.addResult(currentIndent + rule.selectorList.text + ' {');
+    } else {
+      TestRunner.addResult(
+          currentIndent + '@' + rule.type + ' ' + ((rule.selectorList.text ? rule.selectorList.text + ' ' : '')) + '{');
+    }
+
+    ElementsTestRunner.dumpStyle(rule.style, currentIndent + indent);
+    TestRunner.addResult(currentIndent + '}');
+    return;
+  }
+
+  if (rule.type === 'charset') {
+    TestRunner.addResult('@charset');
+    return;
+  }
+
+  TestRunner.addResult(
+      currentIndent + '[UNKNOWN RULE]: header=' + ElementsTestRunner.rangeText(rule.headerRange) +
+      ', body=' + ElementsTestRunner.rangeText(rule.bodyRange));
+};
+
+ElementsTestRunner.dumpStyle = function(style, currentIndent) {
+  currentIndent = currentIndent || '';
+
+  if (!style) {
+    TestRunner.addResult(currentIndent + '[NO STYLE]');
+    return;
+  }
+
+  for (var i = 0; i < style.cssProperties.length; ++i) {
+    var property = style.cssProperties[i];
+
+    if (!property.disabled) {
+      TestRunner.addResult(
+          currentIndent + '[\'' + property.name + '\':\'' + property.value + '\'' +
+          ((property.important ? ' is-important' : '')) + (('parsedOk' in property ? ' non-parsed' : '')) + '] @' +
+          ElementsTestRunner.rangeText(property.range) + ' ');
+    } else {
+      TestRunner.addResult(currentIndent + '[text=\'' + property.text + '\'] disabled');
+    }
+  }
+};
+
+ElementsTestRunner.dumpCSSStyleDeclaration = function(style, currentIndent) {
+  currentIndent = currentIndent || '';
+
+  if (!style) {
+    TestRunner.addResult(currentIndent + '[NO STYLE]');
+    return;
+  }
+
+  var properties = style.allProperties();
+
+  for (var i = 0; i < properties.length; ++i) {
+    var property = properties[i];
+
+    if (!property.disabled) {
+      TestRunner.addResult(
+          currentIndent + '[\'' + property.name + '\':\'' + property.value + '\'' +
+          ((property.important ? ' is-important' : '')) + ((!property['parsedOk'] ? ' non-parsed' : '')) + '] @' +
+          ElementsTestRunner.rangeText(property.range) + ' ');
+    } else {
+      TestRunner.addResult(currentIndent + '[text=\'' + property.text + '\'] disabled');
+    }
+  }
+};
+
+ElementsTestRunner.dumpBreadcrumb = function(message) {
+  if (message)
+    TestRunner.addResult(message + ':');
+
+  var result = [];
+  var crumbs = UI.panels.elements._breadcrumbs.crumbsElement;
+  var crumb = crumbs.lastChild;
+
+  while (crumb) {
+    result.unshift(crumb.textContent);
+    crumb = crumb.previousSibling;
+  }
+
+  TestRunner.addResult(result.join(' > '));
+};
+
+ElementsTestRunner.matchingSelectors = function(matchedStyles, rule) {
+  var selectors = [];
+  var matchingSelectors = matchedStyles.matchingSelectors(rule);
+
+  for (var i = 0; i < matchingSelectors.length; ++i)
+    selectors.push(rule.selectors[matchingSelectors[i]].text);
+
+  return '[' + selectors.join(', ') + ']';
+};
+
+ElementsTestRunner.addNewRuleInStyleSheet = function(styleSheetHeader, selector, callback) {
+  TestRunner.addSniffer(
+      Elements.StylesSidebarPane.prototype, '_addBlankSection', onBlankSection.bind(null, selector, callback));
+  UI.panels.elements._stylesWidget._createNewRuleInStyleSheet(styleSheetHeader);
+};
+
+ElementsTestRunner.addNewRule = function(selector, callback) {
+  UI.panels.elements._stylesWidget.contentElement.querySelector('.styles-pane-toolbar')
+      .shadowRoot.querySelector('.largeicon-add')
+      .click();
+  TestRunner.addSniffer(
+      Elements.StylesSidebarPane.prototype, '_addBlankSection', onBlankSection.bind(null, selector, callback));
+};
+
+function onBlankSection(selector, callback) {
+  var section = ElementsTestRunner.firstMatchedStyleSection();
+
+  if (typeof selector === 'string')
+    section._selectorElement.textContent = selector;
+
+  section._selectorElement.dispatchEvent(TestRunner.createKeyEvent('Enter'));
+  ElementsTestRunner.waitForSelectorCommitted(callback.bind(null, section));
+}
+
+ElementsTestRunner.dumpInspectorHighlightJSON = function(idValue, callback) {
+  ElementsTestRunner.nodeWithId(idValue, nodeResolved);
+
+  async function nodeResolved(node) {
+    var result = await TestRunner.OverlayAgent.getHighlightObjectForTest(node.id);
+    TestRunner.addResult(idValue + JSON.stringify(result, null, 2));
+    callback();
+  }
+};
+
+ElementsTestRunner.waitForAnimationAdded = function(callback) {
+  TestRunner.addSniffer(Animation.AnimationTimeline.prototype, '_addAnimationGroup', callback);
+};
+
+ElementsTestRunner.dumpAnimationTimeline = function(timeline) {
+  for (var ui of timeline._uiAnimations) {
+    TestRunner.addResult(ui.animation().type());
+    TestRunner.addResult(ui._nameElement.innerHTML);
+    TestRunner.addResult(ui._svg.innerHTML);
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/SetOuterHTMLTestRunner.js b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/SetOuterHTMLTestRunner.js
new file mode 100644
index 0000000..abdc7e7
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/SetOuterHTMLTestRunner.js
@@ -0,0 +1,109 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ElementsTestRunner.events = [];
+ElementsTestRunner.containerId;
+
+ElementsTestRunner.setUpTestSuite = function(next) {
+  ElementsTestRunner.expandElementsTree(step1);
+
+  function step1() {
+    ElementsTestRunner.selectNodeWithId('container', step2);
+  }
+
+  function step2(node) {
+    ElementsTestRunner.containerId = node.id;
+    TestRunner.DOMAgent.getOuterHTML(ElementsTestRunner.containerId).then(step3);
+  }
+
+  function step3(text) {
+    ElementsTestRunner.containerText = text;
+
+    for (var key in SDK.DOMModel.Events) {
+      var eventName = SDK.DOMModel.Events[key];
+
+      if (eventName === SDK.DOMModel.Events.MarkersChanged || eventName === SDK.DOMModel.Events.DOMMutated)
+        continue;
+
+      TestRunner.domModel.addEventListener(
+          eventName, ElementsTestRunner.recordEvent.bind(ElementsTestRunner, eventName));
+    }
+
+    next();
+  }
+};
+
+ElementsTestRunner.recordEvent = function(eventName, event) {
+  if (!event.data)
+    return;
+
+  var node = event.data.node || event.data;
+  var parent = event.data.parent;
+
+  for (var currentNode = parent || node; currentNode; currentNode = currentNode.parentNode) {
+    if (currentNode.getAttribute('id') === 'output')
+      return;
+  }
+
+  ElementsTestRunner.events.push('Event ' + eventName.toString() + ': ' + node.nodeName());
+};
+
+ElementsTestRunner.patchOuterHTML = function(pattern, replacement, next) {
+  TestRunner.addResult('Replacing \'' + pattern + '\' with \'' + replacement + '\'\n');
+  ElementsTestRunner.setOuterHTML(ElementsTestRunner.containerText.replace(pattern, replacement), next);
+};
+
+ElementsTestRunner.patchOuterHTMLUseUndo = function(pattern, replacement, next) {
+  TestRunner.addResult('Replacing \'' + pattern + '\' with \'' + replacement + '\'\n');
+  ElementsTestRunner.setOuterHTMLUseUndo(ElementsTestRunner.containerText.replace(pattern, replacement), next);
+};
+
+ElementsTestRunner.setOuterHTML = function(newText, next) {
+  ElementsTestRunner.innerSetOuterHTML(newText, false, bringBack);
+
+  function bringBack() {
+    TestRunner.addResult('\nBringing things back\n');
+    ElementsTestRunner.innerSetOuterHTML(ElementsTestRunner.containerText, true, next);
+  }
+};
+
+ElementsTestRunner.setOuterHTMLUseUndo = function(newText, next) {
+  ElementsTestRunner.innerSetOuterHTML(newText, false, bringBack);
+
+  async function bringBack() {
+    TestRunner.addResult('\nBringing things back\n');
+    await TestRunner.domModel.undo();
+    ElementsTestRunner._dumpOuterHTML(true, next);
+  }
+};
+
+ElementsTestRunner.innerSetOuterHTML = async function(newText, last, next) {
+  await TestRunner.DOMAgent.setOuterHTML(ElementsTestRunner.containerId, newText);
+  ElementsTestRunner._dumpOuterHTML(last, next);
+};
+
+ElementsTestRunner._dumpOuterHTML = async function(last, next) {
+  var result = await TestRunner.RuntimeAgent.evaluate('document.getElementById("identity").wrapperIdentity');
+  TestRunner.addResult('Wrapper identity: ' + result.value);
+  ElementsTestRunner.events.sort();
+
+  for (var i = 0; i < ElementsTestRunner.events.length; ++i)
+    TestRunner.addResult(ElementsTestRunner.events[i]);
+
+  ElementsTestRunner.events = [];
+  var text = await TestRunner.DOMAgent.getOuterHTML(ElementsTestRunner.containerId);
+  TestRunner.addResult('==========8<==========');
+  TestRunner.addResult(text);
+  TestRunner.addResult('==========>8==========');
+
+  if (last)
+    TestRunner.addResult('\n\n\n');
+
+  next();
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/StylesUpdateLinksTestRunner.js b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/StylesUpdateLinksTestRunner.js
new file mode 100644
index 0000000..a48b193
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/StylesUpdateLinksTestRunner.js
@@ -0,0 +1,129 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+function flattenRuleRanges(rule) {
+  var ranges = [];
+  var medias = rule.media || [];
+
+  for (var i = 0; i < medias.length; ++i) {
+    var media = medias[i];
+
+    if (!media.range)
+      continue;
+
+    ranges.push({range: media.range, name: 'media #' + i});
+  }
+
+  for (var i = 0; i < rule.selectors.length; ++i) {
+    var selector = rule.selectors[i];
+
+    if (!selector.range)
+      continue;
+
+    ranges.push({range: selector.range, name: 'selector #' + i});
+  }
+
+  if (rule.style.range)
+    ranges.push({range: rule.style.range, name: 'style range'});
+
+
+  var properties = rule.style.allProperties();
+
+  for (var i = 0; i < properties.length; ++i) {
+    var property = properties[i];
+
+    if (!property.range)
+      continue;
+
+    ranges.push({range: property.range, name: 'property >>' + property.text + '<<'});
+  }
+
+  return ranges;
+}
+
+function compareRuleRanges(lazyRule, originalRule) {
+  if (lazyRule.selectorText !== originalRule.selectorText) {
+    TestRunner.addResult(
+        'Error: rule selectors are not equal: ' + lazyRule.selectorText + ' != ' + originalRule.selectorText);
+    return false;
+  }
+
+  var flattenLazy = flattenRuleRanges(lazyRule);
+  var flattenOriginal = flattenRuleRanges(originalRule);
+
+  if (flattenLazy.length !== flattenOriginal.length) {
+    TestRunner.addResult(
+        'Error: rule range amount is not equal: ' + flattenLazy.length + ' != ' + flattenOriginal.length);
+    return false;
+  }
+
+  for (var i = 0; i < flattenLazy.length; ++i) {
+    var lazyRange = flattenLazy[i];
+    var originalRange = flattenOriginal[i];
+
+    if (lazyRange.name !== originalRange.name) {
+      TestRunner.addResult('Error: rule names are not equal: ' + lazyRange.name + ' != ' + originalRange.name);
+      return false;
+    }
+
+    if (!lazyRange.range.equal(originalRange.range)) {
+      TestRunner.addResult(
+          'Error: ranges \'' + lazyRange.name + '\' are not equal: ' + lazyRange.range.toString() +
+          ' != ' + originalRange.range.toString());
+
+      return false;
+    }
+  }
+
+  TestRunner.addResult(flattenLazy.length + ' rule ranges are equal.');
+  return true;
+}
+
+ElementsTestRunner.validateRuleRanges = function(selector, rules, callback) {
+  ElementsTestRunner.selectNodeAndWaitForStyles('other', onOtherSelected);
+
+  function onOtherSelected() {
+    ElementsTestRunner.selectNodeAndWaitForStyles(selector, onContainerSelected);
+  }
+
+  function onContainerSelected() {
+    var fetchedRules = ElementsTestRunner.getMatchedRules();
+
+    if (fetchedRules.length !== rules.length) {
+      TestRunner.addResult(String.sprintf(
+          'Error: rules sizes are not equal! Expected: %d, actual: %d', fetchedRules.length, rules.length));
+      TestRunner.completeTest();
+      return;
+    }
+
+    for (var i = 0; i < fetchedRules.length; ++i) {
+      if (!compareRuleRanges(rules[i], fetchedRules[i])) {
+        TestRunner.completeTest();
+        return;
+      }
+    }
+
+    callback();
+  }
+};
+
+ElementsTestRunner.getMatchedRules = function() {
+  var rules = [];
+
+  for (var block of UI.panels.elements._stylesWidget._sectionBlocks) {
+    for (var section of block.sections) {
+      var rule = section.style().parentRule;
+
+      if (rule)
+        rules.push(rule);
+    }
+  }
+
+  return rules;
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/module.json
index f39e9e9b..6111fce0 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements_test_runner/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/elements_test_runner/module.json
@@ -3,9 +3,21 @@
     "test_runner",
     "integration_test_runner",
     "elements",
-    "event_listeners"
+    "event_listeners",
+    "animation"
   ],
   "scripts": [
-    "ElementsTestRunner.js"
+    "ElementsTestRunner.js",
+    "EditDOMTestRunner.js",
+    "SetOuterHTMLTestRunner.js",
+    "ElementsPanelShadowSelectionOnRefreshTestRunner.js",
+    "StylesUpdateLinksTestRunner.js"
+  ],
+  "skip_compilation": [
+    "ElementsTestRunner.js",
+    "EditDOMTestRunner.js",
+    "SetOuterHTMLTestRunner.js",
+    "ElementsPanelShadowSelectionOnRefreshTestRunner.js",
+    "StylesUpdateLinksTestRunner.js"
   ]
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/ExtensionsNetworkTestRunner.js b/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/ExtensionsNetworkTestRunner.js
new file mode 100644
index 0000000..497078f
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/ExtensionsNetworkTestRunner.js
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+function extension_getRequestByUrl(urls, callback) {
+  function onHAR(response) {
+    var entries = response.entries;
+
+    for (var i = 0; i < entries.length; ++i) {
+      for (var url = 0; url < urls.length; ++url) {
+        if (urls[url].test(entries[i].request.url)) {
+          callback(entries[i]);
+          return;
+        }
+      }
+    }
+
+    output('no item found');
+    callback(null);
+  }
+
+  webInspector.network.getHAR(onHAR);
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/ExtensionsTestRunner.js b/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/ExtensionsTestRunner.js
new file mode 100644
index 0000000..21cb4d88
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/ExtensionsTestRunner.js
@@ -0,0 +1,75 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+var extensionsHost = 'devtools-extensions.oopif.test';
+var extensionsOrigin = `http://${extensionsHost}:8000`;
+Extensions.extensionServer._registerHandler('evaluateForTestInFrontEnd', onEvaluate);
+
+Extensions.extensionServer._extensionAPITestHook = function(extensionServerClient, coreAPI) {
+  window.webInspector = coreAPI;
+  window._extensionServerForTests = extensionServerClient;
+  coreAPI.panels.themeName = 'themeNameForTest';
+};
+
+ExtensionsTestRunner._replyToExtension = function(requestId, port) {
+  Extensions.extensionServer._dispatchCallback(requestId, port);
+};
+
+function onEvaluate(message, port) {
+  try {
+    eval(message.expression);
+  } catch (e) {
+    TestRunner.addResult('Exception while running: ' + message.expression + '\n' + (e.stack || e));
+    TestRunner.completeTest();
+  }
+}
+
+ExtensionsTestRunner.runExtensionTests = async function() {
+  var result = await TestRunner.RuntimeAgent.evaluate('location.href', 'console', false);
+
+  if (!result)
+    return;
+
+  var pageURL = result.value;
+  var extensionURL = ((/^https?:/.test(pageURL) ? pageURL.replace(/^(https?:\/\/[^\/]*\/).*$/, '$1') :
+                                                  pageURL.replace(/\/inspector\/extensions\/[^\/]*$/, '/http/tests'))) +
+      'inspector/resources/extension-main.html';
+  extensionURL = extensionURL.replace('127.0.0.1', extensionsHost);
+
+  InspectorFrontendAPI.addExtensions(
+      [{startPage: extensionURL, name: 'test extension', exposeWebInspectorNamespace: true}]);
+
+  Extensions.extensionServer.initializeExtensions();
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    function extensionFunctions() {
+      var functions = '';
+
+      for (symbol in window) {
+        if (/^extension_/.exec(symbol) && typeof window[symbol] === 'function')
+          functions += window[symbol].toString();
+      }
+
+      return functions;
+    }
+
+    var extensionsOrigin = 'http://devtools-extensions.oopif.test:8000';
+
+    function extension_showPanel(panelId, callback) {
+      evaluateOnFrontend('InspectorTest.showPanel(unescape(\'' + escape(panelId) + '\')).then(function() { reply(); });', callback);
+    }
+
+    var test = function() {
+      Common.moduleSetting('shortcutPanelSwitch').set(true);
+      InspectorTest.runExtensionTests();
+    };
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/module.json
new file mode 100644
index 0000000..82e956c
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/extensions_test_runner/module.json
@@ -0,0 +1,15 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "extensions"
+  ],
+  "scripts": [
+    "ExtensionsNetworkTestRunner.js",
+    "ExtensionsTestRunner.js"
+  ],
+  "skip_compilation": [
+    "ExtensionsNetworkTestRunner.js",
+    "ExtensionsTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/integration_test_runner.json b/third_party/WebKit/Source/devtools/front_end/integration_test_runner.json
index c327aab4..0242b890 100644
--- a/third_party/WebKit/Source/devtools/front_end/integration_test_runner.json
+++ b/third_party/WebKit/Source/devtools/front_end/integration_test_runner.json
@@ -1,5 +1,18 @@
 {
   "modules" : [
+    { "name": "application_test_runner" },
+    { "name": "audits_test_runner" },
+    { "name": "bindings_test_runner" },
+    { "name": "extensions_test_runner" },
+    { "name": "layers_test_runner" },
+    { "name": "network_test_runner" },
+    { "name": "security_test_runner" },
+    { "name": "performance_test_runner" },
+    { "name": "coverage_test_runner" },
+    { "name": "device_mode_test_runner" },
+    { "name": "accessibility_test_runner" },
+    { "name": "profiler_test_runner" },
+    { "name": "sass_test_runner" },
     { "name": "test_runner", "type": "autostart" },
     { "name": "integration_test_runner", "type": "autostart" },
     { "name": "console_test_runner" },
@@ -8,6 +21,11 @@
     { "name": "data_grid_test_runner" },
 
 
+
+    { "name": "heap_snapshot_worker" },
+
+
+    
     { "name": "platform", "type": "autostart" },
     { "name": "dom_extension", "type": "autostart" },
     { "name": "main", "type": "autostart" },
diff --git a/third_party/WebKit/Source/devtools/front_end/integration_test_runner/IntegrationTestRunner.js b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/IntegrationTestRunner.js
index 8c7901b..5da2f43 100644
--- a/third_party/WebKit/Source/devtools/front_end/integration_test_runner/IntegrationTestRunner.js
+++ b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/IntegrationTestRunner.js
@@ -21,6 +21,7 @@
   TestRunner.HeapProfilerAgent = target.heapProfilerAgent();
   TestRunner.InspectorAgent = target.inspectorAgent();
   TestRunner.NetworkAgent = target.networkAgent();
+  TestRunner.OverlayAgent = target.overlayAgent();
   TestRunner.PageAgent = target.pageAgent();
   TestRunner.ProfilerAgent = target.profilerAgent();
   TestRunner.RuntimeAgent = target.runtimeAgent();
@@ -35,6 +36,7 @@
   TestRunner.domDebuggerModel = target.model(SDK.DOMDebuggerModel);
   TestRunner.cssModel = target.model(SDK.CSSModel);
   TestRunner.cpuProfilerModel = target.model(SDK.CPUProfilerModel);
+  TestRunner.overlayModel = target.model(SDK.OverlayModel);
   TestRunner.serviceWorkerManager = target.model(SDK.ServiceWorkerManager);
   TestRunner.tracingManager = target.model(SDK.TracingManager);
   TestRunner.mainTarget = target;
@@ -732,6 +734,41 @@
   return 'frame';
 };
 
+/**
+ * @param {string} urlSuffix
+ * @param {!Workspace.projectTypes=} projectType
+ * @return {!Promise}
+ */
+TestRunner.waitForUISourceCode = function(urlSuffix, projectType) {
+  /**
+   * @param {!Workspace.UISourceCode} uiSourceCode
+   * @return {boolean}
+   */
+  function matches(uiSourceCode) {
+    if (projectType && uiSourceCode.project().type() !== projectType)
+      return false;
+    if (!projectType && uiSourceCode.project().type() === Workspace.projectTypes.Service)
+      return false;
+    if (urlSuffix && !uiSourceCode.url().endsWith(urlSuffix))
+      return false;
+    return true;
+  }
+
+  for (var uiSourceCode of Workspace.workspace.uiSourceCodes()) {
+    if (urlSuffix && matches(uiSourceCode))
+      return Promise.resolve(uiSourceCode);
+  }
+
+  return TestRunner.waitForEvent(Workspace.Workspace.Events.UISourceCodeAdded, Workspace.workspace, matches);
+};
+
+/**
+ * @param {!Function} callback
+ */
+TestRunner.waitForUISourceCodeRemoved = function(callback) {
+  Workspace.workspace.once(Workspace.Workspace.Events.UISourceCodeRemoved).then(callback);
+};
+
 /** @type {boolean} */
 IntegrationTestRunner._startedTest = false;
 
diff --git a/third_party/WebKit/Source/devtools/front_end/integration_test_runner/PageMockTestRunner.js b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/PageMockTestRunner.js
new file mode 100644
index 0000000..fe125dfb
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/PageMockTestRunner.js
@@ -0,0 +1,244 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+var id = 0;
+
+function nextId(prefix) {
+  return (prefix || '') + ++id;
+}
+
+TestRunner.connectToPage = function(targetName, pageMock, makeMainTarget) {
+  var mockTarget = SDK.targetManager.createTarget(
+      nextId('mock-target-'), targetName, pageMock.capabilities(), params => pageMock.createConnection(params));
+
+  if (makeMainTarget) {
+    SDK.targetManager._targets = SDK.targetManager._targets.filter(target => target !== mockTarget);
+    SDK.targetManager._targets.unshift(mockTarget);
+  }
+
+  return mockTarget;
+};
+
+TestRunner.PageMock = class {
+  constructor(url) {
+    this._url = url;
+    this._capabilities = SDK.Target.Capability.DOM | SDK.Target.Capability.JS | SDK.Target.Capability.Browser;
+    this._enabledDomains = new Set();
+
+    this._mainFrame =
+        {id: nextId(), loaderId: nextId(), mimeType: 'text/html', securityOrigin: this._url, url: this._url};
+
+    this._executionContexts = [];
+    this._executionContexts.push(this._createExecutionContext(this._mainFrame, false));
+    this._scripts = [];
+    this._scriptContents = new Map();
+
+    this._dispatchMap = {
+      'Debugger.enable': this._debuggerEnable,
+      'Debugger.getScriptSource': this._debuggerGetScriptSource,
+      'Debugger.setBlackboxPatterns': (id, params) => this._sendResponse(id, {}),
+      'Runtime.enable': this._runtimeEnable,
+      'Page.enable': this._pageEnable,
+      'Page.getResourceTree': this._pageGetResourceTree
+    };
+  }
+
+  capabilities() {
+    return this._capabilities;
+  }
+
+  disableDOMCapability() {
+    this._capabilities = this._capabilities & ~SDK.Target.Capability.DOM;
+  }
+
+  createConnection(params) {
+    this._enabledDomains.clear();
+    this._connection = new MockPageConnection(this, params);
+    return this._connection;
+  }
+
+  evalScript(url, content, isContentScript) {
+    var id = nextId();
+    content += '\n//# sourceURL=' + url;
+    this._scriptContents.set(id, content);
+    var context = this._executionContexts.find(context => context.auxData.isDefault !== isContentScript);
+
+    if (!context) {
+      context = this._createExecutionContext(this._mainFrame, isContentScript);
+
+      this._fireEvent('Runtime.executionContextCreated', {context: context});
+    }
+
+    var text = new TextUtils.Text(content);
+
+    var script = {
+      scriptId: id,
+      url: url,
+      startLine: 0,
+      startColumn: 0,
+      endLine: text.lineCount(),
+      endColumn: text.lineAt(text.lineCount()).length - 1,
+      executionContextId: context.id,
+      hash: String.hashCode(content),
+      executionContextAuxData: context.auxData,
+      sourceMapURL: '',
+      hasSourceURL: true,
+      isLiveEdit: false,
+      isModule: false,
+      length: content.length
+    };
+
+    this._scripts.push(script);
+    this._fireEvent('Debugger.scriptParsed', script);
+  }
+
+  reload() {
+    this._fireEvent('Page.frameStartedLoading', {frameId: this._mainFrame.id});
+
+    for (var context of this._executionContexts)
+      this._fireEvent('Runtime.executionContextDestroyed', {executionContextId: context.id});
+
+
+    this._scripts = [];
+    this._scriptContents.clear();
+    this._executionContexts = [];
+    this._fireEvent('Runtime.executionContextsCleared', {});
+    this._executionContexts.push(this._createExecutionContext(this._mainFrame, false));
+
+    for (var context of this._executionContexts)
+      this._fireEvent('Runtime.executionContextCreated', {context: context});
+
+
+    this._fireEvent('Page.frameNavigated', {frame: this._mainFrame});
+
+    this._fireEvent('Page.loadEventFired', {timestamp: Date.now() / 1000});
+
+    this._fireEvent('Page.frameStoppedLoading', {frameId: this._mainFrame.id});
+
+    this._fireEvent('Page.domContentEventFired', {timestamp: Date.now() / 1000});
+  }
+
+  close() {
+    if (this._connection) {
+      this._connection.disconnect();
+      this._connection = null;
+    }
+  }
+
+  _createExecutionContext(frame, isContentScript) {
+    return {
+      id: nextId(),
+
+      auxData: {isDefault: !isContentScript, frameId: frame.id},
+
+      origin: frame.securityOrigin,
+      name: ''
+    };
+  }
+
+  _debuggerEnable(id, params) {
+    this._enabledDomains.add('Debugger');
+    this._sendResponse(id, {});
+
+    for (var script of this._scripts)
+      this._fireEvent('Debugger.scriptParsed', script);
+  }
+
+  _debuggerGetScriptSource(id, params) {
+    if (!this._scriptContents.has(params.scriptId)) {
+      this._sendResponse(id, undefined, {message: 'Can\'t get script content for id ' + params.scriptId, code: 1});
+
+      return;
+    }
+
+    var result = {scriptSource: this._scriptContents.get(params.scriptId)};
+
+    this._sendResponse(id, result);
+  }
+
+  _runtimeEnable(id, params) {
+    this._enabledDomains.add('Runtime');
+    this._sendResponse(id, {});
+
+    for (var context of this._executionContexts)
+      this._fireEvent('Runtime.executionContextCreated', {context: context});
+  }
+
+  _pageEnable(id, params) {
+    this._enabledDomains.add('Page');
+    this._sendResponse(id, {});
+  }
+
+  _pageGetResourceTree(id, params) {
+    var result = {frameTree: {frame: this._mainFrame, resources: []}};
+
+    this._sendResponse(id, result);
+  }
+
+  _isSupportedDomain(methodName) {
+    var domain = methodName.split('.')[0];
+
+    if (domain === 'Page')
+      return !!(this._capabilities & SDK.Target.Capability.DOM);
+
+    return true;
+  }
+
+  _dispatch(id, methodName, params, message) {
+    var handler = (this._isSupportedDomain(methodName) ? this._dispatchMap[methodName] : null);
+
+    if (handler)
+      return handler.call(this, id, params);
+
+    this._sendResponse(
+        id, undefined,
+        {message: 'Can\'t handle command ' + methodName, code: Protocol.InspectorBackend.DevToolsStubErrorCode});
+  }
+
+  _sendResponse(id, result, error) {
+    var message = {id: id, result: result, error: error};
+
+    this._connection.sendMessageToDevTools(message);
+  }
+
+  _fireEvent(methodName, params) {
+    var domain = methodName.split('.')[0];
+
+    if (!this._enabledDomains.has(domain))
+      return;
+
+    var message = {method: methodName, params: params};
+
+    this._connection.sendMessageToDevTools(message);
+  }
+};
+
+var MockPageConnection = class {
+  constructor(page, params) {
+    this._page = page;
+    this._onMessage = params.onMessage;
+    this._onDisconnect = params.onDisconnect;
+  }
+
+  sendMessageToDevTools(message) {
+    setTimeout(() => this._onMessage.call(null, JSON.stringify(message)), 0);
+  }
+
+  sendMessage(message) {
+    var json = JSON.parse(message);
+    this._page._dispatch(json.id, json.method, json.params, message);
+  }
+
+  disconnect() {
+    this._onDisconnect.call(null, 'force disconnect');
+    this._onDisconnect = null;
+    this._onMessage = null;
+    return Promise.resolve();
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/integration_test_runner/SyntaxHighlightTestRunner.js b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/SyntaxHighlightTestRunner.js
new file mode 100644
index 0000000..4f44a11
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/SyntaxHighlightTestRunner.js
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+TestRunner.dumpSyntaxHighlight = function(str, mimeType) {
+  var node = document.createElement('span');
+  node.textContent = str;
+  var javascriptSyntaxHighlighter = new UI.SyntaxHighlighter(mimeType);
+  return javascriptSyntaxHighlighter.syntaxHighlightNode(node).then(dumpSyntax);
+
+  function dumpSyntax() {
+    var node_parts = [];
+
+    for (var i = 0; i < node.childNodes.length; i++) {
+      if (node.childNodes[i].getAttribute)
+        node_parts.push(node.childNodes[i].getAttribute('class'));
+      else
+        node_parts.push('*');
+    }
+
+    TestRunner.addResult(str + ': ' + node_parts.join(', '));
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/integration_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/module.json
index 511866b..6d8d374 100644
--- a/third_party/WebKit/Source/devtools/front_end/integration_test_runner/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/integration_test_runner/module.json
@@ -2,9 +2,16 @@
   "dependencies": [
     "test_runner",
     "sdk",
-    "network_log"
+    "network_log",
+    "workspace"
   ],
   "scripts": [
-    "IntegrationTestRunner.js"
+    "IntegrationTestRunner.js",
+    "PageMockTestRunner.js",
+    "SyntaxHighlightTestRunner.js"
+  ],
+  "skip_compilation": [
+    "PageMockTestRunner.js",
+    "SyntaxHighlightTestRunner.js"
   ]
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/layers_test_runner/LayersTestRunner.js b/third_party/WebKit/Source/devtools/front_end/layers_test_runner/LayersTestRunner.js
new file mode 100644
index 0000000..64dda2c
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/layers_test_runner/LayersTestRunner.js
@@ -0,0 +1,157 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+LayersTestRunner.layerTreeModel = function() {
+  if (!LayersTestRunner._layerTreeModel)
+    LayersTestRunner._layerTreeModel = TestRunner.mainTarget.model(Layers.LayerTreeModel);
+
+  return LayersTestRunner._layerTreeModel;
+};
+
+LayersTestRunner.labelForLayer = function(layer) {
+  var node = layer.nodeForSelfOrAncestor();
+  var label = (node ? Components.DOMPresentationUtils.fullQualifiedSelector(node, false) : '<invalid node id>');
+  var height = layer.height();
+  var width = layer.width();
+
+  if (height <= 200 && width <= 200)
+    label += ' ' + height + 'x' + width;
+
+  if (typeof layer.__extraData !== 'undefined')
+    label += ' (' + layer.__extraData + ')';
+
+  return label;
+};
+
+LayersTestRunner.dumpLayerTree = function(prefix, root) {
+  if (!prefix)
+    prefix = '';
+
+  if (!root) {
+    root = LayersTestRunner.layerTreeModel().layerTree().contentRoot();
+
+    if (!root) {
+      TestRunner.addResult('No layer root, perhaps not in the composited mode! ');
+      TestRunner.completeTest();
+      return;
+    }
+  }
+
+  TestRunner.addResult(prefix + LayersTestRunner.labelForLayer(root));
+  root.children().forEach(LayersTestRunner.dumpLayerTree.bind(LayersTestRunner, prefix + '    '));
+};
+
+LayersTestRunner.dumpLayers3DView = function(prefix, root) {
+  if (!prefix)
+    prefix = '';
+
+  if (!root)
+    root = UI.panels.layers._layers3DView._rotatingContainerElement;
+
+  if (root.__layer)
+    TestRunner.addResult(prefix + LayersTestRunner.labelForLayer(root.__layer));
+
+  for (var element = root.firstElementChild; element; element = element.nextSibling)
+    LayersTestRunner.dumpLayers3DView(prefix + '    ', element);
+};
+
+LayersTestRunner.evaluateAndRunWhenTreeChanges = function(expression, callback) {
+  function eventHandler() {
+    LayersTestRunner.layerTreeModel().removeEventListener(Layers.LayerTreeModel.Events.LayerTreeChanged, eventHandler);
+    callback();
+  }
+
+  TestRunner.evaluateInPage(expression, function() {
+    LayersTestRunner.layerTreeModel().addEventListener(Layers.LayerTreeModel.Events.LayerTreeChanged, eventHandler);
+  });
+};
+
+LayersTestRunner.findLayerByNodeIdAttribute = function(nodeIdAttribute) {
+  var result;
+
+  function testLayer(layer) {
+    var node = layer.node();
+
+    if (!node)
+      return false;
+
+    if (!node || node.getAttribute('id') !== nodeIdAttribute)
+      return false;
+
+    result = layer;
+    return true;
+  }
+
+  LayersTestRunner.layerTreeModel().layerTree().forEachLayer(testLayer);
+
+  if (!result)
+    TestRunner.addResult('ERROR: No layer for ' + nodeIdAttribute);
+
+  return result;
+};
+
+LayersTestRunner.requestLayers = function(callback) {
+  LayersTestRunner.layerTreeModel().addEventListener(Layers.LayerTreeModel.Events.LayerTreeChanged, onLayerTreeChanged);
+  LayersTestRunner.layerTreeModel().enable();
+
+  function onLayerTreeChanged() {
+    LayersTestRunner.layerTreeModel().removeEventListener(
+        Layers.LayerTreeModel.Events.LayerTreeChanged, onLayerTreeChanged);
+    callback();
+  }
+};
+
+LayersTestRunner.dumpModelScrollRects = function() {
+  function dumpScrollRectsForLayer(layer) {
+    if (layer._scrollRects.length > 0)
+      TestRunner.addObject(layer._scrollRects);
+  }
+
+  TestRunner.addResult('Model elements dump');
+  LayersTestRunner.layerTreeModel().layerTree().forEachLayer(dumpScrollRectsForLayer.bind(this));
+};
+
+LayersTestRunner.dumpModelStickyPositionConstraint = function() {
+  function dumpModelStickyPositionConstraintForLayer(layer) {
+    var stickyFormatters = {
+      '_nearestLayerShiftingContainingBlock': 'formatAsTypeNameOrNull',
+      '_nearestLayerShiftingStickyBox': 'formatAsTypeNameOrNull'
+    };
+
+    if (layer._stickyPositionConstraint)
+      TestRunner.addObject(layer._stickyPositionConstraint, stickyFormatters);
+  }
+
+  TestRunner.addResult('Model elements dump');
+  LayersTestRunner.layerTreeModel().layerTree().forEachLayer(dumpModelStickyPositionConstraintForLayer.bind(this));
+};
+
+LayersTestRunner.dispatchMouseEvent = function(eventType, button, element, offsetX, offsetY) {
+  var totalOffset = element.totalOffset();
+
+  var eventArguments = {
+    bubbles: true,
+    cancelable: true,
+    view: window,
+    screenX: totalOffset.left - element.scrollLeft + offsetX,
+    screenY: totalOffset.top - element.scrollTop + offsetY,
+    clientX: totalOffset.left + offsetX,
+    clientY: totalOffset.top + offsetY,
+    button: button
+  };
+
+  if (eventType === 'mouseout') {
+    eventArguments.screenX = 0;
+    eventArguments.screenY = 0;
+    eventArguments.clientX = 0;
+    eventArguments.clientY = 0;
+  }
+
+  element.dispatchEvent(new MouseEvent(eventType, eventArguments));
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/layers_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/layers_test_runner/module.json
new file mode 100644
index 0000000..a76d127
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/layers_test_runner/module.json
@@ -0,0 +1,14 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "layers",
+    "components"
+  ],
+  "scripts": [
+    "LayersTestRunner.js"
+  ],
+  "skip_compilation": [
+    "LayersTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/network_test_runner/NetworkTestRunner.js b/third_party/WebKit/Source/devtools/front_end/network_test_runner/NetworkTestRunner.js
new file mode 100644
index 0000000..eaf0454
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/network_test_runner/NetworkTestRunner.js
@@ -0,0 +1,225 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+NetworkTestRunner.waitForRequestResponse = function(request) {
+  if (request.responseReceivedTime !== -1)
+    return Promise.resolve(request);
+
+  return TestRunner.waitForEvent(
+      SDK.NetworkManager.Events.RequestUpdated, TestRunner.networkManager,
+      updateRequest => updateRequest === request && request.responseReceivedTime !== -1);
+};
+
+NetworkTestRunner.waitForNetworkLogViewNodeForRequest = function(request) {
+  var networkLogView = UI.panels.network._networkLogView;
+  var node = networkLogView.nodeForRequest(request);
+
+  if (node)
+    return Promise.resolve(node);
+
+  console.assert(networkLogView._staleRequests.has(request));
+
+  return TestRunner.addSnifferPromise(networkLogView, '_didRefreshForTest').then(() => {
+    var node = networkLogView.nodeForRequest(request);
+    console.assert(node);
+    return node;
+  });
+};
+
+NetworkTestRunner.waitForWebsocketFrameReceived = function(wsRequest, message) {
+  for (var frame of wsRequest.frames()) {
+    if (checkFrame(frame))
+      return Promise.resolve(frame);
+  }
+
+  return TestRunner.waitForEvent(SDK.NetworkRequest.Events.WebsocketFrameAdded, wsRequest, checkFrame);
+
+  function checkFrame(frame) {
+    return frame.type === SDK.NetworkRequest.WebSocketFrameType.Receive && frame.text === message;
+  }
+};
+
+NetworkTestRunner.recordNetwork = function() {
+  UI.panels.network._networkLogView.setRecording(true);
+};
+
+NetworkTestRunner.networkRequests = function() {
+  return Array.from(NetworkLog.networkLog.requests());
+};
+
+NetworkTestRunner.dumpNetworkRequests = function() {
+  var requests = NetworkTestRunner.networkRequests();
+
+  requests.sort(function(a, b) {
+    return a.url().localeCompare(b.url());
+  });
+
+  TestRunner.addResult('resources count = ' + requests.length);
+
+  for (i = 0; i < requests.length; i++)
+    TestRunner.addResult(requests[i].url());
+};
+
+NetworkTestRunner.findRequestsByURLPattern = function(urlPattern) {
+  return NetworkTestRunner.networkRequests().filter(function(value) {
+    return urlPattern.test(value.url());
+  });
+};
+
+NetworkTestRunner.makeSimpleXHR = function(method, url, async, callback) {
+  NetworkTestRunner.makeXHR(method, url, async, undefined, undefined, [], false, undefined, undefined, callback);
+};
+
+NetworkTestRunner.makeSimpleXHRWithPayload = function(method, url, async, payload, callback) {
+  NetworkTestRunner.makeXHR(method, url, async, undefined, undefined, [], false, payload, undefined, callback);
+};
+
+NetworkTestRunner.makeXHR = function(
+    method, url, async, user, password, headers, withCredentials, payload, type, callback) {
+  var args = {};
+  args.method = method;
+  args.url = url;
+  args.async = async;
+  args.user = user;
+  args.password = password;
+  args.headers = headers;
+  args.withCredentials = withCredentials;
+  args.payload = payload;
+  args.type = type;
+  var jsonArgs = JSON.stringify(args).replace(/\"/g, '\\"');
+
+  function innerCallback(msg) {
+    if (msg.messageText.indexOf('XHR loaded') !== -1) {
+      if (callback)
+        callback();
+    } else {
+      ConsoleTestRunner.addConsoleSniffer(innerCallback);
+    }
+  }
+
+  ConsoleTestRunner.addConsoleSniffer(innerCallback);
+  TestRunner.evaluateInPage('makeXHRForJSONArguments("' + jsonArgs + '")');
+};
+
+NetworkTestRunner.makeFetch = function(url, requestInitializer, callback) {
+  TestRunner.callFunctionInPageAsync('makeFetch', [url, requestInitializer]).then(callback);
+};
+
+NetworkTestRunner.makeFetchInWorker = function(url, requestInitializer, callback) {
+  TestRunner.callFunctionInPageAsync('makeFetchInWorker', [url, requestInitializer]).then(callback);
+};
+
+NetworkTestRunner.clearNetworkCache = function() {
+  var networkAgent = TestRunner.NetworkAgent;
+  var promise = networkAgent.setCacheDisabled(true);
+  return promise.then(() => networkAgent.setCacheDisabled(false));
+};
+
+NetworkTestRunner.HARPropertyFormatters = {
+  bodySize: 'formatAsTypeName',
+  compression: 'formatAsTypeName',
+  connection: 'formatAsTypeName',
+  headers: 'formatAsTypeName',
+  headersSize: 'formatAsTypeName',
+  id: 'formatAsTypeName',
+  onContentLoad: 'formatAsTypeName',
+  onLoad: 'formatAsTypeName',
+  receive: 'formatAsTypeName',
+  startedDateTime: 'formatAsRecentTime',
+  time: 'formatAsTypeName',
+  timings: 'formatAsTypeName',
+  version: 'formatAsTypeName',
+  wait: 'formatAsTypeName',
+  _transferSize: 'formatAsTypeName',
+  _error: 'skip'
+};
+
+NetworkTestRunner.HARPropertyFormattersWithSize = JSON.parse(JSON.stringify(NetworkTestRunner.HARPropertyFormatters));
+NetworkTestRunner.HARPropertyFormattersWithSize.size = 'formatAsTypeName';
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    var lastXHRIndex = 0;
+
+    function xhrLoadedCallback() {
+      console.log('XHR loaded: ' + ++lastXHRIndex);
+    }
+
+    function makeSimpleXHR(method, url, async, callback) {
+      makeSimpleXHRWithPayload(method, url, async, null, callback);
+    }
+
+    function makeSimpleXHRWithPayload(method, url, async, payload, callback) {
+      makeXHR(method, url, async, undefined, undefined, [], false, payload, callback);
+    }
+
+    function makeXHR(method, url, async, user, password, headers, withCredentials, payload, type, callback) {
+      var xhr = new XMLHttpRequest();
+
+      if (type == undefined)
+        xhr.responseType = '';
+      else
+        xhr.responseType = type;
+
+      xhr.onreadystatechange = function() {
+        if (xhr.readyState === XMLHttpRequest.DONE) {
+          if (typeof callback === 'function')
+            callback();
+        }
+      };
+
+      xhr.open(method, url, async, user, password);
+      xhr.withCredentials = withCredentials;
+
+      for (var i = 0; i < headers.length; ++i)
+        xhr.setRequestHeader(headers[i][0], headers[i][1]);
+
+      xhr.send(payload);
+    }
+
+    function makeXHRForJSONArguments(jsonArgs) {
+      var args = JSON.parse(jsonArgs);
+
+      makeXHR(
+        args.method,
+        args.url,
+        args.async,
+        args.user,
+        args.password,
+        args.headers || [],
+        args.withCredentials,
+        args.payload,
+        args.type,
+        xhrLoadedCallback
+      );
+    }
+
+    function makeFetch(url, requestInitializer) {
+      return fetch(url, requestInitializer).then(res => {
+        res.text();
+        return res;
+      }).catch(e => e);
+    }
+
+    function makeFetchInWorker(url, requestInitializer) {
+      return new Promise(resolve => {
+        var worker = new Worker('/inspector/network/resources/fetch-worker.js');
+
+        worker.onmessage = event => {
+          resolve(event.data);
+        };
+
+        worker.postMessage({
+          url: url,
+          init: requestInitializer
+        });
+      });
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/network_test_runner/ProductRegistryTestRunner.js b/third_party/WebKit/Source/devtools/front_end/network_test_runner/ProductRegistryTestRunner.js
new file mode 100644
index 0000000..9f6fb50
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/network_test_runner/ProductRegistryTestRunner.js
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+NetworkTestRunner.resetProductRegistry = function() {
+  TestRunner.addResult('Cleared ProductRegistryImpl');
+  ProductRegistryImpl._productsByDomainHash.clear();
+};
+
+NetworkTestRunner.addProductRegistryEntry = function(domainPattern, productName, type) {
+  TestRunner.addResult('Adding entry: ' + domainPattern);
+  var wildCardPosition = domainPattern.indexOf('*');
+  var prefix = '';
+
+  if (wildCardPosition === -1) {
+  } else if (wildCardPosition === 0) {
+    prefix = '*';
+    console.assert(domainPattern.substr(1, 1) === '.', 'Domain pattern wildcard must be followed by \'.\'');
+    domainPattern = domainPattern.substr(2);
+  } else {
+    prefix = domainPattern.substr(0, wildCardPosition);
+    console.assert(
+        domainPattern.substr(wildCardPosition + 1, 1) === '.', 'Domain pattern wildcard must be followed by \'.\'');
+    domainPattern = domainPattern.substr(wildCardPosition + 2);
+  }
+
+  console.assert(domainPattern.indexOf('*') === -1, 'Domain pattern may only have 1 wildcard.');
+  var prefixes = {};
+
+  prefixes[prefix] = {product: 0, type: type};
+
+  ProductRegistryImpl.register(
+      [productName], [{hash: ProductRegistryImpl._hashForDomain(domainPattern), prefixes: prefixes}]);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/network_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/network_test_runner/module.json
new file mode 100644
index 0000000..82029ea
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/network_test_runner/module.json
@@ -0,0 +1,16 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "product_registry_impl",
+    "console_test_runner"
+  ],
+  "scripts": [
+    "NetworkTestRunner.js",
+    "ProductRegistryTestRunner.js"
+  ],
+  "skip_compilation": [
+    "NetworkTestRunner.js",
+    "ProductRegistryTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/performance_test_runner/TimelineDataTestRunner.js b/third_party/WebKit/Source/devtools/front_end/performance_test_runner/TimelineDataTestRunner.js
new file mode 100644
index 0000000..6f53685
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/performance_test_runner/TimelineDataTestRunner.js
@@ -0,0 +1,5363 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+PerformanceTestRunner.timelineData = function() {
+  return JSON.stringify([
+    {
+      'args': {'number': 32},
+
+      'cat': '__metadata',
+      'name': 'num_cpus',
+      'ph': 'M',
+      'pid': 3840,
+      'tid': 0,
+      'ts': 0
+    },
+    {
+      'args': {'sort_index': -5},
+
+      'cat': '__metadata',
+      'name': 'process_sort_index',
+      'ph': 'M',
+      'pid': 3840,
+      'tid': 12,
+      'ts': 0
+    },
+    {
+      'args': {'name': 'Renderer'},
+
+      'cat': '__metadata',
+      'name': 'process_name',
+      'ph': 'M',
+      'pid': 3840,
+      'tid': 12,
+      'ts': 0
+    },
+    {
+      'args': {'sort_index': -1},
+
+      'cat': '__metadata',
+      'name': 'thread_sort_index',
+      'ph': 'M',
+      'pid': 3840,
+      'tid': 11,
+      'ts': 0
+    },
+    {
+      'args': {'sessionId': '9.4'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'TracingStartedInPage',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673092068,
+      'tts': 170409
+    },
+    {
+      'args': {'layerTreeId': 1, 'sessionId': '9.4'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'SetLayerTreeId',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673092082,
+      'tts': 170421
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673092095,
+      'tts': 170434
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673107791,
+      'tts': 170482
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673107799,
+      'tts': 170490
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 438}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 65,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 64,
+      'tid': 9,
+      'ts': 1122673107821,
+      'tts': 170511
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 27,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 26,
+      'tid': 9,
+      'ts': 1122673107849,
+      'tts': 170539
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 439}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673107869,
+      'tts': 170559
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2688936, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673107883,
+      'tts': 170573
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673107891,
+      'tts': 170581
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673107921,
+      'tts': 170612
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673107934,
+      'tts': 170624
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673107941,
+      'tts': 170631
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673126470,
+      'tts': 170708
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673126480,
+      'tts': 170717
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 439}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 108,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 107,
+      'tid': 9,
+      'ts': 1122673126522,
+      'tts': 170759
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 47,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 46,
+      'tid': 9,
+      'ts': 1122673126569,
+      'tts': 170806
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 440}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673126606,
+      'tts': 170843
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2688952, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673126627,
+      'tts': 170864
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673126637,
+      'tts': 170874
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673126683,
+      'tts': 170920
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673126699,
+      'tts': 170936
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673126708,
+      'tts': 170944
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673141177,
+      'tts': 171002
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673141186,
+      'tts': 171010
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 440}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 110,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 109,
+      'tid': 9,
+      'ts': 1122673141212,
+      'tts': 171036
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 46,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 43,
+      'tid': 9,
+      'ts': 1122673141259,
+      'tts': 171084
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 441}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673141293,
+      'tts': 171117
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2688968, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673141318,
+      'tts': 171142
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673141329,
+      'tts': 171154
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673141379,
+      'tts': 171204
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673141406,
+      'tts': 171231
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673141421,
+      'tts': 171246
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673162929,
+      'tts': 171304
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673162938,
+      'tts': 171313
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 441}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 97,
+      'tid': 9,
+      'ts': 1122673162975,
+      'tts': 171350
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 41,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 39,
+      'tid': 9,
+      'ts': 1122673163020,
+      'tts': 171395
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 442}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673163051,
+      'tts': 171426
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2688984, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673163070,
+      'tts': 171445
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673163081,
+      'tts': 171455
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673163119,
+      'tts': 171493
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673163133,
+      'tts': 171508
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673163141,
+      'tts': 171516
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673179592,
+      'tts': 171569
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673179600,
+      'tts': 171576
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 442}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 77,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 76,
+      'tid': 9,
+      'ts': 1122673179625,
+      'tts': 171601
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 31,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 31,
+      'tid': 9,
+      'ts': 1122673179660,
+      'tts': 171635
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 443}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673179683,
+      'tts': 171659
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689000, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673179699,
+      'tts': 171675
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673179707,
+      'tts': 171682
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673179736,
+      'tts': 171711
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673179751,
+      'tts': 171727
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673179759,
+      'tts': 171735
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673196258,
+      'tts': 171788
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673196265,
+      'tts': 171794
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 443}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 73,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 72,
+      'tid': 9,
+      'ts': 1122673196291,
+      'tts': 171820
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 30,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 29,
+      'tid': 9,
+      'ts': 1122673196323,
+      'tts': 171852
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 444}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673196346,
+      'tts': 171875
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689016, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673196361,
+      'tts': 171890
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673196370,
+      'tts': 171899
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673196402,
+      'tts': 171931
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673196417,
+      'tts': 171946
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673196425,
+      'tts': 171954
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673213043,
+      'tts': 172016
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673107747,
+      'tts': 77989
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673107777,
+      'tts': 78018
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673126358,
+      'tts': 78708
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673126413,
+      'tts': 78760
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673141111,
+      'tts': 78880
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673141133,
+      'tts': 78902
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673162867,
+      'tts': 79126
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673162886,
+      'tts': 79145
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673179534,
+      'tts': 79253
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122673179551,
+      'tts': 79269
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673313423,
+      'tts': 174171
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673313452,
+      'tts': 174200
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673313467,
+      'tts': 174215
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673329958,
+      'tts': 174302
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673329971,
+      'tts': 174314
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 451}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 96,
+      'tid': 9,
+      'ts': 1122673330000,
+      'tts': 174343
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 41,
+      'tid': 9,
+      'ts': 1122673330035,
+      'tts': 174379
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 452}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673330066,
+      'tts': 174409
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689144, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673330092,
+      'tts': 174435
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673330106,
+      'tts': 174450
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673330151,
+      'tts': 174495
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673330180,
+      'tts': 174523
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673330194,
+      'tts': 174538
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673346680,
+      'tts': 174624
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673346692,
+      'tts': 174635
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 452}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 96,
+      'tid': 9,
+      'ts': 1122673346721,
+      'tts': 174664
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 43,
+      'tid': 9,
+      'ts': 1122673346756,
+      'tts': 174699
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 453}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673346787,
+      'tts': 174730
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689160, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673346813,
+      'tts': 174756
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673346827,
+      'tts': 174770
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673346871,
+      'tts': 174814
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673346900,
+      'tts': 174843
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673346914,
+      'tts': 174857
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673363348,
+      'tts': 174943
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673363361,
+      'tts': 174954
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 453}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 95,
+      'tid': 9,
+      'ts': 1122673363390,
+      'tts': 174984
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673363425,
+      'tts': 175019
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 454}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673363456,
+      'tts': 175050
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689176, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673363482,
+      'tts': 175075
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673363496,
+      'tts': 175090
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673363540,
+      'tts': 175134
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673363569,
+      'tts': 175163
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673363584,
+      'tts': 175177
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673380043,
+      'tts': 175286
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673380057,
+      'tts': 175299
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 454}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 99,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 97,
+      'tid': 9,
+      'ts': 1122673380086,
+      'tts': 175328
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673380122,
+      'tts': 175364
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 455}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673380153,
+      'tts': 175396
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689192, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673380179,
+      'tts': 175421
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673380194,
+      'tts': 175436
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673380238,
+      'tts': 175480
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673380266,
+      'tts': 175508
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673380281,
+      'tts': 175523
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673396686,
+      'tts': 175611
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673396699,
+      'tts': 175622
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 455}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 99,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 96,
+      'tid': 9,
+      'ts': 1122673396735,
+      'tts': 175659
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673396771,
+      'tts': 175694
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 456}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673396802,
+      'tts': 175725
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689208, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673396828,
+      'tts': 175751
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673396842,
+      'tts': 175765
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673396886,
+      'tts': 175810
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673396915,
+      'tts': 175838
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673396930,
+      'tts': 175853
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673413345,
+      'tts': 175940
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673413358,
+      'tts': 175952
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674113927,
+      'tts': 89784
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674113950,
+      'tts': 89806
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674130606,
+      'tts': 89979
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674130629,
+      'tts': 90001
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674147273,
+      'tts': 90157
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674147296,
+      'tts': 90179
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674163929,
+      'tts': 90337
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674163951,
+      'tts': 90358
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673747387,
+      'tts': 182445
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 476}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 100,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 97,
+      'tid': 9,
+      'ts': 1122673747439,
+      'tts': 182498
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673747475,
+      'tts': 182534
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 477}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673747507,
+      'tts': 182565
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689544, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673747532,
+      'tts': 182591
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673747547,
+      'tts': 182605
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673747592,
+      'tts': 182650
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673747621,
+      'tts': 182680
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673747636,
+      'tts': 182694
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673764047,
+      'tts': 182781
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673764060,
+      'tts': 182792
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 477}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 99,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 97,
+      'tid': 9,
+      'ts': 1122673764089,
+      'tts': 182821
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673764125,
+      'tts': 182858
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 478}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673764156,
+      'tts': 182889
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689560, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673764182,
+      'tts': 182914
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673764196,
+      'tts': 182929
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673764241,
+      'tts': 182973
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673764268,
+      'tts': 183001
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673764283,
+      'tts': 183015
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673780702,
+      'tts': 183103
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673780715,
+      'tts': 183114
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 478}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 106,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 104,
+      'tid': 9,
+      'ts': 1122673780744,
+      'tts': 183143
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673780787,
+      'tts': 183186
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 479}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673780818,
+      'tts': 183217
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689576, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673780844,
+      'tts': 183243
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673780858,
+      'tts': 183257
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673780902,
+      'tts': 183302
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673780932,
+      'tts': 183331
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673780946,
+      'tts': 183346
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673797374,
+      'tts': 183434
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673797387,
+      'tts': 183445
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 479}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 106,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 104,
+      'tid': 9,
+      'ts': 1122673797416,
+      'tts': 183475
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673797458,
+      'tts': 183517
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 480}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673797489,
+      'tts': 183548
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689592, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673797516,
+      'tts': 183575
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673797530,
+      'tts': 183589
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673797575,
+      'tts': 183633
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673797604,
+      'tts': 183662
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673797618,
+      'tts': 183677
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673814044,
+      'tts': 183764
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673814056,
+      'tts': 183776
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 480}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 96,
+      'tid': 9,
+      'ts': 1122673814086,
+      'tts': 183805
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 43,
+      'tid': 9,
+      'ts': 1122673814121,
+      'tts': 183840
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 481}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673814153,
+      'tts': 183872
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689608, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673814178,
+      'tts': 183897
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673814193,
+      'tts': 183912
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673814237,
+      'tts': 183956
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673814265,
+      'tts': 183985
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673814280,
+      'tts': 183999
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673830696,
+      'tts': 184086
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673830709,
+      'tts': 184097
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 481}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 99,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 97,
+      'tid': 9,
+      'ts': 1122673830738,
+      'tts': 184126
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122673830774,
+      'tts': 184163
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 482}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673830806,
+      'tts': 184194
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689624, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673830831,
+      'tts': 184219
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673830846,
+      'tts': 184234
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673830890,
+      'tts': 184278
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673830917,
+      'tts': 184305
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673830932,
+      'tts': 184320
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122673847325,
+      'tts': 184405
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122673847337,
+      'tts': 184417
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674030729,
+      'tts': 188070
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 493}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 96,
+      'tid': 9,
+      'ts': 1122674030758,
+      'tts': 188099
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122674030794,
+      'tts': 188135
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 494}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674030825,
+      'tts': 188166
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689816, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674030850,
+      'tts': 188191
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674030864,
+      'tts': 188205
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674030908,
+      'tts': 188249
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674030937,
+      'tts': 188278
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674030951,
+      'tts': 188292
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674047345,
+      'tts': 188381
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674047358,
+      'tts': 188393
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 494}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 96,
+      'tid': 9,
+      'ts': 1122674047387,
+      'tts': 188422
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 43,
+      'tid': 9,
+      'ts': 1122674047422,
+      'tts': 188457
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 495}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674047453,
+      'tts': 188489
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689832, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674047479,
+      'tts': 188514
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674047493,
+      'tts': 188528
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674047538,
+      'tts': 188573
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674047566,
+      'tts': 188601
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674047580,
+      'tts': 188615
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674064041,
+      'tts': 188723
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674064054,
+      'tts': 188735
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 495}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 114,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 112,
+      'tid': 9,
+      'ts': 1122674064084,
+      'tts': 188765
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 60,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 57,
+      'tid': 9,
+      'ts': 1122674064120,
+      'tts': 188801
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 496}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674064166,
+      'tts': 188847
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689848, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674064192,
+      'tts': 188873
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674064207,
+      'tts': 188888
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674064252,
+      'tts': 188933
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674064279,
+      'tts': 188960
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674064293,
+      'tts': 188974
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674080701,
+      'tts': 189063
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674080714,
+      'tts': 189075
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 496}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 112,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 110,
+      'tid': 9,
+      'ts': 1122674080743,
+      'tts': 189104
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 46,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 44,
+      'tid': 9,
+      'ts': 1122674080791,
+      'tts': 189152
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 497}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674080823,
+      'tts': 189185
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689864, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674080849,
+      'tts': 189210
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674080864,
+      'tts': 189225
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674080908,
+      'tts': 189269
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674080936,
+      'tts': 189297
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674080950,
+      'tts': 189312
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674097365,
+      'tts': 189401
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674097378,
+      'tts': 189412
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 497}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 97,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 95,
+      'tid': 9,
+      'ts': 1122674097407,
+      'tts': 189441
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 43,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122674097443,
+      'tts': 189477
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 498}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674097473,
+      'tts': 189507
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689880, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674097498,
+      'tts': 189532
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674097513,
+      'tts': 189547
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674097556,
+      'tts': 189591
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674097585,
+      'tts': 189619
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674097599,
+      'tts': 189634
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674114019,
+      'tts': 189724
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674114032,
+      'tts': 189735
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 498}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 97,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 95,
+      'tid': 9,
+      'ts': 1122674114061,
+      'tts': 189765
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122674114096,
+      'tts': 189800
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 499}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674114127,
+      'tts': 189830
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689896, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674114153,
+      'tts': 189856
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674114167,
+      'tts': 189870
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674114211,
+      'tts': 189914
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674114239,
+      'tts': 189942
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674114253,
+      'tts': 189957
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674130694,
+      'tts': 190042
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674130707,
+      'tts': 190054
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 499}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 95,
+      'tid': 9,
+      'ts': 1122674130736,
+      'tts': 190084
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122674130772,
+      'tts': 190119
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 500}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674130802,
+      'tts': 190150
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689912, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674130828,
+      'tts': 190175
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674130842,
+      'tts': 190189
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674130887,
+      'tts': 190234
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674130914,
+      'tts': 190262
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674130929,
+      'tts': 190276
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674147364,
+      'tts': 190365
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674147377,
+      'tts': 190376
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 500}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 111,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 109,
+      'tid': 9,
+      'ts': 1122674147406,
+      'tts': 190406
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 47,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 44,
+      'tid': 9,
+      'ts': 1122674147452,
+      'tts': 190453
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 501}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674147486,
+      'tts': 190486
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689928, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674147511,
+      'tts': 190511
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674147526,
+      'tts': 190526
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674147570,
+      'tts': 190570
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674147598,
+      'tts': 190598
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674147613,
+      'tts': 190613
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674164055,
+      'tts': 190720
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674164068,
+      'tts': 190732
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 501}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 113,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 111,
+      'tid': 9,
+      'ts': 1122674164097,
+      'tts': 190761
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 46,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 44,
+      'tid': 9,
+      'ts': 1122674164146,
+      'tts': 190810
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 502}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674164178,
+      'tts': 190842
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689944, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674164204,
+      'tts': 190868
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674164219,
+      'tts': 190883
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674164263,
+      'tts': 190927
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674164312,
+      'tts': 190976
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674164327,
+      'tts': 190991
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674180694,
+      'tts': 191071
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674180707,
+      'tts': 191083
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 502}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 111,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 109,
+      'tid': 9,
+      'ts': 1122674180736,
+      'tts': 191112
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 46,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 43,
+      'tid': 9,
+      'ts': 1122674180783,
+      'tts': 191160
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 503}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674180816,
+      'tts': 191192
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689960, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674180841,
+      'tts': 191217
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674180855,
+      'tts': 191232
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674180899,
+      'tts': 191276
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674180928,
+      'tts': 191305
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674180943,
+      'tts': 191319
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674197354,
+      'tts': 191407
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674197367,
+      'tts': 191418
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 503}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 96,
+      'tid': 9,
+      'ts': 1122674197396,
+      'tts': 191447
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122674197431,
+      'tts': 191483
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 504}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674197462,
+      'tts': 191514
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689976, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674197487,
+      'tts': 191539
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674197502,
+      'tts': 191553
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674197546,
+      'tts': 191598
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674197590,
+      'tts': 191642
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674197606,
+      'tts': 191657
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674214025,
+      'tts': 191747
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674214038,
+      'tts': 191759
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 504}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 111,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 110,
+      'tid': 9,
+      'ts': 1122674214067,
+      'tts': 191788
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 43,
+      'tid': 9,
+      'ts': 1122674214115,
+      'tts': 191836
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 505}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674214147,
+      'tts': 191868
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2689992, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674214173,
+      'tts': 191894
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674214187,
+      'tts': 191908
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674214232,
+      'tts': 191953
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674214259,
+      'tts': 191981
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674214274,
+      'tts': 191995
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674230673,
+      'tts': 192083
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674230686,
+      'tts': 192095
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 505}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 125,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 123,
+      'tid': 9,
+      'ts': 1122674230716,
+      'tts': 192124
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 48,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 46,
+      'tid': 9,
+      'ts': 1122674230775,
+      'tts': 192183
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 506}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674230809,
+      'tts': 192217
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2690008, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674230835,
+      'tts': 192243
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674230849,
+      'tts': 192258
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674230894,
+      'tts': 192303
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674230924,
+      'tts': 192332
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674230938,
+      'tts': 192347
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674247362,
+      'tts': 192436
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674247374,
+      'tts': 192447
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 506}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 97,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 95,
+      'tid': 9,
+      'ts': 1122674247403,
+      'tts': 192476
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 43,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 41,
+      'tid': 9,
+      'ts': 1122674247439,
+      'tts': 192512
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 507}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674247469,
+      'tts': 192543
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2690024, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674247494,
+      'tts': 192567
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674247509,
+      'tts': 192582
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674247553,
+      'tts': 192626
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674247581,
+      'tts': 192655
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674247596,
+      'tts': 192669
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674264048,
+      'tts': 192774
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674264062,
+      'tts': 192786
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 507}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 99,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 97,
+      'tid': 9,
+      'ts': 1122674264091,
+      'tts': 192815
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 45,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122674264127,
+      'tts': 192851
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 508}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674264158,
+      'tts': 192883
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2690040, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674264184,
+      'tts': 192908
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674264199,
+      'tts': 192923
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674264243,
+      'tts': 192967
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674264272,
+      'tts': 192996
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674264286,
+      'tts': 193010
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674280687,
+      'tts': 193099
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674180599,
+      'tts': 90536
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674180634,
+      'tts': 90570
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674197265,
+      'tts': 90728
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674197288,
+      'tts': 90750
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674213934,
+      'tts': 90908
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674213956,
+      'tts': 90929
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674230583,
+      'tts': 91100
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674230606,
+      'tts': 91122
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674247271,
+      'tts': 91300
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674247293,
+      'tts': 91321
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674263915,
+      'tts': 91477
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674263938,
+      'tts': 91499
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674280596,
+      'tts': 91655
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674280619,
+      'tts': 91677
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674297261,
+      'tts': 91831
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674297287,
+      'tts': 91856
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674313908,
+      'tts': 92015
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674313930,
+      'tts': 92036
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674330601,
+      'tts': 92208
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674330623,
+      'tts': 92230
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674347259,
+      'tts': 92385
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674347281,
+      'tts': 92407
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674363933,
+      'tts': 92564
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674363956,
+      'tts': 92586
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674380587,
+      'tts': 92742
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674380610,
+      'tts': 92764
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674397245,
+      'tts': 92919
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674397268,
+      'tts': 92940
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674413915,
+      'tts': 93111
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674413938,
+      'tts': 93133
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674430594,
+      'tts': 93290
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674430617,
+      'tts': 93312
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674447250,
+      'tts': 93468
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674447273,
+      'tts': 93490
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674463925,
+      'tts': 93617
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674463948,
+      'tts': 93639
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674480599,
+      'tts': 93799
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674480621,
+      'tts': 93820
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674497252,
+      'tts': 93974
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674497275,
+      'tts': 93996
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674513924,
+      'tts': 94167
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674513946,
+      'tts': 94189
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674530585,
+      'tts': 94357
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674530607,
+      'tts': 94379
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674547254,
+      'tts': 94537
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674547276,
+      'tts': 94558
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'BeginFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674563916,
+      'tts': 94716
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline.frame',
+      'name': 'RequestMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 't',
+      'tid': 17,
+      'ts': 1122674563954,
+      'tts': 94753
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674514204,
+      'tts': 198558
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674514232,
+      'tts': 198586
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674514246,
+      'tts': 198600
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674530672,
+      'tts': 198686
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674530685,
+      'tts': 198697
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 523}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 97,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 95,
+      'tid': 9,
+      'ts': 1122674530714,
+      'tts': 198727
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 41,
+      'tid': 9,
+      'ts': 1122674530749,
+      'tts': 198762
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 524}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674530780,
+      'tts': 198792
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2693784, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674530805,
+      'tts': 198817
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674530820,
+      'tts': 198832
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674530864,
+      'tts': 198876
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674530892,
+      'tts': 198905
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674530907,
+      'tts': 198919
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674547345,
+      'tts': 199009
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674547358,
+      'tts': 199020
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 524}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 98,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 95,
+      'tid': 9,
+      'ts': 1122674547387,
+      'tts': 199050
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 41,
+      'tid': 9,
+      'ts': 1122674547423,
+      'tts': 199086
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 525}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674547454,
+      'tts': 199116
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2693800, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674547479,
+      'tts': 199141
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674547493,
+      'tts': 199156
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674547538,
+      'tts': 199200
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674547566,
+      'tts': 199229
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674547581,
+      'tts': 199243
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674564053,
+      'tts': 199343
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'BeginMainThreadFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674564066,
+      'tts': 199355
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 525}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 99,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 97,
+      'tid': 9,
+      'ts': 1122674564096,
+      'tts': 199384
+    },
+    {
+      'args': {
+        'data':
+            {'frame': '0x1100e70a8000', 'scriptId': '29', 'scriptLine': 2, 'scriptName': 'http://localhost/raf.html'}
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 44,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3834,
+      'tdur': 42,
+      'tid': 9,
+      'ts': 1122674564132,
+      'tts': 199420
+    },
+    {
+      'args': {'data': {'frame': '0x1100e70a8000', 'id': 526}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674564162,
+      'tts': 199451
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 0, 'jsHeapSizeUsed': 2693816, 'nodes': 7}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674564188,
+      'tts': 199477
+    },
+    {
+      'args': {'frame': '0x1100e70a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3834,
+      's': 'g',
+      'tid': 9,
+      'ts': 1122674564203,
+      'tts': 199492
+    },
+    {
+      'args': {'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'B',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674564248,
+      'tts': 199536
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'CompositeLayers',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674564276,
+      'tts': 199565
+    },
+    {
+      'args': {},
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Program',
+      'ph': 'E',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 1122674564291,
+      'tts': 199579
+    },
+    {
+      'args': {'number': 32},
+
+      'cat': '__metadata',
+      'name': 'num_cpus',
+      'ph': 'M',
+      'pid': 3834,
+      'tid': 0,
+      'ts': 0
+    },
+    {
+      'args': {'sort_index': -5},
+
+      'cat': '__metadata',
+      'name': 'process_sort_index',
+      'ph': 'M',
+      'pid': 3834,
+      'tid': 10,
+      'ts': 0
+    },
+    {
+      'args': {'name': 'Renderer'},
+
+      'cat': '__metadata',
+      'name': 'process_name',
+      'ph': 'M',
+      'pid': 3834,
+      'tid': 10,
+      'ts': 0
+    },
+    {
+      'args': {'sort_index': -1},
+
+      'cat': '__metadata',
+      'name': 'thread_sort_index',
+      'ph': 'M',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 0
+    },
+    {
+      'args': {'name': 'CrRendererMain'},
+
+      'cat': '__metadata',
+      'name': 'thread_name',
+      'ph': 'M',
+      'pid': 3834,
+      'tid': 9,
+      'ts': 0
+    },
+    {
+      'args': {'name': 'Compositor'},
+
+      'cat': '__metadata',
+      'name': 'thread_name',
+      'ph': 'M',
+      'pid': 3834,
+      'tid': 17,
+      'ts': 0
+    },
+    {
+      'args': {'elementCount': 47},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RecalculateStyles',
+      'ph': 'E',
+      'pid': 3872,
+      'tid': 26,
+      'ts': 1122673092850,
+      'tts': 827512
+    },
+    {
+      'args':
+          {'beginData': {'dirtyObjects': 44, 'frame': '0x176b9c2a8000', 'partialLayout': false, 'totalObjects': 261}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Layout',
+      'ph': 'B',
+      'pid': 3872,
+      'tid': 26,
+      'ts': 1122673092873,
+      'tts': 827535
+    },
+    {
+      'args': {'endData': {'root': [0, 0, 1570, 0, 1570, 1472, 0, 1472], 'rootNode': 2}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'Layout',
+      'ph': 'E',
+      'pid': 3872,
+      'tid': 26,
+      'ts': 1122673093523,
+      'tts': 828186
+    },
+    {
+      'args': {'data': {'frame': '0x176b9c2a8000', 'id': 12}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 1828,
+      'name': 'FireAnimationFrame',
+      'ph': 'X',
+      'pid': 3872,
+      'tdur': 1827,
+      'tid': 26,
+      'ts': 1122673093558,
+      'tts': 828220
+    },
+    {
+      'args': {
+        'data': {
+          'frame': '0x176b9c2a8000',
+          'scriptId': '65',
+          'scriptLine': 939,
+          'scriptName': 'chrome-devtools://devtools/bundled/ui/UIUtils.js'
+        }
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 1770,
+      'name': 'FunctionCall',
+      'ph': 'X',
+      'pid': 3872,
+      'tdur': 1770,
+      'tid': 26,
+      'ts': 1122673093604,
+      'tts': 828265
+    },
+    {
+      'args': {'frame': '0x176b9c2a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'ScheduleStyleRecalculation',
+      'ph': 'I',
+      'pid': 3872,
+      's': 'g',
+      'tid': 26,
+      'ts': 1122673094798,
+      'tts': 829461
+    },
+    {
+      'args': {'frame': '0x176b9c2a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RecalculateStyles',
+      'ph': 'B',
+      'pid': 3872,
+      'tid': 26,
+      'ts': 1122673094958,
+      'tts': 829620
+    },
+    {
+      'args': {'elementCount': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RecalculateStyles',
+      'ph': 'E',
+      'pid': 3872,
+      'tid': 26,
+      'ts': 1122673094989,
+      'tts': 829651
+    },
+    {
+      'args': {'data': {'frame': '0x176b9c2a8000', 'id': 13}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'RequestAnimationFrame',
+      'ph': 'I',
+      'pid': 3872,
+      's': 'g',
+      'tid': 26,
+      'ts': 1122673095367,
+      'tts': 830030
+    },
+    {
+      'args': {'data': {'documents': 1, 'jsEventListeners': 141, 'jsHeapSizeUsed': 15850696, 'nodes': 1705}},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateCounters',
+      'ph': 'I',
+      'pid': 3872,
+      's': 'g',
+      'tid': 26,
+      'ts': 1122673095384,
+      'tts': 830046
+    },
+    {
+      'args': {'frame': '0x176b9c2a8000'},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayerTree',
+      'ph': 'I',
+      'pid': 3872,
+      's': 'g',
+      'tid': 26,
+      'ts': 1122673095390,
+      'tts': 830052
+    },
+    {
+      'args': {'layerId': 11, 'layerTreeId': 1},
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'name': 'UpdateLayer',
+      'ph': 'B',
+      'pid': 3872,
+      'tid': 26,
+      'ts': 1122673095845,
+      'tts': 830507
+    },
+    {
+      'args': {
+        'data': {
+          'clip': [-15, -15, 1585, -15, 1585, 1487, -15, 1487],
+          'frame': '0x176b9c2a8000',
+          'layerId': 11,
+          'nodeId': 2
+        }
+      },
+
+      'cat': 'disabled-by-default-devtools.timeline',
+      'dur': 4637,
+      'name': 'Paint',
+      'ph': 'X',
+      'pid': 3872,
+      'tdur': 4630,
+      'tid': 26,
+      'ts': 1122673095897,
+      'tts': 830559
+    },
+    {
+      'args': {'name': 'Browser'},
+
+      'cat': '__metadata',
+      'name': 'process_name',
+      'ph': 'M',
+      'pid': 3778,
+      'tid': 3801,
+      'ts': 0
+    },
+    {
+      'args': {'name': 'CrBrowserMain'},
+
+      'cat': '__metadata',
+      'name': 'thread_name',
+      'ph': 'M',
+      'pid': 3778,
+      'tid': 3778,
+      'ts': 0
+    }
+  ]);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/performance_test_runner/TimelineTestRunner.js b/third_party/WebKit/Source/devtools/front_end/performance_test_runner/TimelineTestRunner.js
new file mode 100644
index 0000000..2f8442b
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/performance_test_runner/TimelineTestRunner.js
@@ -0,0 +1,408 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+PerformanceTestRunner.timelinePropertyFormatters = {
+  children: 'formatAsTypeName',
+  endTime: 'formatAsTypeName',
+  requestId: 'formatAsTypeName',
+  startTime: 'formatAsTypeName',
+  stackTrace: 'formatAsTypeName',
+  url: 'formatAsURL',
+  scriptName: 'formatAsTypeName',
+  scriptId: 'formatAsTypeName',
+  usedHeapSizeDelta: 'skip',
+  mimeType: 'formatAsTypeName',
+  id: 'formatAsTypeName',
+  timerId: 'formatAsTypeName',
+  scriptLine: 'formatAsTypeName',
+  layerId: 'formatAsTypeName',
+  lineNumber: 'formatAsTypeName',
+  columnNumber: 'formatAsTypeName',
+  frameId: 'formatAsTypeName',
+  frame: 'formatAsTypeName',
+  page: 'formatAsTypeName',
+  encodedDataLength: 'formatAsTypeName',
+  identifier: 'formatAsTypeName',
+  clip: 'formatAsTypeName',
+  root: 'formatAsTypeName',
+  backendNodeId: 'formatAsTypeName',
+  nodeId: 'formatAsTypeName',
+  rootNode: 'formatAsTypeName',
+  finishTime: 'formatAsTypeName',
+  thread: 'formatAsTypeName',
+  allottedMilliseconds: 'formatAsTypeName',
+  timedOut: 'formatAsTypeName',
+  networkTime: 'formatAsTypeName'
+};
+
+PerformanceTestRunner.InvalidationFormatters = {
+  _tracingEvent: 'skip',
+  cause: 'formatAsInvalidationCause',
+  frame: 'skip',
+  invalidatedSelectorId: 'skip',
+  invalidationList: 'skip',
+  invalidationSet: 'skip',
+  linkedRecalcStyleEvent: 'skip',
+  linkedLayoutEvent: 'skip',
+  nodeId: 'skip',
+  paintId: 'skip',
+  startTime: 'skip'
+};
+
+TestRunner.formatters.formatAsInvalidationCause = function(cause) {
+  if (!cause)
+    return '<undefined>';
+
+  var stackTrace;
+
+  if (cause.stackTrace && cause.stackTrace.length) {
+    stackTrace =
+        TestRunner.formatters.formatAsURL(cause.stackTrace[0].url) + ':' + (cause.stackTrace[0].lineNumber + 1);
+  }
+
+  return '{reason: ' + cause.reason + ', stackTrace: ' + stackTrace + '}';
+};
+
+PerformanceTestRunner.createTracingModel = function(events) {
+  var model = new SDK.TracingModel(new Bindings.TempFileBackingStorage('tracing'));
+  model.addEvents(events);
+  model.tracingComplete();
+  return model;
+};
+
+PerformanceTestRunner.tracingModel = function() {
+  return UI.panels.timeline._performanceModel.tracingModel();
+};
+
+PerformanceTestRunner.invokeWithTracing = function(functionName, callback, additionalCategories, enableJSSampling) {
+  var categories =
+      '-*,disabled-by-default-devtools.timeline*,devtools.timeline,' + SDK.TracingModel.TopLevelEventCategory;
+
+  if (additionalCategories)
+    categories += ',' + additionalCategories;
+
+  var timelinePanel = UI.panels.timeline;
+  var timelineController = PerformanceTestRunner.timelineController();
+  timelinePanel._timelineController = timelineController;
+  timelineController._startRecordingWithCategories(categories, enableJSSampling).then(tracingStarted);
+
+  function tracingStarted() {
+    timelinePanel._recordingStarted();
+    TestRunner.callFunctionInPageAsync(functionName).then(onPageActionsDone);
+  }
+
+  function onPageActionsDone() {
+    PerformanceTestRunner.runWhenTimelineIsReady(callback);
+    timelineController.stopRecording();
+  }
+};
+
+PerformanceTestRunner.performanceModel = function() {
+  return UI.panels.timeline._performanceModel;
+};
+
+PerformanceTestRunner.timelineModel = function() {
+  return PerformanceTestRunner.performanceModel().timelineModel();
+};
+
+PerformanceTestRunner.timelineFrameModel = function() {
+  return PerformanceTestRunner.performanceModel().frameModel();
+};
+
+PerformanceTestRunner.createPerformanceModelWithEvents = function(events) {
+  var tracingModel = new SDK.TracingModel(new Bindings.TempFileBackingStorage('tracing'));
+  tracingModel.addEvents(events);
+  tracingModel.tracingComplete();
+  var performanceModel = new Timeline.PerformanceModel();
+  performanceModel.setTracingModel(tracingModel);
+  return performanceModel;
+};
+
+PerformanceTestRunner.timelineController = function() {
+  var performanceModel = new Timeline.PerformanceModel();
+  UI.panels.timeline._pendingPerformanceModel = performanceModel;
+  return new Timeline.TimelineController(TestRunner.tracingManager, performanceModel, UI.panels.timeline);
+};
+
+PerformanceTestRunner.runWhenTimelineIsReady = function(callback) {
+  TestRunner.addSniffer(UI.panels.timeline, 'loadingComplete', () => callback());
+};
+
+PerformanceTestRunner.startTimeline = function(callback) {
+  var panel = UI.panels.timeline;
+  TestRunner.addSniffer(panel, '_recordingStarted', callback);
+  panel._toggleRecording();
+};
+
+PerformanceTestRunner.stopTimeline = function(callback) {
+  PerformanceTestRunner.runWhenTimelineIsReady(callback);
+  UI.panels.timeline._toggleRecording();
+};
+
+PerformanceTestRunner.evaluateWithTimeline = function(actions, doneCallback) {
+  PerformanceTestRunner.startTimeline(step1);
+
+  function step1() {
+    TestRunner.evaluateInPage(actions, step2);
+  }
+
+  function step2() {
+    PerformanceTestRunner.stopTimeline(doneCallback);
+  }
+};
+
+PerformanceTestRunner.invokeAsyncWithTimeline = function(functionName, doneCallback) {
+  PerformanceTestRunner.startTimeline(step1);
+
+  function step1() {
+    TestRunner.callFunctionInPageAsync(functionName).then(step2);
+  }
+
+  function step2() {
+    PerformanceTestRunner.stopTimeline(TestRunner.safeWrap(doneCallback));
+  }
+};
+
+PerformanceTestRunner.performActionsAndPrint = function(actions, typeName, includeTimeStamps) {
+  function callback() {
+    PerformanceTestRunner.printTimelineRecordsWithDetails(typeName);
+
+    if (includeTimeStamps) {
+      TestRunner.addResult('Timestamp records: ');
+      PerformanceTestRunner.printTimestampRecords(typeName);
+    }
+
+    TestRunner.completeTest();
+  }
+
+  PerformanceTestRunner.evaluateWithTimeline(actions, callback);
+};
+
+PerformanceTestRunner.printTimelineRecords = function(name) {
+  for (let event of PerformanceTestRunner.timelineModel().mainThreadEvents()) {
+    if (event.name === name)
+      PerformanceTestRunner.printTraceEventProperties(event);
+  }
+};
+
+PerformanceTestRunner.printTimelineRecordsWithDetails = function(name) {
+  for (let event of PerformanceTestRunner.timelineModel().mainThreadEvents()) {
+    if (name === event.name)
+      PerformanceTestRunner.printTraceEventPropertiesWithDetails(event);
+  }
+};
+
+PerformanceTestRunner.walkTimelineEventTree = function(callback) {
+  var performanceModel = PerformanceTestRunner.performanceModel();
+  var view = new Timeline.EventsTimelineTreeView(UI.panels.timeline._filters, null);
+  view.setModel(performanceModel);
+  var selection = Timeline.TimelineSelection.fromRange(
+      performanceModel.timelineModel().minimumRecordTime(), performanceModel.timelineModel().maximumRecordTime());
+  view.updateContents(selection);
+  PerformanceTestRunner.walkTimelineEventTreeUnderNode(callback, view._currentTree, 0);
+};
+
+PerformanceTestRunner.walkTimelineEventTreeUnderNode = function(callback, root, level) {
+  var event = root.event;
+
+  if (event)
+    callback(event, level);
+
+  for (var child of root.children().values())
+    PerformanceTestRunner.walkTimelineEventTreeUnderNode(callback, child, (level || 0) + 1);
+};
+
+PerformanceTestRunner.printTimestampRecords = function(typeName) {
+  var dividers = PerformanceTestRunner.timelineModel().eventDividers();
+
+  for (var event of dividers) {
+    if (event.name === typeName)
+      PerformanceTestRunner.printTraceEventProperties(event);
+  }
+};
+
+PerformanceTestRunner.forAllEvents = function(events, callback) {
+  let eventStack = [];
+
+  for (let event of events) {
+    while (eventStack.length && eventStack.peekLast().endTime <= event.startTime)
+      eventStack.pop();
+
+    callback(event, eventStack);
+
+    if (event.endTime)
+      eventStack.push(event);
+  }
+};
+
+PerformanceTestRunner.printTraceEventProperties = function(traceEvent) {
+  TestRunner.addResult(traceEvent.name + ' Properties:');
+  var data = traceEvent.args['beginData'] || traceEvent.args['data'];
+  var frameId = data && data['frame'];
+
+  var object = {
+    data: traceEvent.args['data'] || traceEvent.args,
+    endTime: traceEvent.endTime || traceEvent.startTime,
+    frameId: frameId,
+    stackTrace: TimelineModel.TimelineData.forEvent(traceEvent).stackTrace,
+    startTime: traceEvent.startTime,
+    type: traceEvent.name
+  };
+
+  for (var field in object) {
+    if (object[field] === null || object[field] === undefined)
+      delete object[field];
+  }
+
+  TestRunner.addObject(object, PerformanceTestRunner.timelinePropertyFormatters);
+};
+
+PerformanceTestRunner.printTraceEventPropertiesWithDetails = function(event) {
+  PerformanceTestRunner.printTraceEventProperties(event);
+  const details = Timeline.TimelineUIUtils.buildDetailsTextForTraceEvent(
+      event, SDK.targetManager.mainTarget(), new Components.Linkifier());
+  TestRunner.addResult(`Text details for ${event.name}: ${details}`);
+
+  if (TimelineModel.TimelineData.forEvent(event).warning)
+    TestRunner.addResult(`${event.name} has a warning`);
+};
+
+PerformanceTestRunner.findTimelineEvent = function(name, index) {
+  return PerformanceTestRunner.timelineModel().mainThreadEvents().filter(e => e.name === name)[index || 0];
+};
+
+PerformanceTestRunner.findChildEvent = function(events, parentIndex, name) {
+  var endTime = events[parentIndex].endTime;
+
+  for (var i = parentIndex + 1; i < events.length && (!events[i].endTime || events[i].endTime <= endTime); ++i) {
+    if (events[i].name === name)
+      return events[i];
+  }
+
+  return null;
+};
+
+PerformanceTestRunner.dumpFrame = function(frame) {
+  var fieldsToDump = [
+    'cpuTime', 'duration', 'startTime', 'endTime', 'id', 'mainThreadFrameId', 'timeByCategory', 'other', 'scripting',
+    'painting', 'rendering', 'committedFrom', 'idle'
+  ];
+
+  function formatFields(object) {
+    var result = {};
+
+    for (var key in object) {
+      if (fieldsToDump.indexOf(key) < 0)
+        continue;
+
+      var value = object[key];
+
+      if (typeof value === 'number')
+        value = Number(value.toFixed(7));
+      else if (typeof value === 'object' && value)
+        value = formatFields(value);
+
+      result[key] = value;
+    }
+
+    return result;
+  }
+
+  TestRunner.addObject(formatFields(frame));
+};
+
+PerformanceTestRunner.dumpInvalidations = function(recordType, index, comment) {
+  var event = PerformanceTestRunner.findTimelineEvent(recordType, index || 0);
+
+  TestRunner.addArray(
+      TimelineModel.InvalidationTracker.invalidationEventsFor(event), PerformanceTestRunner.InvalidationFormatters, '',
+      comment);
+};
+
+PerformanceTestRunner.dumpFlameChartProvider = function(provider, includeGroups) {
+  var includeGroupsSet = includeGroups && new Set(includeGroups);
+  var timelineData = provider.timelineData();
+  var stackDepth = provider.maxStackDepth();
+  var entriesByLevel = new Multimap();
+
+  for (let i = 0; i < timelineData.entryLevels.length; ++i)
+    entriesByLevel.set(timelineData.entryLevels[i], i);
+
+  for (let groupIndex = 0; groupIndex < timelineData.groups.length; ++groupIndex) {
+    const group = timelineData.groups[groupIndex];
+
+    if (includeGroupsSet && !includeGroupsSet.has(group.name))
+      continue;
+
+    var maxLevel =
+        (groupIndex + 1 < timelineData.groups.length ? timelineData.groups[groupIndex + 1].firstLevel : stackDepth);
+    TestRunner.addResult(`Group: ${group.name}`);
+
+    for (let level = group.startLevel; level < maxLevel; ++level) {
+      TestRunner.addResult(`Level ${level - group.startLevel}`);
+      var entries = entriesByLevel.get(level);
+
+      for (const index of entries) {
+        const title = provider.entryTitle(index);
+        const color = provider.entryColor(index);
+        TestRunner.addResult(`${title} (${color})`);
+      }
+    }
+  }
+};
+
+PerformanceTestRunner.dumpTimelineFlameChart = function(includeGroups) {
+  const provider = UI.panels.timeline._flameChart._mainDataProvider;
+  TestRunner.addResult('Timeline Flame Chart');
+  PerformanceTestRunner.dumpFlameChartProvider(provider, includeGroups);
+};
+
+PerformanceTestRunner.loadTimeline = function(timelineData) {
+  var promise = new Promise(fulfill => PerformanceTestRunner.runWhenTimelineIsReady(fulfill));
+
+  UI.panels.timeline._loadFromFile(new Blob([timelineData], {type: 'text/plain'}));
+
+  return promise;
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    function wrapCallFunctionForTimeline(f) {
+      var script = document.createElement('script');
+      script.textContent = '(' + f.toString() + ')()\n//# sourceURL=wrapCallFunctionForTimeline.js';
+      document.body.appendChild(script);
+    }
+
+    function generateFrames(count) {
+      var promise = Promise.resolve();
+
+      for (let i = count; i > 0; --i)
+        promise = promise.then(changeBackgroundAndWaitForFrame.bind(null, i));
+
+      return promise;
+
+      function changeBackgroundAndWaitForFrame(i) {
+        document.body.style.backgroundColor = (i & 1 ? 'rgb(200, 200, 200)' : 'rgb(240, 240, 240)');
+        return waitForFrame();
+      }
+    }
+
+    function waitForFrame() {
+      var callback;
+      var promise = new Promise(fulfill => callback = fulfill);
+
+      if (window.testRunner)
+        testRunner.capturePixelsAsyncThen(() => window.requestAnimationFrame(callback));
+      else
+        window.requestAnimationFrame(callback);
+
+      return promise;
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/performance_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/performance_test_runner/module.json
new file mode 100644
index 0000000..c3e98e8
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/performance_test_runner/module.json
@@ -0,0 +1,16 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "timeline_model",
+    "timeline"
+  ],
+  "scripts": [
+    "TimelineTestRunner.js",
+    "TimelineDataTestRunner.js"
+  ],
+  "skip_compilation": [
+    "TimelineTestRunner.js",
+    "TimelineDataTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/HeapSnapshotTestRunner.js b/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/HeapSnapshotTestRunner.js
new file mode 100644
index 0000000..c6a2e2d
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/HeapSnapshotTestRunner.js
@@ -0,0 +1,641 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ProfilerTestRunner.createHeapSnapshotMockFactories = function() {
+  ProfilerTestRunner.createJSHeapSnapshotMockObject = function() {
+    return {
+      _rootNodeIndex: 0,
+      _nodeTypeOffset: 0,
+      _nodeNameOffset: 1,
+      _nodeEdgeCountOffset: 2,
+      _nodeFieldCount: 3,
+      _edgeFieldsCount: 3,
+      _edgeTypeOffset: 0,
+      _edgeNameOffset: 1,
+      _edgeToNodeOffset: 2,
+      _nodeTypes: ['hidden', 'object'],
+      _edgeTypes: ['element', 'property', 'shortcut'],
+      _edgeShortcutType: -1,
+      _edgeHiddenType: -1,
+      _edgeElementType: 0,
+      _realNodesLength: 18,
+      nodes: new Uint32Array([0, 0, 2, 1, 1, 2, 1, 2, 2, 1, 3, 1, 1, 4, 0, 1, 5, 0]),
+      containmentEdges: new Uint32Array([2, 6, 3, 1, 7, 6, 0, 1, 6, 1, 8, 9, 1, 9, 9, 1, 10, 12, 1, 11, 15]),
+      strings: ['', 'A', 'B', 'C', 'D', 'E', 'a', 'b', 'ac', 'bc', 'bd', 'ce'],
+      _firstEdgeIndexes: new Uint32Array([0, 6, 12, 18, 21, 21, 21]),
+      createNode: HeapSnapshotWorker.JSHeapSnapshot.prototype.createNode,
+      createEdge: HeapSnapshotWorker.JSHeapSnapshot.prototype.createEdge,
+      createRetainingEdge: HeapSnapshotWorker.JSHeapSnapshot.prototype.createRetainingEdge
+    };
+  };
+
+  ProfilerTestRunner.createHeapSnapshotMockRaw = function() {
+    return {
+      snapshot: {
+        meta: {
+          node_fields: ['type', 'name', 'id', 'self_size', 'retained_size', 'dominator', 'edge_count'],
+          node_types: [['hidden', 'object'], '', '', '', '', '', ''],
+          edge_fields: ['type', 'name_or_index', 'to_node'],
+          edge_types: [['element', 'property', 'shortcut'], '', '']
+        },
+
+        node_count: 6,
+        edge_count: 7
+      },
+
+      nodes: [
+        0, 0, 1, 0, 20, 0, 2, 1, 1, 2, 2, 2, 0,  2, 1, 2, 3, 3, 8, 0,  2,
+        1, 3, 4, 4, 10, 0, 1, 1, 4, 5, 5, 5, 14, 0, 1, 5, 6, 6, 6, 21, 0
+      ],
+
+      edges: [1, 6, 7, 1, 7, 14, 0, 1, 14, 1, 8, 21, 1, 9, 21, 1, 10, 28, 1, 11, 35],
+      strings: ['', 'A', 'B', 'C', 'D', 'E', 'a', 'b', 'ac', 'bc', 'bd', 'ce']
+    };
+  };
+
+  ProfilerTestRunner._postprocessHeapSnapshotMock = function(mock) {
+    mock.nodes = new Uint32Array(mock.nodes);
+    mock.edges = new Uint32Array(mock.edges);
+    return mock;
+  };
+
+  ProfilerTestRunner.createHeapSnapshotMock = function() {
+    return ProfilerTestRunner._postprocessHeapSnapshotMock(ProfilerTestRunner.createHeapSnapshotMockRaw());
+  };
+
+  ProfilerTestRunner.createHeapSnapshotMockWithDOM = function() {
+    return ProfilerTestRunner._postprocessHeapSnapshotMock({
+      snapshot: {
+        meta: {
+          node_fields: ['type', 'name', 'id', 'edge_count'],
+          node_types: [['hidden', 'object', 'synthetic'], '', '', ''],
+          edge_fields: ['type', 'name_or_index', 'to_node'],
+          edge_types: [['element', 'hidden', 'internal'], '', '']
+        },
+
+        node_count: 13,
+        edge_count: 13
+      },
+
+      nodes: [
+        2, 0, 1, 4, 1, 11, 2, 2, 1, 11, 3, 3, 2,  5, 4, 0, 2,  6, 5, 1,  1,  1, 6, 0, 1,  2,
+        7, 1, 1, 4, 8, 2,  1, 8, 9, 0,  1, 7, 10, 0, 1, 3, 11, 0, 1, 10, 12, 0, 1, 9, 13, 0
+      ],
+
+      edges: [
+        0,  1, 4, 0,  2, 8, 0,  3, 12, 0,  4, 16, 0,  1, 20, 0,  2, 24, 0, 1,
+        24, 0, 2, 28, 1, 3, 32, 0, 1,  36, 0, 1,  40, 2, 12, 44, 2, 1,  48
+      ],
+
+      strings: ['', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'M', 'N', 'Window', 'native']
+    });
+  };
+
+  ProfilerTestRunner.HeapNode = function(name, selfSize, type, id) {
+    this._type = type || ProfilerTestRunner.HeapNode.Type.object;
+    this._name = name;
+    this._selfSize = selfSize || 0;
+    this._builder = null;
+    this._edges = {};
+    this._edgesCount = 0;
+    this._id = id;
+  };
+
+  ProfilerTestRunner.HeapNode.Type = {
+    'hidden': 'hidden',
+    'array': 'array',
+    'string': 'string',
+    'object': 'object',
+    'code': 'code',
+    'closure': 'closure',
+    'regexp': 'regexp',
+    'number': 'number',
+    'native': 'native',
+    'synthetic': 'synthetic'
+  };
+
+  ProfilerTestRunner.HeapNode.prototype = {
+    linkNode: function(node, type, nameOrIndex) {
+      if (!this._builder)
+        throw new Error('parent node is not connected to a snapshot');
+
+      if (!node._builder)
+        node._setBuilder(this._builder);
+
+      if (nameOrIndex === undefined)
+        nameOrIndex = this._edgesCount;
+
+      ++this._edgesCount;
+
+      if (nameOrIndex in this._edges) {
+        throw new Error(
+            'Can\'t add edge with the same nameOrIndex. nameOrIndex: ' + nameOrIndex +
+            ' oldNodeName: ' + this._edges[nameOrIndex]._name + ' newNodeName: ' + node._name);
+      }
+
+      this._edges[nameOrIndex] = new ProfilerTestRunner.HeapEdge(node, type, nameOrIndex);
+    },
+
+    _setBuilder: function(builder) {
+      if (this._builder)
+        throw new Error('node reusing is prohibited');
+
+      this._builder = builder;
+      this._ordinal = this._builder._registerNode(this);
+    },
+
+    _serialize: function(rawSnapshot) {
+      rawSnapshot.nodes.push(this._builder.lookupNodeType(this._type));
+      rawSnapshot.nodes.push(this._builder.lookupOrAddString(this._name));
+      rawSnapshot.nodes.push(this._id || this._ordinal * 2 + 1);
+      rawSnapshot.nodes.push(this._selfSize);
+      rawSnapshot.nodes.push(0);
+      rawSnapshot.nodes.push(0);
+      rawSnapshot.nodes.push(Object.keys(this._edges).length);
+
+      for (var i in this._edges)
+        this._edges[i]._serialize(rawSnapshot);
+    }
+  };
+
+  ProfilerTestRunner.HeapEdge = function(targetNode, type, nameOrIndex) {
+    this._targetNode = targetNode;
+    this._type = type;
+    this._nameOrIndex = nameOrIndex;
+  };
+
+  ProfilerTestRunner.HeapEdge.prototype = {
+    _serialize: function(rawSnapshot) {
+      if (!this._targetNode._builder)
+        throw new Error('Inconsistent state of node: ' + this._name + ' no builder assigned');
+
+      var builder = this._targetNode._builder;
+      rawSnapshot.edges.push(builder.lookupEdgeType(this._type));
+      rawSnapshot.edges.push(
+          (typeof this._nameOrIndex === 'string' ? builder.lookupOrAddString(this._nameOrIndex) : this._nameOrIndex));
+      rawSnapshot.edges.push(this._targetNode._ordinal * builder.nodeFieldsCount);
+    }
+  };
+
+  ProfilerTestRunner.HeapEdge.Type = {
+    'context': 'context',
+    'element': 'element',
+    'property': 'property',
+    'internal': 'internal',
+    'hidden': 'hidden',
+    'shortcut': 'shortcut',
+    'weak': 'weak'
+  };
+
+  ProfilerTestRunner.HeapSnapshotBuilder = function() {
+    this._nodes = [];
+    this._string2id = {};
+    this._strings = [];
+    this.nodeFieldsCount = 7;
+    this._nodeTypesMap = {};
+    this._nodeTypesArray = [];
+
+    for (var nodeType in ProfilerTestRunner.HeapNode.Type) {
+      this._nodeTypesMap[nodeType] = this._nodeTypesArray.length;
+      this._nodeTypesArray.push(nodeType);
+    }
+
+    this._edgeTypesMap = {};
+    this._edgeTypesArray = [];
+
+    for (var edgeType in ProfilerTestRunner.HeapEdge.Type) {
+      this._edgeTypesMap[edgeType] = this._edgeTypesArray.length;
+      this._edgeTypesArray.push(edgeType);
+    }
+
+    this.rootNode = new ProfilerTestRunner.HeapNode('root', 0, 'object');
+    this.rootNode._setBuilder(this);
+  };
+
+  ProfilerTestRunner.HeapSnapshotBuilder.prototype = {
+    generateSnapshot: function() {
+      var rawSnapshot = {
+        'snapshot': {
+          'meta': {
+            'node_fields': ['type', 'name', 'id', 'self_size', 'retained_size', 'dominator', 'edge_count'],
+            'node_types': [this._nodeTypesArray, 'string', 'number', 'number', 'number', 'number', 'number'],
+            'edge_fields': ['type', 'name_or_index', 'to_node'],
+            'edge_types': [this._edgeTypesArray, 'string_or_number', 'node']
+          }
+        },
+
+        'nodes': [],
+        'edges': [],
+        'strings': []
+      };
+
+      for (var i = 0; i < this._nodes.length; ++i)
+        this._nodes[i]._serialize(rawSnapshot);
+
+      rawSnapshot.strings = this._strings.slice();
+      var meta = rawSnapshot.snapshot.meta;
+      rawSnapshot.snapshot.edge_count = rawSnapshot.edges.length / meta.edge_fields.length;
+      rawSnapshot.snapshot.node_count = rawSnapshot.nodes.length / meta.node_fields.length;
+      return rawSnapshot;
+    },
+
+    createJSHeapSnapshot: function() {
+      var parsedSnapshot = ProfilerTestRunner._postprocessHeapSnapshotMock(this.generateSnapshot());
+      return new HeapSnapshotWorker.JSHeapSnapshot(parsedSnapshot, new HeapSnapshotWorker.HeapSnapshotProgress());
+    },
+
+    _registerNode: function(node) {
+      this._nodes.push(node);
+      return this._nodes.length - 1;
+    },
+
+    lookupNodeType: function(typeName) {
+      if (typeName === undefined)
+        throw new Error('wrong node type: ' + typeName);
+
+      if (!(typeName in this._nodeTypesMap))
+        throw new Error('wrong node type name: ' + typeName);
+
+      return this._nodeTypesMap[typeName];
+    },
+
+    lookupEdgeType: function(typeName) {
+      if (!(typeName in this._edgeTypesMap))
+        throw new Error('wrong edge type name: ' + typeName);
+
+      return this._edgeTypesMap[typeName];
+    },
+
+    lookupOrAddString: function(string) {
+      if (string in this._string2id)
+        return this._string2id[string];
+
+      this._string2id[string] = this._strings.length;
+      this._strings.push(string);
+      return this._strings.length - 1;
+    }
+  };
+
+  ProfilerTestRunner.createHeapSnapshot = function(instanceCount, firstId) {
+    var seed = 881669;
+
+    function pseudoRandom(limit) {
+      seed = seed * 355109 + 153763 >> 2 & 65535;
+      return seed % limit;
+    }
+
+    var builder = new ProfilerTestRunner.HeapSnapshotBuilder();
+    var rootNode = builder.rootNode;
+    var gcRootsNode = new ProfilerTestRunner.HeapNode('(GC roots)', 0, ProfilerTestRunner.HeapNode.Type.synthetic);
+    rootNode.linkNode(gcRootsNode, ProfilerTestRunner.HeapEdge.Type.element);
+    var windowNode = new ProfilerTestRunner.HeapNode('Window', 20);
+    rootNode.linkNode(windowNode, ProfilerTestRunner.HeapEdge.Type.shortcut);
+    gcRootsNode.linkNode(windowNode, ProfilerTestRunner.HeapEdge.Type.element);
+
+    for (var i = 0; i < instanceCount; ++i) {
+      var sizeOfB = pseudoRandom(20) + 1;
+      var nodeB = new ProfilerTestRunner.HeapNode('B', sizeOfB, undefined, firstId++);
+      windowNode.linkNode(nodeB, ProfilerTestRunner.HeapEdge.Type.element);
+      var sizeOfA = pseudoRandom(50) + 1;
+      var nodeA = new ProfilerTestRunner.HeapNode('A', sizeOfA, undefined, firstId++);
+      nodeB.linkNode(nodeA, ProfilerTestRunner.HeapEdge.Type.property, 'a');
+      nodeA.linkNode(nodeA, ProfilerTestRunner.HeapEdge.Type.property, 'a');
+    }
+
+    return builder.generateSnapshot();
+  };
+};
+
+ProfilerTestRunner.createHeapSnapshotMockFactories();
+
+ProfilerTestRunner.startProfilerTest = function(callback) {
+  TestRunner.addResult('Profiler was enabled.');
+  ProfilerTestRunner._panelReset = TestRunner.override(UI.panels.heap_profiler, '_reset', function() {}, true);
+  TestRunner.addSniffer(Profiler.HeapSnapshotView.prototype, 'show', ProfilerTestRunner._snapshotViewShown, true);
+
+  Profiler.HeapSnapshotContainmentDataGrid.prototype.defaultPopulateCount = function() {
+    return 10;
+  };
+
+  Profiler.HeapSnapshotConstructorsDataGrid.prototype.defaultPopulateCount = function() {
+    return 10;
+  };
+
+  Profiler.HeapSnapshotDiffDataGrid.prototype.defaultPopulateCount = function() {
+    return 5;
+  };
+
+  TestRunner.addResult('Detailed heap profiles were enabled.');
+  TestRunner.safeWrap(callback)();
+};
+
+ProfilerTestRunner.completeProfilerTest = function() {
+  TestRunner.addResult('');
+  TestRunner.addResult('Profiler was disabled.');
+  TestRunner.completeTest();
+};
+
+ProfilerTestRunner.runHeapSnapshotTestSuite = function(testSuite) {
+  var testSuiteTests = testSuite.slice();
+  var completeTestStack;
+
+  function runner() {
+    if (!testSuiteTests.length) {
+      if (completeTestStack)
+        TestRunner.addResult('FAIL: test already completed at ' + completeTestStack);
+
+      ProfilerTestRunner.completeProfilerTest();
+      completeTestStack = new Error().stack;
+      return;
+    }
+
+    var nextTest = testSuiteTests.shift();
+    TestRunner.addResult('');
+    TestRunner.addResult(
+        'Running: ' +
+        /function\s([^(]*)/.exec(nextTest)[1]);
+    ProfilerTestRunner._panelReset.call(UI.panels.heap_profiler);
+    TestRunner.safeWrap(nextTest)(runner, runner);
+  }
+
+  ProfilerTestRunner.startProfilerTest(runner);
+};
+
+ProfilerTestRunner.assertColumnContentsEqual = function(reference, actual) {
+  var length = Math.min(reference.length, actual.length);
+
+  for (var i = 0; i < length; ++i)
+    TestRunner.assertEquals(reference[i], actual[i], 'row ' + i);
+
+  if (reference.length > length)
+    TestRunner.addResult('extra rows in reference array:\n' + reference.slice(length).join('\n'));
+  else if (actual.length > length)
+    TestRunner.addResult('extra rows in actual array:\n' + actual.slice(length).join('\n'));
+};
+
+ProfilerTestRunner.checkArrayIsSorted = function(contents, sortType, sortOrder) {
+  function simpleComparator(a, b) {
+    return (a < b ? -1 : (a > b ? 1 : 0));
+  }
+
+  function parseSize(size) {
+    return parseInt(size.replace(/[\xa0,]/g, ''), 10);
+  }
+
+  var extractor = {
+    text: function(data) {
+      data;
+    },
+
+    number: function(data) {
+      return parseInt(data, 10);
+    },
+
+    size: parseSize,
+
+    name: function(data) {
+      return data;
+    },
+
+    id: function(data) {
+      return parseInt(data, 10);
+    }
+  }[sortType];
+
+  if (!extractor) {
+    TestRunner.addResult('Invalid sort type: ' + sortType);
+    return;
+  }
+
+  var acceptableComparisonResult;
+
+  if (sortOrder === DataGrid.DataGrid.Order.Ascending) {
+    acceptableComparisonResult = -1;
+  } else if (sortOrder === DataGrid.DataGrid.Order.Descending) {
+    acceptableComparisonResult = 1;
+  } else {
+    TestRunner.addResult('Invalid sort order: ' + sortOrder);
+    return;
+  }
+
+  for (var i = 0; i < contents.length - 1; ++i) {
+    var a = extractor(contents[i]);
+    var b = extractor(contents[i + 1]);
+    var result = simpleComparator(a, b);
+
+    if (result !== 0 && result !== acceptableComparisonResult) {
+      TestRunner.addResult(
+          'Elements ' + i + ' and ' + (i + 1) + ' are out of order: ' + a + ' ' + b + ' (' + sortOrder + ')');
+    }
+  }
+};
+
+ProfilerTestRunner.clickColumn = function(column, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var cell = this._currentGrid()._headerTableHeaders[column.id];
+
+  var event = {
+    target: {
+      enclosingNodeOrSelfWithNodeName: function() {
+        return cell;
+      }
+    }
+  };
+
+  function sortingComplete() {
+    ProfilerTestRunner._currentGrid().removeEventListener(
+        Profiler.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, this);
+    TestRunner.assertEquals(column.id, this._currentGrid().sortColumnId(), 'unexpected sorting');
+    column.sort = this._currentGrid().sortOrder();
+
+    function callCallback() {
+      callback(column);
+    }
+
+    setTimeout(callCallback, 0);
+  }
+
+  ProfilerTestRunner._currentGrid().addEventListener(
+      Profiler.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, this);
+  this._currentGrid()._clickInHeaderCell(event);
+};
+
+ProfilerTestRunner.clickRowAndGetRetainers = function(row, callback) {
+  callback = TestRunner.safeWrap(callback);
+
+  var event = {
+    target: {
+      enclosingNodeOrSelfWithNodeName: function() {
+        return row._element;
+      },
+
+      selectedNode: row
+    }
+  };
+
+  this._currentGrid()._mouseDownInDataTable(event);
+  var rootNode = ProfilerTestRunner.currentProfileView()._retainmentDataGrid.rootNode();
+  rootNode.once(Profiler.HeapSnapshotGridNode.Events.PopulateComplete).then(() => callback(rootNode));
+};
+
+ProfilerTestRunner.clickShowMoreButton = function(buttonName, row, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var parent = row.parent;
+  parent.once(Profiler.HeapSnapshotGridNode.Events.PopulateComplete).then(() => setTimeout(() => callback(parent), 0));
+  row[buttonName].click();
+};
+
+ProfilerTestRunner.columnContents = function(column, row) {
+  this._currentGrid().updateVisibleNodes();
+  var columnOrdinal = ProfilerTestRunner.viewColumns().indexOf(column);
+  var result = [];
+  var parent = row || this._currentGrid().rootNode();
+
+  for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
+    if (!node.selectable)
+      continue;
+
+    var content = node.element().children[columnOrdinal];
+
+    if (content.firstElementChild)
+      content = content.firstElementChild;
+
+    result.push(content.textContent);
+  }
+
+  return result;
+};
+
+ProfilerTestRunner.countDataRows = function(row, filter) {
+  var result = 0;
+
+  filter = filter || function(node) {
+    return node.selectable;
+  };
+
+  for (var node = row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
+    if (filter(node))
+      ++result;
+  }
+
+  return result;
+};
+
+ProfilerTestRunner.expandRow = function(row, callback) {
+  callback = TestRunner.safeWrap(callback);
+  row.once(Profiler.HeapSnapshotGridNode.Events.PopulateComplete).then(() => setTimeout(() => callback(row), 0));
+
+  (function expand() {
+    if (row.hasChildren())
+      row.expand();
+    else
+      setTimeout(expand, 0);
+  })();
+};
+
+ProfilerTestRunner.findAndExpandGCRoots = function(callback) {
+  ProfilerTestRunner.findAndExpandRow('(GC roots)', callback);
+};
+
+ProfilerTestRunner.findAndExpandWindow = function(callback) {
+  ProfilerTestRunner.findAndExpandRow('Window', callback);
+};
+
+ProfilerTestRunner.findAndExpandRow = function(name, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var row = ProfilerTestRunner.findRow(name);
+  TestRunner.assertEquals(true, !!row, '"' + name + '" row');
+  ProfilerTestRunner.expandRow(row, callback);
+};
+
+ProfilerTestRunner.findButtonsNode = function(row, startNode) {
+  for (var node = startNode || row.children[0]; node; node = node.traverseNextNode(true, row, true)) {
+    if (!node.selectable && node.showNext)
+      return node;
+  }
+
+  return null;
+};
+
+ProfilerTestRunner.findRow = function(name, parent) {
+  function matcher(x) {
+    return x._name === name;
+  }
+
+  return ProfilerTestRunner.findMatchingRow(matcher, parent);
+};
+
+ProfilerTestRunner.findMatchingRow = function(matcher, parent) {
+  parent = parent || this._currentGrid().rootNode();
+
+  for (var node = parent.children[0]; node; node = node.traverseNextNode(true, parent, true)) {
+    if (matcher(node))
+      return node;
+  }
+
+  return null;
+};
+
+ProfilerTestRunner.switchToView = function(title, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var view = UI.panels.heap_profiler.visibleView;
+  view._changePerspectiveAndWait(title).then(callback);
+  ProfilerTestRunner._currentGrid().scrollContainer.style.height = '10000px';
+};
+
+ProfilerTestRunner.takeAndOpenSnapshot = function(generator, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var snapshot = generator();
+  var profileType = Profiler.ProfileTypeRegistry.instance.heapSnapshotProfileType;
+
+  function pushGeneratedSnapshot(reportProgress) {
+    if (reportProgress) {
+      profileType._reportHeapSnapshotProgress({data: {done: 50, total: 100, finished: false}});
+
+      profileType._reportHeapSnapshotProgress({data: {done: 100, total: 100, finished: true}});
+    }
+
+    snapshot.snapshot.typeId = 'HEAP';
+
+    profileType._addHeapSnapshotChunk({data: JSON.stringify(snapshot)});
+
+    return Promise.resolve();
+  }
+
+  TestRunner.override(TestRunner.HeapProfilerAgent, 'takeHeapSnapshot', pushGeneratedSnapshot);
+  ProfilerTestRunner._takeAndOpenSnapshotCallback = callback;
+  profileType._takeHeapSnapshot();
+};
+
+ProfilerTestRunner.viewColumns = function() {
+  return ProfilerTestRunner._currentGrid()._columnsArray;
+};
+
+ProfilerTestRunner.currentProfileView = function() {
+  return UI.panels.heap_profiler.visibleView;
+};
+
+ProfilerTestRunner._currentGrid = function() {
+  return this.currentProfileView()._dataGrid;
+};
+
+ProfilerTestRunner._snapshotViewShown = function() {
+  if (ProfilerTestRunner._takeAndOpenSnapshotCallback) {
+    var callback = ProfilerTestRunner._takeAndOpenSnapshotCallback;
+    ProfilerTestRunner._takeAndOpenSnapshotCallback = null;
+    var dataGrid = this._dataGrid;
+
+    function sortingComplete() {
+      dataGrid.removeEventListener(Profiler.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, null);
+      callback();
+    }
+
+    dataGrid.addEventListener(Profiler.HeapSnapshotSortableDataGrid.Events.SortingComplete, sortingComplete, null);
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/ProfilerTestRunner.js b/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/ProfilerTestRunner.js
new file mode 100644
index 0000000..2200015
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/ProfilerTestRunner.js
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+ProfilerTestRunner.startProfilerTest = function(callback) {
+  TestRunner.addResult('Profiler was enabled.');
+  TestRunner.addSniffer(UI.panels.js_profiler, '_addProfileHeader', ProfilerTestRunner._profileHeaderAdded, true);
+  TestRunner.addSniffer(Profiler.ProfileView.prototype, 'refresh', ProfilerTestRunner._profileViewRefresh, true);
+  TestRunner.safeWrap(callback)();
+};
+
+ProfilerTestRunner.completeProfilerTest = function() {
+  TestRunner.addResult('');
+  TestRunner.addResult('Profiler was disabled.');
+  TestRunner.completeTest();
+};
+
+ProfilerTestRunner.runProfilerTestSuite = function(testSuite) {
+  var testSuiteTests = testSuite.slice();
+
+  function runner() {
+    if (!testSuiteTests.length) {
+      ProfilerTestRunner.completeProfilerTest();
+      return;
+    }
+
+    var nextTest = testSuiteTests.shift();
+    TestRunner.addResult('');
+    TestRunner.addResult(
+        'Running: ' +
+        /function\s([^(]*)/.exec(nextTest)[1]);
+    TestRunner.safeWrap(nextTest)(runner, runner);
+  }
+
+  ProfilerTestRunner.startProfilerTest(runner);
+};
+
+ProfilerTestRunner.showProfileWhenAdded = function(title) {
+  ProfilerTestRunner._showProfileWhenAdded = title;
+};
+
+ProfilerTestRunner._profileHeaderAdded = function(profile) {
+  if (ProfilerTestRunner._showProfileWhenAdded === profile.title)
+    UI.panels.js_profiler.showProfile(profile);
+};
+
+ProfilerTestRunner.waitUntilProfileViewIsShown = function(title, callback) {
+  callback = TestRunner.safeWrap(callback);
+  var profilesPanel = UI.panels.js_profiler;
+
+  if (profilesPanel.visibleView && profilesPanel.visibleView.profile &&
+      profilesPanel.visibleView._profileHeader.title === title)
+    callback(profilesPanel.visibleView);
+  else
+    ProfilerTestRunner._waitUntilProfileViewIsShownCallback = {title: title, callback: callback};
+
+};
+
+ProfilerTestRunner._profileViewRefresh = function() {
+  if (ProfilerTestRunner._waitUntilProfileViewIsShownCallback &&
+      ProfilerTestRunner._waitUntilProfileViewIsShownCallback.title === this._profileHeader.title) {
+    var callback = ProfilerTestRunner._waitUntilProfileViewIsShownCallback;
+    delete ProfilerTestRunner._waitUntilProfileViewIsShownCallback;
+    callback.callback(this);
+  }
+};
+
+ProfilerTestRunner.startSamplingHeapProfiler = function() {
+  Profiler.SamplingHeapProfileType.instance.startRecordingProfile();
+};
+
+ProfilerTestRunner.stopSamplingHeapProfiler = function() {
+  Profiler.SamplingHeapProfileType.instance.stopRecordingProfile();
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/module.json
new file mode 100644
index 0000000..1ea4ac0
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/profiler_test_runner/module.json
@@ -0,0 +1,17 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "profiler",
+    "data_grid",
+    "heap_snapshot_worker"
+  ],
+  "scripts": [
+    "HeapSnapshotTestRunner.js",
+    "ProfilerTestRunner.js"
+  ],
+  "skip_compilation": [
+    "HeapSnapshotTestRunner.js",
+    "ProfilerTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js b/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js
index 0a3fae67..2bf85cc8 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/DOMStorageItemsView.js
@@ -36,8 +36,8 @@
     this.element.classList.add('storage-view', 'table');
 
     var columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([
-      {id: 'key', title: Common.UIString('Key'), sortable: false, editable: true, weight: 50},
-      {id: 'value', title: Common.UIString('Value'), sortable: false, editable: true, weight: 50}
+      {id: 'key', title: Common.UIString('Key'), sortable: false, editable: true, longText: true, weight: 50},
+      {id: 'value', title: Common.UIString('Value'), sortable: false, editable: true, longText: true, weight: 50}
     ]);
     this._dataGrid = new DataGrid.DataGrid(columns, this._editingCallback.bind(this), this._deleteCallback.bind(this));
     this._dataGrid.setStriped(true);
diff --git a/third_party/WebKit/Source/devtools/front_end/sass_test_runner/SASSTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sass_test_runner/SASSTestRunner.js
new file mode 100644
index 0000000..9e8d661
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sass_test_runner/SASSTestRunner.js
@@ -0,0 +1,331 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+var sassSourceMapFactory = null;
+
+SASSTestRunner.sassSourceMapFactory = function() {
+  if (!sassSourceMapFactory)
+    sassSourceMapFactory = new Sass.SASSSourceMapFactory();
+
+  return sassSourceMapFactory;
+};
+
+SASSTestRunner.parseSCSS = function(url, text) {
+  return Sass.SASSSupport.parseSCSS(url, text);
+};
+
+SASSTestRunner.parseCSS = SASSTestRunner.parseSCSS;
+
+SASSTestRunner.loadASTMapping = function(header, callback) {
+  var sourceMapManager = header.cssModel().sourceMapManager();
+  var sourceMap = sourceMapManager.sourceMapForClient(header);
+
+  if (sourceMap) {
+    callback((sourceMap.editable() ? sourceMap : null));
+    return;
+  }
+
+  sourceMapManager.addEventListener(SDK.SourceMapManager.Events.SourceMapAttached, onAttached);
+
+  function onAttached(event) {
+    if (event.data.client !== header)
+      return;
+
+    sourceMapManager.removeEventListener(SDK.SourceMapManager.Events.SourceMapAttached, onAttached);
+    var sourceMap = event.data.sourceMap;
+    callback((sourceMap.editable() ? sourceMap : null));
+  }
+};
+
+SASSTestRunner.dumpAST = function(ast) {
+  var lines = [String.sprintf('=== AST === %s', ast.document.url)];
+
+  for (var i = 0; i < ast.rules.length; ++i) {
+    var rule = ast.rules[i];
+    lines.push(String.sprintf('rule %d', i));
+    var ruleLines = dumpRule(rule);
+    lines = lines.concat(indent(ruleLines));
+  }
+
+  lines.push('======');
+  TestRunner.addResult(lines.join('\n'));
+  return ast;
+
+  function dumpRule(rule) {
+    var lines = [];
+
+    for (var i = 0; i < rule.selectors.length; ++i) {
+      var selector = rule.selectors[i];
+      lines.push(`selector ${i}: "${selector.text}"`);
+      var selectorLines = dumpTextNode(selector);
+      lines = lines.concat(indent(selectorLines));
+    }
+
+    for (var i = 0; i < rule.properties.length; ++i) {
+      var property = rule.properties[i];
+      lines.push('property ' + i);
+      var propertyLines = dumpProperty(property);
+      lines = lines.concat(indent(propertyLines));
+    }
+
+    return lines;
+  }
+
+  function dumpProperty(property) {
+    var lines = [];
+    lines.push(String.sprintf('name: "%s"', property.name.text));
+    lines = lines.concat(indent(dumpTextNode(property.name)));
+    lines.push(String.sprintf('value: "%s"', property.value.text));
+    lines = lines.concat(indent(dumpTextNode(property.value)));
+    lines.push(String.sprintf('range: %s', property.range.toString()));
+    lines.push(String.sprintf('disabled: %s', property.disabled));
+    return lines;
+  }
+
+  function dumpTextNode(textNode) {
+    return [String.sprintf('range: %s', textNode.range.toString())];
+  }
+};
+
+function indent(lines) {
+  return lines.map(line => '    ' + line);
+}
+
+SASSTestRunner.dumpASTDiff = function(diff) {
+  TestRunner.addResult('=== Diff ===');
+  var changesPerRule = new Map();
+
+  for (var change of diff.changes) {
+    var oldRule = change.oldRule;
+    var ruleChanges = changesPerRule.get(oldRule);
+
+    if (!ruleChanges) {
+      ruleChanges = [];
+      changesPerRule.set(oldRule, ruleChanges);
+    }
+
+    ruleChanges.push(change);
+  }
+
+  var T = Sass.SASSSupport.PropertyChangeType;
+
+  for (var rule of changesPerRule.keys()) {
+    var changes = changesPerRule.get(rule);
+    var names = [];
+    var values = [];
+
+    for (var property of rule.properties) {
+      names.push(str(property.name, '    '));
+      values.push(str(property.value));
+    }
+
+    for (var i = changes.length - 1; i >= 0; --i) {
+      var change = changes[i];
+      var newProperty = change.newRule.properties[change.newPropertyIndex];
+      var oldProperty = change.oldRule.properties[change.oldPropertyIndex];
+
+      switch (change.type) {
+        case T.PropertyAdded:
+          names.splice(change.oldPropertyIndex, 0, str(newProperty.name, '[+] '));
+          values.splice(change.oldPropertyIndex, 0, str(newProperty.value));
+          break;
+        case T.PropertyRemoved:
+          names[change.oldPropertyIndex] = str(oldProperty.name, '[-] ');
+          break;
+        case T.PropertyToggled:
+          names[change.oldPropertyIndex] = str(oldProperty.name, '[T] ');
+          break;
+        case T.NameChanged:
+          names[change.oldPropertyIndex] = str(oldProperty.name, '[M] ');
+          break;
+        case T.ValueChanged:
+          values[change.oldPropertyIndex] = str(oldProperty.value, '[M] ');
+          break;
+      }
+    }
+
+    var selectorText = rule.selectors.map(selector => selector.text).join(',');
+    TestRunner.addResult('Changes for rule: ' + selectorText);
+    names = indent(names);
+
+    for (var i = 0; i < names.length; ++i)
+      TestRunner.addResult(names[i] + ': ' + values[i]);
+  }
+
+  function str(node, prefix) {
+    prefix = prefix || '';
+    return prefix + node.text.trim();
+  }
+};
+
+SASSTestRunner.validateASTRanges = function(ast) {
+  var invalidNodes = [];
+
+  for (var rule of ast.rules) {
+    for (var property of rule.properties) {
+      validate(property.name);
+      validate(property.value);
+    }
+  }
+
+  if (invalidNodes.length) {
+    TestRunner.addResult('Bad ranges: ' + invalidNodes.length);
+
+    for (var node of invalidNodes)
+      TestRunner.addResult(String.sprintf('  - range: %s text: %s', node.range.toString(), node.text));
+  } else {
+    TestRunner.addResult('Ranges OK.');
+  }
+
+  return ast;
+
+  function validate(textNode) {
+    if (textNode.document.text.extract(textNode.range) !== textNode.text)
+      invalidNodes.push(textNode);
+  }
+};
+
+SASSTestRunner.validateMapping = function(mapping) {
+  TestRunner.addResult('Mapped CSS: ' + mapping._compiledToSource.size);
+  TestRunner.addResult('Mapped SCSS: ' + mapping._sourceToCompiled.size);
+  var cssNodes = mapping._compiledToSource.keysArray();
+  var staleCSS = 0;
+  var staleSASS = 0;
+
+  for (var i = 0; i < cssNodes.length; ++i) {
+    var cssNode = cssNodes[i];
+    staleCSS += (cssNode.document !== mapping.compiledModel().document ? 1 : 0);
+    var sassNode = mapping.toSourceNode(cssNode);
+    var sassAST = mapping.sourceModels().get(sassNode.document.url);
+    staleSASS += (sassNode.document !== sassAST.document ? 1 : 0);
+  }
+
+  if (staleCSS || staleSASS) {
+    TestRunner.addResult('ERROR: found stale entries');
+    TestRunner.addResult('   -stale CSS: ' + staleCSS);
+    TestRunner.addResult('   -stale SASS: ' + staleSASS);
+  } else {
+    TestRunner.addResult('No stale entries found.');
+  }
+};
+
+SASSTestRunner.updateCSSText = function(url, newText) {
+  var styleSheetIds = TestRunner.cssModel.styleSheetIdsForURL(url);
+  var promises = styleSheetIds.map(id => TestRunner.cssModel.setStyleSheetText(id, newText, true));
+  return Promise.all(promises);
+};
+
+SASSTestRunner.updateSASSText = function(url, newText) {
+  var uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url);
+  uiSourceCode.addRevision(newText);
+};
+
+SASSTestRunner.runCSSEditTests = function(header, tests) {
+  var astSourceMap;
+  SASSTestRunner.loadASTMapping(header, onMapping);
+
+  function onMapping(map) {
+    astSourceMap = map;
+    TestRunner.addResult('INITIAL MODELS');
+    logASTText(map.compiledModel(), true);
+
+    for (var ast of map.sourceModels().values())
+      logASTText(ast, true);
+
+    runTests();
+  }
+
+  function runTests() {
+    if (!tests.length) {
+      TestRunner.completeTest();
+      return;
+    }
+
+    var test = tests.shift();
+    logTestName(test.name);
+    var text = astSourceMap.compiledModel().document.text.value();
+    var edits = test(text);
+    logSourceEdits(text, edits);
+    var ranges = edits.map(edit => edit.oldRange);
+    var texts = edits.map(edit => edit.newText);
+    astSourceMap.editCompiled(ranges, texts).then(onEditsDone);
+  }
+
+  function onEditsDone(result) {
+    if (!result.map) {
+      TestRunner.addResult('SASSProcessor failed to process edits.');
+      runTests();
+      return;
+    }
+
+    logASTText(result.map.compiledModel());
+
+    for (var sassURL of result.newSources.keys()) {
+      var ast = result.map.sourceModels().get(sassURL);
+      logASTText(ast);
+    }
+
+    runTests();
+  }
+
+  function logASTText(ast, avoidIndent, customTitle) {
+    customTitle = customTitle || ast.document.url.split('/').pop();
+    TestRunner.addResult('===== ' + customTitle + ' =====');
+    var text = ast.document.text.value().replace(/ /g, '.');
+    var lines = text.split('\n');
+
+    if (!avoidIndent)
+      lines = indent(lines);
+
+    TestRunner.addResult(lines.join('\n'));
+  }
+
+  function logTestName(testName) {
+    var titleText = ' TEST: ' + testName + ' ';
+    var totalLength = 80;
+    var prefixLength = (totalLength - titleText.length) / 2 | 0;
+    var suffixLength = totalLength - titleText.length - prefixLength;
+    var prefix = new Array(prefixLength).join('-');
+    var suffix = new Array(suffixLength).join('-');
+    TestRunner.addResult('\n' + prefix + titleText + suffix + '\n');
+  }
+
+  function logSourceEdits(text, edits) {
+    var lines = [];
+
+    for (var i = 0; i < edits.length; ++i) {
+      var edit = edits[i];
+      var range = edit.oldRange;
+      var line = String.sprintf('{%d, %d, %d, %d}', range.startLine, range.startColumn, range.endLine, range.endColumn);
+      line += String.sprintf(' \'%s\' => \'%s\'', new TextUtils.Text(text).extract(range), edit.newText);
+      lines.push(line);
+    }
+
+    lines = indent(lines);
+    lines.unshift('Edits:');
+    TestRunner.addResult(lines.join('\n'));
+  }
+};
+
+SASSTestRunner.createEdit = function(source, pattern, newText, matchNumber) {
+  matchNumber = matchNumber || 0;
+  var re = new RegExp(pattern.escapeForRegExp(), 'g');
+  var match;
+
+  while ((match = re.exec(source)) !== null && matchNumber)
+    --matchNumber;
+
+
+  if (!match)
+    return null;
+
+  var sourceRange = new TextUtils.SourceRange(match.index, match[0].length);
+  var textRange = new TextUtils.Text(source).toTextRange(sourceRange);
+  return new TextUtils.SourceEdit('', textRange, newText);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/sass_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/sass_test_runner/module.json
new file mode 100644
index 0000000..3aace150
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sass_test_runner/module.json
@@ -0,0 +1,13 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "sass"
+  ],
+  "scripts": [
+    "SASSTestRunner.js"
+  ],
+  "skip_compilation": [
+    "SASSTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js b/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js
index 411c70d..a90a173 100644
--- a/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/security/SecurityModel.js
@@ -58,10 +58,6 @@
 
     return aScore - bScore;
   }
-
-  showCertificateViewer() {
-    this._securityAgent.showCertificateViewer();
-  }
 };
 
 SDK.SDKModel.register(Security.SecurityModel, SDK.Target.Capability.Security, false);
diff --git a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
index 3407caa..1cb04de 100644
--- a/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js
@@ -38,37 +38,27 @@
 
   /**
    * @param {string} text
-   * @param {!Security.SecurityPanel} panel
+   * @param {string} origin
    * @return {!Element}
    */
-  static createCertificateViewerButton(text, panel) {
-    /**
-     * @param {!Event} e
-     */
-    function showCertificateViewer(e) {
+  static createCertificateViewerButtonForOrigin(text, origin) {
+    return UI.createTextButton(text, async e => {
       e.consume();
-      panel.showCertificateViewer();
-    }
-
-    return UI.createTextButton(text, showCertificateViewer, 'security-certificate-button');
+      var names = await SDK.multitargetNetworkManager.getCertificate(origin);
+      InspectorFrontendHost.showCertificateViewer(names);
+    }, 'security-certificate-button');
   }
 
   /**
    * @param {string} text
-   * @param {string} origin
+   * @param {!Array<string>} names
    * @return {!Element}
    */
-  static createCertificateViewerButton2(text, origin) {
-    /**
-     * @param {!Event} e
-     */
-    function showCertificateViewer(e) {
+  static createCertificateViewerButtonForCert(text, names) {
+    return UI.createTextButton(text, e => {
       e.consume();
-      SDK.multitargetNetworkManager.getCertificate(origin).then(
-          names => InspectorFrontendHost.showCertificateViewer(names));
-    }
-
-    return UI.createTextButton(text, showCertificateViewer, 'security-certificate-button');
+      InspectorFrontendHost.showCertificateViewer(names);
+    }, 'security-certificate-button');
   }
 
   /**
@@ -258,10 +248,6 @@
     return this._filterRequestCounts.get(filterKey) || 0;
   }
 
-  showCertificateViewer() {
-    this._securityModel.showCertificateViewer();
-  }
-
   /**
    * @param {!Protocol.Security.SecurityState} stateA
    * @param {!Protocol.Security.SecurityState} stateB
@@ -614,11 +600,10 @@
     text.createChild('div', 'security-explanation-title').textContent = explanation.summary;
     text.createChild('div').textContent = explanation.description;
 
-    if (explanation.hasCertificate) {
-      text.appendChild(
-          Security.SecurityPanel.createCertificateViewerButton(Common.UIString('View certificate'), this._panel));
+    if (explanation.certificate.length) {
+      text.appendChild(Security.SecurityPanel.createCertificateViewerButtonForCert(
+          Common.UIString('View certificate'), explanation.certificate));
     }
-
     return text;
   }
 
@@ -682,10 +667,11 @@
 
     if (this._panel.filterRequestCount(Network.NetworkLogView.MixedContentFilterValues.Blocked) > 0) {
       var explanation = /** @type {!Protocol.Security.SecurityStateExplanation} */ ({
-        'securityState': Protocol.Security.SecurityState.Info,
-        'summary': Common.UIString('Blocked mixed content'),
-        'description': Common.UIString('Your page requested non-secure resources that were blocked.'),
-        'mixedContentType': Protocol.Security.MixedContentType.Blockable
+        securityState: Protocol.Security.SecurityState.Info,
+        summary: Common.UIString('Blocked mixed content'),
+        description: Common.UIString('Your page requested non-secure resources that were blocked.'),
+        mixedContentType: Protocol.Security.MixedContentType.Blockable,
+        certificate: []
       });
       this._addMixedContentExplanation(
           this._securityExplanationsMain, explanation, Network.NetworkLogView.MixedContentFilterValues.Blocked);
@@ -805,9 +791,11 @@
       table.addRow(Common.UIString('Valid from'), validFromString);
       table.addRow(Common.UIString('Valid until'), validUntilString);
       table.addRow(Common.UIString('Issuer'), originState.securityDetails.issuer);
+
       table.addRow(
-          '', Security.SecurityPanel.createCertificateViewerButton2(
-                  Common.UIString('Open full certificate details'), origin));
+          '',
+          Security.SecurityPanel.createCertificateViewerButtonForOrigin(
+              Common.UIString('Open full certificate details'), origin));
 
       if (!originState.securityDetails.signedCertificateTimestampList.length)
         return;
diff --git a/third_party/WebKit/Source/devtools/front_end/security_test_runner/SecurityTestRunner.js b/third_party/WebKit/Source/devtools/front_end/security_test_runner/SecurityTestRunner.js
new file mode 100644
index 0000000..b50fa77
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/security_test_runner/SecurityTestRunner.js
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+SecurityTestRunner.dumpSecurityPanelSidebarOrigins = function() {
+  for (var key in Security.SecurityPanelSidebarTree.OriginGroupName) {
+    var originGroupName = Security.SecurityPanelSidebarTree.OriginGroupName[key];
+    var originGroup = Security.SecurityPanel._instance()._sidebarTree._originGroups.get(originGroupName);
+
+    if (originGroup.hidden)
+      continue;
+
+    TestRunner.addResult('Group: ' + originGroupName);
+    var originTitles = originGroup.childrenListElement.getElementsByClassName('title');
+
+    for (var originTitle of originTitles)
+      TestRunner.dumpDeepInnerHTML(originTitle);
+  }
+};
+
+SecurityTestRunner.dispatchRequestFinished = function(request) {
+  TestRunner.networkManager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, request);
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/security_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/security_test_runner/module.json
new file mode 100644
index 0000000..20d36679
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/security_test_runner/module.json
@@ -0,0 +1,13 @@
+{
+  "dependencies": [
+    "test_runner",
+    "integration_test_runner",
+    "security"
+  ],
+  "scripts": [
+    "SecurityTestRunner.js"
+  ],
+  "skip_compilation": [
+    "SecurityTestRunner.js"
+  ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/AutocompleteTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/AutocompleteTestRunner.js
new file mode 100644
index 0000000..8a71653
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/AutocompleteTestRunner.js
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+SourcesTestRunner.dumpSuggestions = function(textEditor, lines) {
+  var resolve;
+  var promise = new Promise(fulfill => resolve = fulfill);
+  var lineNumber = -1, columnNumber;
+
+  for (var i = 0; i < lines.length; ++i) {
+    var columnNumber = lines[i].indexOf('|');
+
+    if (columnNumber !== -1) {
+      lineNumber = i;
+      break;
+    }
+  }
+
+  if (lineNumber === -1)
+    throw new Error('Test case is invalid: cursor position is not marked with \'|\' symbol.');
+
+  textEditor.setText(lines.join('\n').replace('|', ''));
+  textEditor.setSelection(TextUtils.TextRange.createFromLocation(lineNumber, columnNumber));
+  TestRunner.addSniffer(
+      TextEditor.TextEditorAutocompleteController.prototype, '_onSuggestionsShownForTest', suggestionsShown);
+  textEditor._autocompleteController.autocomplete();
+
+  function suggestionsShown(words) {
+    TestRunner.addResult('========= Selection In Editor =========');
+    SourcesTestRunner.dumpTextWithSelection(textEditor);
+    TestRunner.addResult('======= Autocomplete Suggestions =======');
+    TestRunner.addResult('[' + words.map(item => item.text).join(', ') + ']');
+    resolve();
+  }
+
+  return promise;
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/BreakpointManagerTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/BreakpointManagerTestRunner.js
new file mode 100644
index 0000000..88aec2c
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/BreakpointManagerTestRunner.js
@@ -0,0 +1,430 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+SourcesTestRunner.createWorkspace = function() {
+  SourcesTestRunner.testTargetManager = new SDK.TargetManager();
+  SourcesTestRunner.testWorkspace = new Workspace.Workspace();
+  SourcesTestRunner.testNetworkProjectManager =
+      new Bindings.NetworkProjectManager(SourcesTestRunner.testTargetManager, SourcesTestRunner.testWorkspace);
+  SourcesTestRunner.testResourceMapping =
+      new Bindings.ResourceMapping(SourcesTestRunner.testTargetManager, SourcesTestRunner.testWorkspace);
+  SourcesTestRunner.testDebuggerWorkspaceBinding =
+      new Bindings.DebuggerWorkspaceBinding(SourcesTestRunner.testTargetManager, SourcesTestRunner.testWorkspace);
+  Bindings.resourceMapping = SourcesTestRunner.testResourceMapping;
+};
+
+function resourceMappingModelInfoForTarget(target) {
+  var resourceTreeModel = target.model(SDK.ResourceTreeModel);
+  var binding = (resourceTreeModel ? SourcesTestRunner.testResourceMapping._modelToInfo.get(resourceTreeModel) : null);
+  return binding;
+}
+
+SourcesTestRunner.createMockTarget = function(id) {
+  var capabilities = SDK.Target.Capability.AllForTests;
+
+  var target = SourcesTestRunner.testTargetManager.createTarget(
+      'mock-target-id-' + id, 'mock-target-' + id, capabilities & ~SDK.Target.Capability.JS,
+      params => new SDK.StubConnection(params), null);
+
+  SourcesTestRunner.testNetworkProject = Bindings.NetworkProject.forTarget(target);
+  SourcesTestRunner.testResourceMappingModelInfo = resourceMappingModelInfoForTarget(target);
+  target._capabilitiesMask = capabilities;
+  target._inspectedURL = TestRunner.mainTarget.inspectedURL();
+  target.resourceTreeModel = target.model(SDK.ResourceTreeModel);
+  target.resourceTreeModel._cachedResourcesProcessed = true;
+  target.resourceTreeModel._frameAttached('42', 0);
+  target.runtimeModel = target.model(SDK.RuntimeModel);
+  target.debuggerModel = new SourcesTestRunner.DebuggerModelMock(target);
+  target._modelByConstructor.set(SDK.DebuggerModel, target.debuggerModel);
+  SourcesTestRunner.testTargetManager.modelAdded(target, SDK.DebuggerModel, target.debuggerModel);
+  return target;
+};
+
+SourcesTestRunner.uiSourceCodes = {};
+
+SourcesTestRunner.initializeDefaultMappingOnTarget = function(target) {
+  var defaultMapping = {
+    rawLocationToUILocation: function(rawLocation) {
+      return null;
+    },
+
+    uiLocationToRawLocation: function(uiSourceCode, lineNumber) {
+      if (!SourcesTestRunner.uiSourceCodes[uiSourceCode.url()])
+        return null;
+
+      return new SDK.DebuggerModel.Location(target.debuggerModel, uiSourceCode.url(), lineNumber, 0);
+    },
+
+    isIdentity: function() {
+      return true;
+    }
+  };
+
+  target.defaultMapping = defaultMapping;
+};
+
+SourcesTestRunner.DebuggerModelMock = class extends SDK.SDKModel {
+  sourceMapManager() {
+    return this._sourceMapManager;
+  }
+
+  constructor(target) {
+    super(target);
+    this._sourceMapManager = new SDK.SourceMapManager();
+    this._target = target;
+    this._breakpointResolvedEventTarget = new Common.Object();
+    this._scripts = {};
+    this._breakpoints = {};
+    this._debuggerWorkspaceBinding = SourcesTestRunner.testDebuggerWorkspaceBinding;
+  }
+
+  target() {
+    return this._target;
+  }
+
+  runtimeModel() {
+    return this._target.runtimeModel;
+  }
+
+  setBeforePausedCallback(callback) {
+  }
+
+  debuggerEnabled() {
+    return true;
+  }
+
+  scriptsForSourceURL(url) {
+    var script = this._scriptForURL(url);
+    return (script ? [script] : []);
+  }
+
+  _addScript(scriptId, url) {
+    var script = new SDK.Script(this, scriptId, url);
+    this._scripts[scriptId] = script;
+    var modelData = this._debuggerWorkspaceBinding._debuggerModelToData.get(this);
+
+    modelData._defaultMapping._parsedScriptSource({data: script});
+
+    modelData._resourceMapping._parsedScriptSource({data: script});
+  }
+
+  _scriptForURL(url) {
+    for (var scriptId in this._scripts) {
+      var script = this._scripts[scriptId];
+
+      if (script.sourceURL === url)
+        return script;
+    }
+  }
+
+  _scheduleSetBeakpointCallback(callback, breakpointId, locations) {
+    setTimeout(innerCallback.bind(this), 0);
+
+    function innerCallback() {
+      if (callback)
+        callback(breakpointId, locations);
+
+      if (window.setBreakpointCallback) {
+        var savedCallback = window.setBreakpointCallback;
+        delete window.setBreakpointCallback;
+        savedCallback();
+      }
+    }
+  }
+
+  createRawLocation(script, line, column) {
+    return new SDK.DebuggerModel.Location(this, script.scriptId, line, column);
+  }
+
+  createRawLocationByURL(url, line, column) {
+    var script = this._scriptForURL(url);
+
+    if (!script)
+      return null;
+
+    return new SDK.DebuggerModel.Location(this, script.scriptId, line, column);
+  }
+
+  setBreakpointByURL(url, lineNumber, columnNumber, condition, callback) {
+    TestRunner.addResult('    debuggerModel.setBreakpoint(' + [url, lineNumber, condition].join(':') + ')');
+    var breakpointId = url + ':' + lineNumber;
+
+    if (this._breakpoints[breakpointId]) {
+      this._scheduleSetBeakpointCallback(callback, null);
+      return;
+    }
+
+    this._breakpoints[breakpointId] = true;
+
+    if (lineNumber >= 2000) {
+      this._scheduleSetBeakpointCallback(callback, breakpointId, []);
+      return;
+    }
+
+    if (lineNumber >= 1000) {
+      var shiftedLocation = new SDK.DebuggerModel.Location(this, url, lineNumber + 10, columnNumber);
+      this._scheduleSetBeakpointCallback(callback, breakpointId, [shiftedLocation]);
+      return;
+    }
+
+    var locations = [];
+    var script = this._scriptForURL(url);
+
+    if (script) {
+      var location = new SDK.DebuggerModel.Location(this, script.scriptId, lineNumber, 0);
+      locations.push(location);
+    }
+
+    this._scheduleSetBeakpointCallback(callback, breakpointId, locations);
+  }
+
+  async removeBreakpoint(breakpointId) {
+    TestRunner.addResult('    debuggerModel.removeBreakpoint(' + breakpointId + ')');
+    delete this._breakpoints[breakpointId];
+  }
+
+  setBreakpointsActive() {
+  }
+
+  scriptForId(scriptId) {
+    return this._scripts[scriptId];
+  }
+
+  reset() {
+    TestRunner.addResult('  Resetting debugger.');
+    this._scripts = {};
+    this._debuggerWorkspaceBinding._reset(this);
+  }
+
+  addBreakpointListener(breakpointId, listener, thisObject) {
+    this._breakpointResolvedEventTarget.addEventListener(breakpointId, listener, thisObject);
+  }
+
+  removeBreakpointListener(breakpointId, listener, thisObject) {
+    this._breakpointResolvedEventTarget.removeEventListener(breakpointId, listener, thisObject);
+  }
+
+  _breakpointResolved(breakpointId, location) {
+    this._breakpointResolvedEventTarget.dispatchEventToListeners(breakpointId, location);
+  }
+};
+
+SourcesTestRunner.setupLiveLocationSniffers = function() {
+  TestRunner.addSniffer(Bindings.DebuggerWorkspaceBinding.prototype, 'createLiveLocation', function(rawLocation) {
+    TestRunner.addResult('    Location created: ' + rawLocation.scriptId + ':' + rawLocation.lineNumber);
+  }, true);
+
+  TestRunner.addSniffer(Bindings.DebuggerWorkspaceBinding.Location.prototype, 'dispose', function() {
+    TestRunner.addResult('    Location disposed: ' + this._rawLocation.scriptId + ':' + this._rawLocation.lineNumber);
+  }, true);
+};
+
+SourcesTestRunner.addScript = function(target, breakpointManager, url) {
+  target.debuggerModel._addScript(url, url);
+  TestRunner.addResult('  Adding script: ' + url);
+};
+
+SourcesTestRunner.addUISourceCode = function(target, breakpointManager, url, doNotSetSourceMapping, doNotAddScript) {
+  if (!doNotAddScript)
+    SourcesTestRunner.addScript(target, breakpointManager, url);
+
+  TestRunner.addResult('  Adding UISourceCode: ' + url);
+  var resourceMappingModelInfo = resourceMappingModelInfoForTarget(target);
+
+  if (resourceMappingModelInfo._bindings.has(url)) {
+    resourceMappingModelInfo._bindings.get(url).dispose();
+    resourceMappingModelInfo._bindings.delete(url);
+  }
+
+  var resource =
+      new SDK.Resource(target, null, url, url, '', '', Common.resourceTypes.Document, 'text/html', null, null);
+
+  resourceMappingModelInfo._resourceAdded({data: resource});
+
+  uiSourceCode = SourcesTestRunner.testWorkspace.uiSourceCodeForURL(url);
+  SourcesTestRunner.uiSourceCodes[url] = uiSourceCode;
+
+  if (!doNotSetSourceMapping)
+    breakpointManager._debuggerWorkspaceBinding.updateLocations(target.debuggerModel.scriptForId(url));
+
+
+  return uiSourceCode;
+};
+
+SourcesTestRunner.createBreakpointManager = function(targetManager, debuggerWorkspaceBinding, persistentBreakpoints) {
+  SourcesTestRunner._pendingBreakpointUpdates = 0;
+  TestRunner.addSniffer(
+      Bindings.BreakpointManager.ModelBreakpoint.prototype, '_updateInDebugger', updateInDebugger, true);
+  TestRunner.addSniffer(
+      Bindings.BreakpointManager.ModelBreakpoint.prototype, '_didUpdateInDebugger', didUpdateInDebugger, true);
+
+  function updateInDebugger() {
+    SourcesTestRunner._pendingBreakpointUpdates++;
+  }
+
+  function didUpdateInDebugger() {
+    SourcesTestRunner._pendingBreakpointUpdates--;
+    SourcesTestRunner._notifyAfterBreakpointUpdate();
+  }
+
+  persistentBreakpoints = persistentBreakpoints || [];
+
+  var setting = {
+    get: function() {
+      return persistentBreakpoints;
+    },
+
+    set: function(breakpoints) {
+      persistentBreakpoints = breakpoints;
+    }
+  };
+
+  function breakpointAdded(event) {
+    var breakpoint = event.data.breakpoint;
+    var uiLocation = event.data.uiLocation;
+
+    TestRunner.addResult('    breakpointAdded(' + [
+      uiLocation.uiSourceCode.url(), uiLocation.lineNumber, uiLocation.columnNumber, breakpoint.condition(),
+      breakpoint.enabled()
+    ].join(', ') + ')');
+  }
+
+  function breakpointRemoved(event) {
+    var uiLocation = event.data.uiLocation;
+
+    TestRunner.addResult('    breakpointRemoved(' + [
+      uiLocation.uiSourceCode.url(), uiLocation.lineNumber, uiLocation.columnNumber
+    ].join(', ') + ')');
+  }
+
+  var targets = targetManager.targets();
+  var mappingForManager;
+
+  for (var i = 0; i < targets.length; ++i) {
+    SourcesTestRunner.initializeDefaultMappingOnTarget(targets[i]);
+
+    if (!mappingForManager)
+      mappingForManager = targets[i].defaultMapping;
+  }
+
+  var breakpointManager = new Bindings.BreakpointManager(
+      setting, debuggerWorkspaceBinding._workspace, targetManager, debuggerWorkspaceBinding);
+  breakpointManager.defaultMapping = mappingForManager;
+  breakpointManager.addEventListener(Bindings.BreakpointManager.Events.BreakpointAdded, breakpointAdded);
+  breakpointManager.addEventListener(Bindings.BreakpointManager.Events.BreakpointRemoved, breakpointRemoved);
+  TestRunner.addResult('  Created breakpoints manager');
+  SourcesTestRunner.dumpBreakpointStorage(breakpointManager);
+  return breakpointManager;
+};
+
+SourcesTestRunner.setBreakpoint = function(
+    breakpointManager, uiSourceCode, lineNumber, columnNumber, condition, enabled, setBreakpointCallback) {
+  TestRunner.addResult(
+      '  Setting breakpoint at ' + uiSourceCode.url() + ':' + lineNumber + ':' + columnNumber + ' enabled:' + enabled +
+      ' condition:' + condition);
+
+  if (setBreakpointCallback)
+    window.setBreakpointCallback = setBreakpointCallback;
+
+  return breakpointManager.setBreakpoint(uiSourceCode, lineNumber, columnNumber, condition, enabled);
+};
+
+SourcesTestRunner.removeBreakpoint = function(breakpointManager, uiSourceCode, lineNumber, columnNumber) {
+  TestRunner.addResult('  Removing breakpoint at ' + uiSourceCode.url() + ':' + lineNumber + ':' + columnNumber);
+  breakpointManager.findBreakpoint(uiSourceCode, lineNumber, columnNumber).remove();
+};
+
+SourcesTestRunner.dumpBreakpointStorage = function(breakpointManager) {
+  var breakpoints = breakpointManager._storage._setting.get();
+  TestRunner.addResult('  Dumping Storage');
+
+  for (var i = 0; i < breakpoints.length; ++i) {
+    TestRunner.addResult(
+        '    ' + breakpoints[i].url + ':' + breakpoints[i].lineNumber + ' enabled:' + breakpoints[i].enabled +
+        ' condition:' + breakpoints[i].condition);
+  }
+};
+
+SourcesTestRunner.dumpBreakpointLocations = function(breakpointManager) {
+  var allBreakpointLocations = breakpointManager.allBreakpointLocations();
+  TestRunner.addResult('  Dumping Breakpoint Locations');
+  var lastUISourceCode = null;
+  var locations = [];
+
+  function dumpLocations(uiSourceCode, locations) {
+    locations.sort(function(a, b) {
+      return a.lineNumber - b.lineNumber;
+    });
+
+    TestRunner.addResult('    UISourceCode (url=\'' + uiSourceCode.url() + '\', uri=\'' + uiSourceCode.url() + '\')');
+
+    for (var i = 0; i < locations.length; ++i)
+      TestRunner.addResult('      Location: (' + locations[i].lineNumber + ', ' + locations[i].columnNumber + ')');
+  }
+
+  for (var i = 0; i < allBreakpointLocations.length; ++i) {
+    var uiLocation = allBreakpointLocations[i].uiLocation;
+    var uiSourceCode = uiLocation.uiSourceCode;
+
+    if (lastUISourceCode && lastUISourceCode !== uiSourceCode) {
+      dumpLocations(uiSourceCode, locations);
+      locations = [];
+    }
+
+    lastUISourceCode = uiSourceCode;
+    locations.push(uiLocation);
+  }
+
+  if (lastUISourceCode)
+    dumpLocations(lastUISourceCode, locations);
+};
+
+SourcesTestRunner.resetBreakpointManager = function(breakpointManager, next) {
+  TestRunner.addResult('  Resetting breakpoint manager');
+  breakpointManager.removeAllBreakpoints();
+  breakpointManager.removeProvisionalBreakpointsForTest();
+  SourcesTestRunner.uiSourceCodes = {};
+  next();
+};
+
+SourcesTestRunner.runAfterPendingBreakpointUpdates = function(breakpointManager, callback) {
+  SourcesTestRunner._pendingBreakpointUpdatesCallback = callback;
+  SourcesTestRunner._notifyAfterBreakpointUpdate();
+};
+
+SourcesTestRunner._notifyAfterBreakpointUpdate = function() {
+  if (!SourcesTestRunner._pendingBreakpointUpdates && SourcesTestRunner._pendingBreakpointUpdatesCallback) {
+    var callback = SourcesTestRunner._pendingBreakpointUpdatesCallback;
+    delete SourcesTestRunner._pendingBreakpointUpdatesCallback;
+    callback();
+  }
+};
+
+SourcesTestRunner.finishBreakpointTest = function(breakpointManager, next) {
+  SourcesTestRunner.runAfterPendingBreakpointUpdates(breakpointManager, dump);
+
+  function dump() {
+    SourcesTestRunner.dumpBreakpointLocations(breakpointManager);
+    SourcesTestRunner.dumpBreakpointStorage(breakpointManager);
+    SourcesTestRunner.runAfterPendingBreakpointUpdates(breakpointManager, reset);
+  }
+
+  function reset() {
+    SourcesTestRunner.resetBreakpointManager(breakpointManager, didReset);
+  }
+
+  function didReset() {
+    SourcesTestRunner.runAfterPendingBreakpointUpdates(breakpointManager, finish);
+  }
+
+  function finish() {
+    SourcesTestRunner.dumpBreakpointLocations(breakpointManager);
+    next();
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/DebuggerTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/DebuggerTestRunner.js
new file mode 100644
index 0000000..3f52c02
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/DebuggerTestRunner.js
@@ -0,0 +1,757 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+SourcesTestRunner.startDebuggerTest = function(callback, quiet) {
+  console.assert(TestRunner.debuggerModel.debuggerEnabled(), 'Debugger has to be enabled');
+
+  if (quiet !== undefined)
+    SourcesTestRunner._quiet = quiet;
+
+  UI.viewManager.showView('sources');
+  TestRunner.addSniffer(SDK.DebuggerModel.prototype, '_pausedScript', SourcesTestRunner._pausedScript, true);
+  TestRunner.addSniffer(SDK.DebuggerModel.prototype, '_resumedScript', SourcesTestRunner._resumedScript, true);
+  TestRunner.safeWrap(callback)();
+};
+
+SourcesTestRunner.startDebuggerTestPromise = function(quiet) {
+  var cb;
+  var p = new Promise(fullfill => cb = fullfill);
+  SourcesTestRunner.startDebuggerTest(cb, quiet);
+  return p;
+};
+
+SourcesTestRunner.completeDebuggerTest = function() {
+  Bindings.breakpointManager.setBreakpointsActive(true);
+  SourcesTestRunner.resumeExecution(TestRunner.completeTest.bind(TestRunner));
+};
+
+(function() {
+var origThen = Promise.prototype.then;
+var origCatch = Promise.prototype.catch;
+
+Promise.prototype.then = function() {
+  var result = origThen.apply(this, arguments);
+  origThen.call(result, undefined, onUncaughtPromiseReject.bind(null, new Error().stack));
+  return result;
+};
+
+Promise.prototype.catch = function() {
+  var result = origCatch.apply(this, arguments);
+  origThen.call(result, undefined, onUncaughtPromiseReject.bind(null, new Error().stack));
+  return result;
+};
+
+function onUncaughtPromiseReject(stack, e) {
+  var message = typeof e === 'object' && e.stack || e;
+  TestRunner.addResult('FAIL: Uncaught exception in promise: ' + message + ' ' + stack);
+  SourcesTestRunner.completeDebuggerTest();
+}
+})();
+
+SourcesTestRunner.runDebuggerTestSuite = function(testSuite) {
+  var testSuiteTests = testSuite.slice();
+
+  function runner() {
+    if (!testSuiteTests.length) {
+      SourcesTestRunner.completeDebuggerTest();
+      return;
+    }
+
+    var nextTest = testSuiteTests.shift();
+    TestRunner.addResult('');
+    TestRunner.addResult(
+        'Running: ' +
+        /function\s([^(]*)/.exec(nextTest)[1]);
+    TestRunner.safeWrap(nextTest)(runner, runner);
+  }
+
+  SourcesTestRunner.startDebuggerTest(runner);
+};
+
+SourcesTestRunner.runTestFunction = function() {
+  TestRunner.evaluateInPage('scheduleTestFunction()');
+  TestRunner.addResult('Set timer for test function.');
+};
+
+SourcesTestRunner.runTestFunctionAndWaitUntilPaused = function(callback) {
+  SourcesTestRunner.runTestFunction();
+  SourcesTestRunner.waitUntilPaused(callback);
+};
+
+SourcesTestRunner.runTestFunctionAndWaitUntilPausedPromise = function() {
+  return new Promise(SourcesTestRunner.runTestFunctionAndWaitUntilPaused);
+};
+
+SourcesTestRunner.runAsyncCallStacksTest = function(totalDebuggerStatements, maxAsyncCallStackDepth) {
+  var defaultMaxAsyncCallStackDepth = 32;
+  SourcesTestRunner.setQuiet(true);
+  SourcesTestRunner.startDebuggerTest(step1);
+
+  async function step1() {
+    await TestRunner.DebuggerAgent.setAsyncCallStackDepth(maxAsyncCallStackDepth || defaultMaxAsyncCallStackDepth);
+    SourcesTestRunner.runTestFunctionAndWaitUntilPaused(didPause);
+  }
+
+  var step = 0;
+  var callStacksOutput = [];
+
+  function didPause(callFrames, reason, breakpointIds, asyncStackTrace) {
+    ++step;
+    callStacksOutput.push(SourcesTestRunner.captureStackTraceIntoString(callFrames, asyncStackTrace) + '\n');
+
+    if (step < totalDebuggerStatements) {
+      SourcesTestRunner.resumeExecution(SourcesTestRunner.waitUntilPaused.bind(SourcesTestRunner, didPause));
+    } else {
+      TestRunner.addResult('Captured call stacks in no particular order:');
+      callStacksOutput.sort();
+      TestRunner.addResults(callStacksOutput);
+      SourcesTestRunner.completeDebuggerTest();
+    }
+  }
+};
+
+SourcesTestRunner.dumpSourceFrameMessages = function(sourceFrame, dumpFullURL) {
+  var messages = [];
+
+  for (var bucket of sourceFrame._rowMessageBuckets.values()) {
+    for (var rowMessage of bucket._messages) {
+      var message = rowMessage.message();
+      messages.push(String.sprintf(
+          '  %d:%d [%s] %s', message.lineNumber(), message.columnNumber(), message.level(), message.text()));
+    }
+  }
+
+  var name = (dumpFullURL ? sourceFrame.uiSourceCode().url() : sourceFrame.uiSourceCode().displayName());
+  TestRunner.addResult('SourceFrame ' + name + ': ' + messages.length + ' message(s)');
+  TestRunner.addResult(messages.join('\n'));
+};
+
+SourcesTestRunner.waitUntilPausedNextTime = function(callback) {
+  SourcesTestRunner._waitUntilPausedCallback = TestRunner.safeWrap(callback);
+};
+
+SourcesTestRunner.waitUntilPaused = function(callback) {
+  callback = TestRunner.safeWrap(callback);
+
+  if (SourcesTestRunner._pausedScriptArguments)
+    callback.apply(callback, SourcesTestRunner._pausedScriptArguments);
+  else
+    SourcesTestRunner._waitUntilPausedCallback = callback;
+};
+
+SourcesTestRunner.waitUntilPausedPromise = function() {
+  return new Promise(resolve => SourcesTestRunner.waitUntilPaused(resolve));
+};
+
+SourcesTestRunner.waitUntilResumedNextTime = function(callback) {
+  SourcesTestRunner._waitUntilResumedCallback = TestRunner.safeWrap(callback);
+};
+
+SourcesTestRunner.waitUntilResumed = function(callback) {
+  callback = TestRunner.safeWrap(callback);
+
+  if (!SourcesTestRunner._pausedScriptArguments)
+    callback();
+  else
+    SourcesTestRunner._waitUntilResumedCallback = callback;
+};
+
+SourcesTestRunner.resumeExecution = function(callback) {
+  if (UI.panels.sources.paused())
+    UI.panels.sources._togglePause();
+
+  SourcesTestRunner.waitUntilResumed(callback);
+};
+
+SourcesTestRunner.waitUntilPausedAndDumpStackAndResume = function(callback, options) {
+  SourcesTestRunner.waitUntilPaused(paused);
+  TestRunner.addSniffer(Sources.SourcesPanel.prototype, '_updateDebuggerButtonsAndStatus', setStatus);
+  var caption;
+  var callFrames;
+  var asyncStackTrace;
+
+  function setStatus() {
+    var statusElement = this.element.querySelector('.paused-message');
+    caption = statusElement.deepTextContent();
+
+    if (callFrames)
+      step1();
+  }
+
+  function paused(frames, reason, breakpointIds, async) {
+    callFrames = frames;
+    asyncStackTrace = async;
+
+    if (typeof caption === 'string')
+      step1();
+  }
+
+  function step1() {
+    SourcesTestRunner.captureStackTrace(callFrames, asyncStackTrace, options);
+    TestRunner.addResult(TestRunner.clearSpecificInfoFromStackFrames(caption));
+    TestRunner.deprecatedRunAfterPendingDispatches(step2);
+  }
+
+  function step2() {
+    SourcesTestRunner.resumeExecution(TestRunner.safeWrap(callback));
+  }
+};
+
+SourcesTestRunner.stepOver = function() {
+  Promise.resolve().then(function() {
+    UI.panels.sources._stepOver();
+  });
+};
+
+SourcesTestRunner.stepInto = function() {
+  Promise.resolve().then(function() {
+    UI.panels.sources._stepInto();
+  });
+};
+
+SourcesTestRunner.stepOut = function() {
+  Promise.resolve().then(function() {
+    UI.panels.sources._stepOut();
+  });
+};
+
+SourcesTestRunner.togglePause = function() {
+  Promise.resolve().then(function() {
+    UI.panels.sources._togglePause();
+  });
+};
+
+SourcesTestRunner.waitUntilPausedAndPerformSteppingActions = function(actions, callback) {
+  callback = TestRunner.safeWrap(callback);
+  SourcesTestRunner.waitUntilPaused(didPause);
+
+  function didPause(callFrames, reason, breakpointIds, asyncStackTrace) {
+    var action = actions.shift();
+
+    if (action === 'Print') {
+      SourcesTestRunner.captureStackTrace(callFrames, asyncStackTrace);
+      TestRunner.addResult('');
+
+      while (action === 'Print')
+        action = actions.shift();
+    }
+
+    if (!action) {
+      callback();
+      return;
+    }
+
+    TestRunner.addResult('Executing ' + action + '...');
+
+    switch (action) {
+      case 'StepInto':
+        SourcesTestRunner.stepInto();
+        break;
+      case 'StepOver':
+        SourcesTestRunner.stepOver();
+        break;
+      case 'StepOut':
+        SourcesTestRunner.stepOut();
+        break;
+      case 'Resume':
+        SourcesTestRunner.togglePause();
+        break;
+      default:
+        TestRunner.addResult('FAIL: Unknown action: ' + action);
+        callback();
+        return;
+    }
+
+    SourcesTestRunner.waitUntilResumed(
+        (actions.length ? SourcesTestRunner.waitUntilPaused.bind(SourcesTestRunner, didPause) : callback));
+  }
+};
+
+SourcesTestRunner.captureStackTrace = function(callFrames, asyncStackTrace, options) {
+  TestRunner.addResult(SourcesTestRunner.captureStackTraceIntoString(callFrames, asyncStackTrace, options));
+};
+
+SourcesTestRunner.captureStackTraceIntoString = function(callFrames, asyncStackTrace, options) {
+  var results = [];
+  options = options || {};
+
+  function printCallFrames(callFrames, locationFunction, returnValueFunction) {
+    var printed = 0;
+
+    for (var i = 0; i < callFrames.length; i++) {
+      var frame = callFrames[i];
+      var location = locationFunction.call(frame);
+      var script = location.script();
+      var uiLocation = Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(location);
+      var isFramework = Bindings.blackboxManager.isBlackboxedRawLocation(location);
+
+      if (options.dropFrameworkCallFrames && isFramework)
+        continue;
+
+      var url;
+      var lineNumber;
+
+      if (uiLocation && uiLocation.uiSourceCode.project().type() !== Workspace.projectTypes.Debugger) {
+        url = uiLocation.uiSourceCode.name();
+        lineNumber = uiLocation.lineNumber + 1;
+      } else {
+        url = Bindings.displayNameForURL(script.sourceURL);
+        lineNumber = location.lineNumber + 1;
+      }
+
+      var s = ((isFramework ? '  * ' : '    ')) + printed++ + ') ' + frame.functionName + ' (' + url +
+          ((options.dropLineNumbers ? '' : ':' + lineNumber)) + ')';
+      results.push(s);
+
+      if (options.printReturnValue && returnValueFunction && returnValueFunction.call(frame))
+        results.push('       <return>: ' + returnValueFunction.call(frame).description);
+
+      if (frame.functionName === 'scheduleTestFunction') {
+        var remainingFrames = callFrames.length - 1 - i;
+
+        if (remainingFrames)
+          results.push('    <... skipped remaining frames ...>');
+
+        break;
+      }
+    }
+
+    return printed;
+  }
+
+  function runtimeCallFramePosition() {
+    return new SDK.DebuggerModel.Location(debuggerModel, this.scriptId, this.lineNumber, this.columnNumber);
+  }
+
+  results.push('Call stack:');
+  printCallFrames(
+      callFrames, SDK.DebuggerModel.CallFrame.prototype.location, SDK.DebuggerModel.CallFrame.prototype.returnValue);
+
+  while (asyncStackTrace) {
+    results.push('    [' + (asyncStackTrace.description || 'Async Call') + ']');
+    var debuggerModel = TestRunner.debuggerModel;
+    var printed = printCallFrames(asyncStackTrace.callFrames, runtimeCallFramePosition);
+
+    if (!printed)
+      results.pop();
+
+    asyncStackTrace = asyncStackTrace.parent;
+  }
+
+  return results.join('\n');
+};
+
+SourcesTestRunner.dumpSourceFrameContents = function(sourceFrame) {
+  TestRunner.addResult('==Source frame contents start==');
+  var textEditor = sourceFrame._textEditor;
+
+  for (var i = 0; i < textEditor.linesCount; ++i)
+    TestRunner.addResult(textEditor.line(i));
+
+  TestRunner.addResult('==Source frame contents end==');
+};
+
+SourcesTestRunner._pausedScript = function(callFrames, reason, auxData, breakpointIds, asyncStackTrace) {
+  if (!SourcesTestRunner._quiet)
+    TestRunner.addResult('Script execution paused.');
+
+  var debuggerModel = this.target().model(SDK.DebuggerModel);
+  SourcesTestRunner._pausedScriptArguments = [
+    SDK.DebuggerModel.CallFrame.fromPayloadArray(debuggerModel, callFrames), reason, breakpointIds, asyncStackTrace,
+    auxData
+  ];
+
+  if (SourcesTestRunner._waitUntilPausedCallback) {
+    var callback = SourcesTestRunner._waitUntilPausedCallback;
+    delete SourcesTestRunner._waitUntilPausedCallback;
+    setTimeout(() => callback.apply(callback, SourcesTestRunner._pausedScriptArguments));
+  }
+};
+
+SourcesTestRunner._resumedScript = function() {
+  if (!SourcesTestRunner._quiet)
+    TestRunner.addResult('Script execution resumed.');
+
+  delete SourcesTestRunner._pausedScriptArguments;
+
+  if (SourcesTestRunner._waitUntilResumedCallback) {
+    var callback = SourcesTestRunner._waitUntilResumedCallback;
+    delete SourcesTestRunner._waitUntilResumedCallback;
+    callback();
+  }
+};
+
+SourcesTestRunner.showUISourceCode = function(uiSourceCode, callback) {
+  var panel = UI.panels.sources;
+  panel.showUISourceCode(uiSourceCode);
+  var sourceFrame = panel.visibleView;
+
+  if (sourceFrame.loaded)
+    callback(sourceFrame);
+  else
+    TestRunner.addSniffer(sourceFrame, 'onTextEditorContentSet', callback && callback.bind(null, sourceFrame));
+};
+
+SourcesTestRunner.showUISourceCodePromise = function(uiSourceCode) {
+  var fulfill;
+  var promise = new Promise(x => fulfill = x);
+  SourcesTestRunner.showUISourceCode(uiSourceCode, fulfill);
+  return promise;
+};
+
+SourcesTestRunner.showScriptSource = function(scriptName, callback) {
+  SourcesTestRunner.waitForScriptSource(scriptName, onScriptSource);
+
+  function onScriptSource(uiSourceCode) {
+    SourcesTestRunner.showUISourceCode(uiSourceCode, callback);
+  }
+};
+
+SourcesTestRunner.waitForScriptSource = function(scriptName, callback) {
+  var panel = UI.panels.sources;
+  var uiSourceCodes = panel._workspace.uiSourceCodes();
+
+  for (var i = 0; i < uiSourceCodes.length; ++i) {
+    if (uiSourceCodes[i].project().type() === Workspace.projectTypes.Service)
+      continue;
+
+    if (uiSourceCodes[i].name() === scriptName) {
+      callback(uiSourceCodes[i]);
+      return;
+    }
+  }
+
+  TestRunner.addSniffer(
+      Sources.SourcesView.prototype, '_addUISourceCode',
+      SourcesTestRunner.waitForScriptSource.bind(SourcesTestRunner, scriptName, callback));
+};
+
+SourcesTestRunner.setBreakpoint = function(sourceFrame, lineNumber, condition, enabled) {
+  if (!sourceFrame._muted)
+    sourceFrame._setBreakpoint(lineNumber, 0, condition, enabled);
+};
+
+SourcesTestRunner.removeBreakpoint = function(sourceFrame, lineNumber) {
+  sourceFrame._breakpointManager.findBreakpoints(sourceFrame._uiSourceCode, lineNumber)[0].remove();
+};
+
+SourcesTestRunner.createNewBreakpoint = function(sourceFrame, lineNumber, condition, enabled) {
+  var promise =
+      new Promise(resolve => TestRunner.addSniffer(sourceFrame.__proto__, '_breakpointWasSetForTest', resolve));
+  sourceFrame._createNewBreakpoint(lineNumber, condition, enabled);
+  return promise;
+};
+
+SourcesTestRunner.toggleBreakpoint = function(sourceFrame, lineNumber, disableOnly) {
+  if (!sourceFrame._muted)
+    sourceFrame._toggleBreakpoint(lineNumber, disableOnly);
+};
+
+SourcesTestRunner.waitBreakpointSidebarPane = function(waitUntilResolved) {
+  return new Promise(
+             resolve => TestRunner.addSniffer(
+                 Sources.JavaScriptBreakpointsSidebarPane.prototype, '_didUpdateForTest', resolve))
+      .then(checkIfReady);
+
+  function checkIfReady() {
+    if (!waitUntilResolved)
+      return;
+
+    for (var breakpoint of Bindings.breakpointManager._allBreakpoints()) {
+      if (breakpoint._fakePrimaryLocation && breakpoint.enabled())
+        return SourcesTestRunner.waitBreakpointSidebarPane();
+    }
+  }
+};
+
+SourcesTestRunner.breakpointsSidebarPaneContent = function() {
+  var paneElement = self.runtime.sharedInstance(Sources.JavaScriptBreakpointsSidebarPane).contentElement;
+  var empty = paneElement.querySelector('.gray-info-message');
+
+  if (empty)
+    return TestRunner.textContentWithLineBreaks(empty);
+
+  var entries = Array.from(paneElement.querySelectorAll('.breakpoint-entry'));
+  return entries.map(TestRunner.textContentWithLineBreaks).join('\n');
+};
+
+SourcesTestRunner.dumpBreakpointSidebarPane = function(title) {
+  TestRunner.addResult('Breakpoint sidebar pane ' + (title || ''));
+  TestRunner.addResult(SourcesTestRunner.breakpointsSidebarPaneContent());
+};
+
+SourcesTestRunner.dumpScopeVariablesSidebarPane = function() {
+  TestRunner.addResult('Scope variables sidebar pane:');
+  var sections = SourcesTestRunner.scopeChainSections();
+
+  for (var i = 0; i < sections.length; ++i) {
+    var textContent = TestRunner.textContentWithLineBreaks(sections[i].element);
+    var text = TestRunner.clearSpecificInfoFromStackFrames(textContent);
+
+    if (text.length > 0)
+      TestRunner.addResult(text);
+
+    if (!sections[i].objectTreeElement().expanded)
+      TestRunner.addResult('    <section collapsed>');
+  }
+};
+
+SourcesTestRunner.scopeChainSections = function() {
+  var children = self.runtime.sharedInstance(Sources.ScopeChainSidebarPane).contentElement.children;
+  var sections = [];
+
+  for (var i = 0; i < children.length; ++i)
+    sections.push(children[i]._section);
+
+  return sections;
+};
+
+SourcesTestRunner.expandScopeVariablesSidebarPane = function(callback) {
+  var sections = SourcesTestRunner.scopeChainSections();
+
+  for (var i = 0; i < sections.length - 1; ++i)
+    sections[i].expand();
+
+  TestRunner.deprecatedRunAfterPendingDispatches(callback);
+};
+
+SourcesTestRunner.expandProperties = function(properties, callback) {
+  var index = 0;
+
+  function expandNextPath() {
+    if (index === properties.length) {
+      TestRunner.safeWrap(callback)();
+      return;
+    }
+
+    var parentTreeElement = properties[index++];
+    var path = properties[index++];
+    SourcesTestRunner._expandProperty(parentTreeElement, path, 0, expandNextPath);
+  }
+
+  TestRunner.deprecatedRunAfterPendingDispatches(expandNextPath);
+};
+
+SourcesTestRunner._expandProperty = function(parentTreeElement, path, pathIndex, callback) {
+  if (pathIndex === path.length) {
+    TestRunner.addResult('Expanded property: ' + path.join('.'));
+    callback();
+    return;
+  }
+
+  var name = path[pathIndex++];
+  var propertyTreeElement = SourcesTestRunner._findChildPropertyTreeElement(parentTreeElement, name);
+
+  if (!propertyTreeElement) {
+    TestRunner.addResult('Failed to expand property: ' + path.slice(0, pathIndex).join('.'));
+    SourcesTestRunner.completeDebuggerTest();
+    return;
+  }
+
+  propertyTreeElement.expand();
+  TestRunner.deprecatedRunAfterPendingDispatches(
+      SourcesTestRunner._expandProperty.bind(SourcesTestRunner, propertyTreeElement, path, pathIndex, callback));
+};
+
+SourcesTestRunner._findChildPropertyTreeElement = function(parent, childName) {
+  var children = parent.children();
+
+  for (var i = 0; i < children.length; i++) {
+    var treeElement = children[i];
+    var property = treeElement.property;
+
+    if (property.name === childName)
+      return treeElement;
+  }
+};
+
+SourcesTestRunner.setQuiet = function(quiet) {
+  SourcesTestRunner._quiet = quiet;
+};
+
+SourcesTestRunner.queryScripts = function(filter) {
+  var scripts = TestRunner.debuggerModel.scripts();
+  return (filter ? scripts.filter(filter) : scripts);
+};
+
+SourcesTestRunner.createScriptMock = function(
+    url, startLine, startColumn, isContentScript, source, target, preRegisterCallback) {
+  target = target || SDK.targetManager.mainTarget();
+  var debuggerModel = target.model(SDK.DebuggerModel);
+  var scriptId = ++SourcesTestRunner._lastScriptId + '';
+  var lineCount = source.computeLineEndings().length;
+  var endLine = startLine + lineCount - 1;
+  var endColumn =
+      (lineCount === 1 ? startColumn + source.length : source.length - source.computeLineEndings()[lineCount - 2]);
+  var hasSourceURL =
+      !!source.match(/\/\/#\ssourceURL=\s*(\S*?)\s*$/m) || !!source.match(/\/\/@\ssourceURL=\s*(\S*?)\s*$/m);
+
+  var script = new SDK.Script(
+      debuggerModel, scriptId, url, startLine, startColumn, endLine, endColumn, 0, '', isContentScript, false,
+      undefined, hasSourceURL, source.length);
+
+  script.requestContent = function() {
+    var trimmedSource = SDK.Script._trimSourceURLComment(source);
+    return Promise.resolve(trimmedSource);
+  };
+
+  if (preRegisterCallback)
+    preRegisterCallback(script);
+
+  debuggerModel._registerScript(script);
+  return script;
+};
+
+SourcesTestRunner._lastScriptId = 0;
+
+SourcesTestRunner.checkRawLocation = function(script, lineNumber, columnNumber, location) {
+  TestRunner.assertEquals(script.scriptId, location.scriptId, 'Incorrect scriptId');
+  TestRunner.assertEquals(lineNumber, location.lineNumber, 'Incorrect lineNumber');
+  TestRunner.assertEquals(columnNumber, location.columnNumber, 'Incorrect columnNumber');
+};
+
+SourcesTestRunner.checkUILocation = function(uiSourceCode, lineNumber, columnNumber, location) {
+  TestRunner.assertEquals(
+      uiSourceCode, location.uiSourceCode,
+      'Incorrect uiSourceCode, expected \'' + ((uiSourceCode ? uiSourceCode.url() : null)) + '\',' +
+          ' but got \'' + ((location.uiSourceCode ? location.uiSourceCode.url() : null)) + '\'');
+
+  TestRunner.assertEquals(
+      lineNumber, location.lineNumber,
+      'Incorrect lineNumber, expected \'' + lineNumber + '\', but got \'' + location.lineNumber + '\'');
+
+  TestRunner.assertEquals(
+      columnNumber, location.columnNumber,
+      'Incorrect columnNumber, expected \'' + columnNumber + '\', but got \'' + location.columnNumber + '\'');
+};
+
+SourcesTestRunner.scriptFormatter = function() {
+  return self.runtime.allInstances(Sources.SourcesView.EditorAction).then(function(editorActions) {
+    for (var i = 0; i < editorActions.length; ++i) {
+      if (editorActions[i] instanceof Sources.ScriptFormatterEditorAction)
+        return editorActions[i];
+    }
+
+    return null;
+  });
+};
+
+SourcesTestRunner.waitForExecutionContextInTarget = function(target, callback) {
+  var runtimeModel = target.model(SDK.RuntimeModel);
+
+  if (runtimeModel.executionContexts().length) {
+    callback(runtimeModel.executionContexts()[0]);
+    return;
+  }
+
+  runtimeModel.addEventListener(SDK.RuntimeModel.Events.ExecutionContextCreated, contextCreated);
+
+  function contextCreated() {
+    runtimeModel.removeEventListener(SDK.RuntimeModel.Events.ExecutionContextCreated, contextCreated);
+    callback(runtimeModel.executionContexts()[0]);
+  }
+};
+
+SourcesTestRunner.selectThread = function(target) {
+  var threadsPane = self.runtime.sharedInstance(Sources.ThreadsSidebarPane);
+  threadsPane._list.selectItem(target.model(SDK.DebuggerModel));
+};
+
+SourcesTestRunner.evaluateOnCurrentCallFrame = function(code) {
+  return new Promise(
+      succ => TestRunner.debuggerModel.evaluateOnSelectedCallFrame(
+          code, 'console', false, true, false, false, TestRunner.safeWrap(succ)));
+};
+
+SourcesTestRunner.waitJavaScriptSourceFrameBreakpoints = function(sourceFrame, inline) {
+  return waitUpdate().then(checkIfReady);
+
+  function waitUpdate() {
+    return new Promise(
+        resolve => TestRunner.addSniffer(sourceFrame.__proto__, '_breakpointDecorationsUpdatedForTest', resolve));
+  }
+
+  function checkIfReady() {
+    for (var breakpoint of Bindings.breakpointManager._allBreakpoints()) {
+      if (breakpoint._fakePrimaryLocation && breakpoint.enabled())
+        return waitUpdate().then(checkIfReady);
+    }
+
+    return Promise.resolve();
+  }
+};
+
+SourcesTestRunner.dumpJavaScriptSourceFrameBreakpoints = function(sourceFrame) {
+  var textEditor = sourceFrame._textEditor;
+
+  for (var lineNumber = 0; lineNumber < textEditor.linesCount; ++lineNumber) {
+    if (!textEditor.hasLineClass(lineNumber, 'cm-breakpoint'))
+      continue;
+
+    var disabled = textEditor.hasLineClass(lineNumber, 'cm-breakpoint-disabled');
+    var conditional = textEditor.hasLineClass(lineNumber, 'cm-breakpoint-conditional');
+    TestRunner.addResult(
+        'breakpoint at ' + lineNumber + ((disabled ? ' disabled' : '')) + ((conditional ? ' conditional' : '')));
+    var range = new TextUtils.TextRange(lineNumber, 0, lineNumber, textEditor.line(lineNumber).length);
+    var bookmarks = textEditor.bookmarks(range, Sources.JavaScriptSourceFrame.BreakpointDecoration._bookmarkSymbol);
+    bookmarks = bookmarks.filter(bookmark => !!bookmark.position());
+    bookmarks.sort((bookmark1, bookmark2) => bookmark1.position().startColumn - bookmark2.position().startColumn);
+
+    for (var bookmark of bookmarks) {
+      var position = bookmark.position();
+      var element = bookmark[Sources.JavaScriptSourceFrame.BreakpointDecoration._elementSymbolForTest];
+      var disabled = element.classList.contains('cm-inline-disabled');
+      var conditional = element.classList.contains('cm-inline-conditional');
+
+      TestRunner.addResult(
+          '  inline breakpoint at (' + position.startLine + ', ' + position.startColumn + ')' +
+          ((disabled ? ' disabled' : '')) + ((conditional ? ' conditional' : '')));
+    }
+  }
+};
+
+SourcesTestRunner.clickJavaScriptSourceFrameBreakpoint = function(sourceFrame, lineNumber, index, next) {
+  var textEditor = sourceFrame._textEditor;
+  var lineLength = textEditor.line(lineNumber).length;
+  var lineRange = new TextUtils.TextRange(lineNumber, 0, lineNumber, lineLength);
+  var bookmarks = textEditor.bookmarks(lineRange, Sources.JavaScriptSourceFrame.BreakpointDecoration._bookmarkSymbol);
+  bookmarks.sort((bookmark1, bookmark2) => bookmark1.position().startColumn - bookmark2.position().startColumn);
+  var bookmark = bookmarks[index];
+
+  if (bookmark) {
+    bookmark[Sources.JavaScriptSourceFrame.BreakpointDecoration._elementSymbolForTest].click();
+  } else {
+    TestRunner.addResult(`Could not click on Javascript breakpoint - lineNumber: ${lineNumber}, index: ${index}`);
+    next();
+  }
+};
+
+SourcesTestRunner.setEventListenerBreakpoint = function(id, enabled, targetName) {
+  var pane = self.runtime.sharedInstance(Sources.EventListenerBreakpointsSidebarPane);
+
+  var auxData = {'eventName': id};
+
+  if (targetName)
+    auxData.targetName = targetName;
+
+  var breakpoint = SDK.domDebuggerManager.resolveEventListenerBreakpoint(auxData);
+
+  if (breakpoint.enabled() !== enabled) {
+    pane._breakpoints.get(breakpoint).checkbox.checked = enabled;
+    pane._breakpointCheckboxClicked(breakpoint);
+  }
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    function scheduleTestFunction() {
+      setTimeout(testFunction, 0);
+    }
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/EditorTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/EditorTestRunner.js
new file mode 100644
index 0000000..59983cc
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/EditorTestRunner.js
@@ -0,0 +1,219 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+SourcesTestRunner.createTestEditor = function(clientHeight, textEditorDelegate) {
+  var textEditor = new SourceFrame.SourcesTextEditor(textEditorDelegate || new SourceFrame.SourcesTextEditorDelegate());
+  clientHeight = clientHeight || 100;
+  textEditor.element.style.height = clientHeight + 'px';
+  textEditor.element.style.flex = 'none';
+  textEditor.show(UI.inspectorView.element);
+  return textEditor;
+};
+
+function textWithSelection(text, selections) {
+  if (!selections.length)
+    return text;
+
+  function lineWithCursor(line, column, cursorChar) {
+    return line.substring(0, column) + cursorChar + line.substring(column);
+  }
+
+  var lines = text.split('\n');
+  selections.sort(TextUtils.TextRange.comparator);
+
+  for (var i = selections.length - 1; i >= 0; --i) {
+    var selection = selections[i];
+    selection = selection.normalize();
+    var endCursorChar = (selection.isEmpty() ? '|' : '<');
+    lines[selection.endLine] = lineWithCursor(lines[selection.endLine], selection.endColumn, endCursorChar);
+
+    if (!selection.isEmpty())
+      lines[selection.startLine] = lineWithCursor(lines[selection.startLine], selection.startColumn, '>');
+  }
+
+  return lines.join('\n');
+}
+
+SourcesTestRunner.dumpTextWithSelection = function(textEditor, dumpWhiteSpaces) {
+  var text = textWithSelection(textEditor.text(), textEditor.selections());
+
+  if (dumpWhiteSpaces)
+    text = text.replace(/ /g, '.');
+
+  TestRunner.addResult(text);
+};
+
+SourcesTestRunner.setLineSelections = function(editor, selections) {
+  var coords = [];
+
+  for (var i = 0; i < selections.length; ++i) {
+    var selection = selections[i];
+
+    if (typeof selection.column === 'number') {
+      selection.from = selection.column;
+      selection.to = selection.column;
+    }
+
+    coords.push(new TextUtils.TextRange(selection.line, selection.from, selection.line, selection.to));
+  }
+
+  editor.setSelections(coords);
+};
+
+SourcesTestRunner.typeIn = function(editor, typeText, callback) {
+  callback = callback || new Function();
+  var noop = new Function();
+
+  for (var charIndex = 0; charIndex < typeText.length; ++charIndex) {
+    var iterationCallback = (charIndex + 1 === typeText.length ? callback : noop);
+
+    switch (typeText[charIndex]) {
+      case '\n':
+        SourcesTestRunner.fakeKeyEvent(editor, 'Enter', null, iterationCallback);
+        break;
+      case 'L':
+        SourcesTestRunner.fakeKeyEvent(editor, 'ArrowLeft', null, iterationCallback);
+        break;
+      case 'R':
+        SourcesTestRunner.fakeKeyEvent(editor, 'ArrowRight', null, iterationCallback);
+        break;
+      case 'U':
+        SourcesTestRunner.fakeKeyEvent(editor, 'ArrowUp', null, iterationCallback);
+        break;
+      case 'D':
+        SourcesTestRunner.fakeKeyEvent(editor, 'ArrowDown', null, iterationCallback);
+        break;
+      default:
+        SourcesTestRunner.fakeKeyEvent(editor, typeText[charIndex], null, iterationCallback);
+    }
+  }
+};
+
+var eventCodes = {Enter: 13, Home: 36, ArrowLeft: 37, ArrowUp: 38, ArrowRight: 39, ArrowDown: 40};
+
+function createCodeMirrorFakeEvent(editor, eventType, code, charCode, modifiers) {
+  function eventPreventDefault() {
+    this._handled = true;
+  }
+
+  var event = {
+    _handled: false,
+    type: eventType,
+    keyCode: code,
+    charCode: charCode,
+    preventDefault: eventPreventDefault,
+    stopPropagation: function() {},
+    target: editor._codeMirror.display.input.textarea
+  };
+
+  if (modifiers) {
+    for (var i = 0; i < modifiers.length; ++i)
+      event[modifiers[i]] = true;
+  }
+
+  return event;
+}
+
+function fakeCodeMirrorKeyEvent(editor, eventType, code, charCode, modifiers) {
+  var event = createCodeMirrorFakeEvent(editor, eventType, code, charCode, modifiers);
+
+  switch (eventType) {
+    case 'keydown':
+      editor._codeMirror.triggerOnKeyDown(event);
+      break;
+    case 'keypress':
+      editor._codeMirror.triggerOnKeyPress(event);
+      break;
+    case 'keyup':
+      editor._codeMirror.triggerOnKeyUp(event);
+      break;
+    default:
+      throw new Error('Unknown KeyEvent type');
+  }
+
+  return event._handled;
+}
+
+function fakeCodeMirrorInputEvent(editor, character) {
+  if (typeof character === 'string')
+    editor._codeMirror.display.input.textarea.value += character;
+}
+
+SourcesTestRunner.fakeKeyEvent = function(editor, originalCode, modifiers, callback) {
+  modifiers = modifiers || [];
+  var code;
+  var charCode;
+
+  if (originalCode === '\'') {
+    code = 222;
+    charCode = 0;
+  } else if (originalCode === '"') {
+    code = 222;
+    modifiers.push('shiftKey');
+    charCode = 34;
+  } else if (originalCode === '(') {
+    code = '9'.charCodeAt(0);
+    modifiers.push('shiftKey');
+    charCode = originalCode.charCodeAt(0);
+  }
+
+  var code = code || eventCodes[originalCode] || originalCode;
+
+  if (typeof code === 'string')
+    code = code.charCodeAt(0);
+
+  if (fakeCodeMirrorKeyEvent(editor, 'keydown', code, charCode, modifiers)) {
+    callback();
+    return;
+  }
+
+  if (fakeCodeMirrorKeyEvent(editor, 'keypress', code, charCode, modifiers)) {
+    callback();
+    return;
+  }
+
+  fakeCodeMirrorInputEvent(editor, originalCode);
+  fakeCodeMirrorKeyEvent(editor, 'keyup', code, charCode, modifiers);
+
+  function callbackWrapper() {
+    editor._codeMirror.off('inputRead', callbackWrapper);
+    callback();
+  }
+
+  editor._codeMirror.on('inputRead', callbackWrapper);
+};
+
+SourcesTestRunner.dumpSelectionStats = function(textEditor) {
+  var listHashMap = {};
+  var sortedKeys = [];
+  var selections = textEditor.selections();
+
+  for (var i = 0; i < selections.length; ++i) {
+    var selection = selections[i];
+    var text = textEditor.text(selection);
+
+    if (!listHashMap[text]) {
+      listHashMap[text] = 1;
+      sortedKeys.push(text);
+    } else {
+      ++listHashMap[text];
+    }
+  }
+
+  for (var i = 0; i < sortedKeys.length; ++i) {
+    var keyName = sortedKeys[i];
+
+    if (!keyName.length)
+      keyName = '<Empty string>';
+    else
+      keyName = '\'' + keyName + '\'';
+
+    TestRunner.addResult(keyName + ': ' + listHashMap[sortedKeys[i]]);
+  }
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/LiveEditTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/LiveEditTestRunner.js
new file mode 100644
index 0000000..80afde0
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/LiveEditTestRunner.js
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+SourcesTestRunner.replaceInSource = function(sourceFrame, string, replacement) {
+  sourceFrame._textEditor.setReadOnly(false);
+
+  for (var i = 0; i < sourceFrame._textEditor.linesCount; ++i) {
+    var line = sourceFrame._textEditor.line(i);
+    var column = line.indexOf(string);
+
+    if (column === -1)
+      continue;
+
+    range = new TextUtils.TextRange(i, column, i, column + string.length);
+    break;
+  }
+};
+
+SourcesTestRunner.commitSource = function(sourceFrame) {
+  sourceFrame.commitEditing();
+};
+
+SourcesTestRunner.undoSourceEditing = function(sourceFrame) {
+  sourceFrame._textEditor.undo();
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SearchTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SearchTestRunner.js
new file mode 100644
index 0000000..588523a
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SearchTestRunner.js
@@ -0,0 +1,148 @@
+// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+SourcesTestRunner.dumpSearchResults = function(searchResults) {
+  function comparator(a, b) {
+    a.url.localeCompare(b.url);
+  }
+
+  searchResults.sort(comparator);
+  TestRunner.addResult('Search results: ');
+
+  for (var i = 0; i < searchResults.length; i++) {
+    TestRunner.addResult(
+        'url: ' + searchResults[i].url.replace(/VM\d+/, 'VMXX') + ', matchesCount: ' + searchResults[i].matchesCount);
+  }
+
+  TestRunner.addResult('');
+};
+
+SourcesTestRunner.dumpSearchMatches = function(searchMatches) {
+  TestRunner.addResult('Search matches: ');
+
+  for (var i = 0; i < searchMatches.length; i++) {
+    TestRunner.addResult(
+        'lineNumber: ' + searchMatches[i].lineNumber + ', line: \'' + searchMatches[i].lineContent + '\'');
+  }
+
+  TestRunner.addResult('');
+};
+
+SourcesTestRunner.runSearchAndDumpResults = function(scope, searchConfig, callback) {
+  var searchResults = [];
+  var progress = new Common.Progress();
+  scope.performSearch(searchConfig, progress, searchResultCallback, searchFinishedCallback);
+
+  function searchResultCallback(searchResult) {
+    searchResults.push(searchResult);
+  }
+
+  function searchFinishedCallback() {
+    function comparator(searchResultA, searchResultB) {
+      return searchResultA.uiSourceCode.url().compareTo(searchResultB.uiSourceCode.url());
+    }
+
+    searchResults.sort(comparator);
+
+    for (var i = 0; i < searchResults.length; ++i) {
+      var searchResult = searchResults[i];
+      var uiSourceCode = searchResult.uiSourceCode;
+      var searchMatches = searchResult.searchMatches;
+
+      if (!searchMatches.length)
+        continue;
+
+      TestRunner.addResult(
+          'Search result #' + (i + 1) + ': uiSourceCode.url = ' + uiSourceCode.url().replace(/VM\d+/, 'VMXX'));
+
+      for (var j = 0; j < searchMatches.length; ++j) {
+        var lineNumber = searchMatches[j].lineNumber;
+        var lineContent = searchMatches[j].lineContent;
+        TestRunner.addResult(
+            '  search match #' + (j + 1) + ': lineNumber = ' + lineNumber + ', lineContent = \'' + lineContent + '\'');
+      }
+    }
+
+    callback();
+  }
+};
+
+SourcesTestRunner.replaceAndDumpChange = function(sourceFrame, searchConfig, replacement, replaceAll) {
+  var modifiers = [];
+
+  if (searchConfig.isRegex)
+    modifiers.push('regex');
+
+  if (searchConfig.caseSensitive)
+    modifiers.push('caseSensitive');
+
+  if (replaceAll)
+    modifiers.push('replaceAll');
+
+  var modifiersString = (modifiers.length ? ' (' + modifiers.join(', ') + ')' : '');
+  TestRunner.addResult(
+      'Running replace test for /' + searchConfig.query + '/' + replacement + '/ ' + modifiersString + ':');
+  editor = sourceFrame._textEditor;
+  var oldLines = [];
+
+  for (var i = 0; i < editor.linesCount; ++i)
+    oldLines.push(editor.line(i));
+
+  var searchableView = UI.panels.sources.sourcesView().searchableView();
+  searchableView.showSearchField();
+  searchableView._caseSensitiveButton.setToggled(searchConfig.caseSensitive);
+  searchableView._regexButton.setToggled(searchConfig.isRegex);
+  searchableView._searchInputElement.value = searchConfig.query;
+  searchableView._replaceCheckboxElement.checked = true;
+  searchableView._updateSecondRowVisibility();
+  searchableView._replaceInputElement.value = replacement;
+  searchableView._performSearch(true, true);
+
+  if (replaceAll)
+    searchableView._replaceAll();
+  else
+    searchableView._replace();
+
+  var newLines = [];
+
+  for (var i = 0; i < editor.linesCount; ++i)
+    newLines.push(editor.line(i));
+
+  for (var i = 0; i < newLines.length; ++i) {
+    if (oldLines[i] === newLines[i])
+      continue;
+
+    var oldLine = oldLines[i];
+    var newLine = newLines[i];
+    var prefixLength = 0;
+
+    for (var j = 0; j < oldLine.length && j < newLine.length && newLine[j] === oldLine[j]; ++j)
+      ++prefixLength;
+
+    var postfixLength = 0;
+
+    for (var j = 0; j < oldLine.length && j < newLine.length &&
+         newLine[newLine.length - j - 1] === oldLine[oldLine.length - j - 1];
+         ++j)
+      ++postfixLength;
+
+    var prefix = oldLine.substring(0, prefixLength);
+    var removed = oldLine.substring(prefixLength, oldLine.length - postfixLength);
+    var added = newLine.substring(prefixLength, newLine.length - postfixLength);
+    var postfix = oldLine.substring(oldLine.length - postfixLength);
+    TestRunner.addResult('  - ' + prefix + '#' + removed + '#' + added + '#' + postfix);
+  }
+};
+
+(async function() {
+  await TestRunner.evaluateInPagePromise(`
+    if (window.GCController)
+      GCController.collect();
+  `);
+})();
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SourcesTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SourcesTestRunner.js
index ed36909..4b28e95 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SourcesTestRunner.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SourcesTestRunner.js
@@ -74,41 +74,6 @@
 };
 
 /**
- * @param {string} urlSuffix
- * @param {!Workspace.projectTypes=} projectType
- * @return {!Promise}
- */
-SourcesTestRunner.waitForUISourceCode = function(urlSuffix, projectType) {
-  /**
-   * @param {!Workspace.UISourceCode} uiSourceCode
-   * @return {boolean}
-   */
-  function matches(uiSourceCode) {
-    if (projectType && uiSourceCode.project().type() !== projectType)
-      return false;
-    if (!projectType && uiSourceCode.project().type() === Workspace.projectTypes.Service)
-      return false;
-    if (urlSuffix && !uiSourceCode.url().endsWith(urlSuffix))
-      return false;
-    return true;
-  }
-
-  for (var uiSourceCode of Workspace.workspace.uiSourceCodes()) {
-    if (urlSuffix && matches(uiSourceCode))
-      return Promise.resolve(uiSourceCode);
-  }
-
-  return TestRunner.waitForEvent(Workspace.Workspace.Events.UISourceCodeAdded, Workspace.workspace, matches);
-};
-
-/**
- * @param {!Function} callback
- */
-SourcesTestRunner.waitForUISourceCodeRemoved = function(callback) {
-  Workspace.workspace.once(Workspace.Workspace.Events.UISourceCodeRemoved).then(callback);
-};
-
-/**
  * @param {string} url
  * @param {string} content
  * @param {boolean=} isContentScript
@@ -120,5 +85,70 @@
   if (isContentScript)
     content = `testRunner.evaluateScriptInIsolatedWorld(${worldId}, \`${content}\`)`;
   TestRunner.evaluateInPagePromise(content);
-  return SourcesTestRunner.waitForUISourceCode(url);
+  return TestRunner.waitForUISourceCode(url);
+};
+
+function testSourceMapping(text1, text2, mapping, testToken) {
+  var originalPosition = text1.indexOf(testToken);
+  TestRunner.assertTrue(originalPosition !== -1);
+  var originalLocation = Formatter.Formatter.positionToLocation(text1.computeLineEndings(), originalPosition);
+  var formattedLocation = mapping.originalToFormatted(originalLocation[0], originalLocation[1]);
+  var formattedPosition =
+      Formatter.Formatter.locationToPosition(text2.computeLineEndings(), formattedLocation[0], formattedLocation[1]);
+  var expectedFormattedPosition = text2.indexOf(testToken);
+
+  if (expectedFormattedPosition === formattedPosition)
+    TestRunner.addResult(String.sprintf('Correct mapping for <%s>', testToken));
+  else
+    TestRunner.addResult(String.sprintf('ERROR: Wrong mapping for <%s>', testToken));
+}
+
+SourcesTestRunner.testPrettyPrint = function(mimeType, text, mappingQueries, next) {
+  new Formatter.ScriptFormatter(mimeType, text, didFormatContent);
+
+  function didFormatContent(formattedSource, mapping) {
+    TestRunner.addResult('====== 8< ------');
+    TestRunner.addResult(formattedSource);
+    TestRunner.addResult('------ >8 ======');
+
+    while (mappingQueries && mappingQueries.length)
+      testSourceMapping(text, formattedSource, mapping, mappingQueries.shift());
+
+    next();
+  }
+};
+
+SourcesTestRunner.testJavascriptOutline = function(text) {
+  var fulfill;
+  var promise = new Promise(x => fulfill = x);
+  Formatter.formatterWorkerPool().javaScriptOutline(text, onChunk);
+  var items = [];
+  return promise;
+
+  function onChunk(isLastChunk, outlineItems) {
+    items.pushAll(outlineItems);
+
+    if (!isLastChunk)
+      return;
+
+    TestRunner.addResult('Text:');
+    TestRunner.addResult(text.split('\n').map(line => '    ' + line).join('\n'));
+    TestRunner.addResult('Outline:');
+
+    for (var item of items)
+      TestRunner.addResult('    ' + item.name + (item.arguments || '') + ':' + item.line + ':' + item.column);
+
+    fulfill();
+  }
+};
+
+SourcesTestRunner.dumpSwatchPositions = function(sourceFrame, bookmarkType) {
+  var textEditor = sourceFrame.textEditor;
+  var markers = textEditor.bookmarks(textEditor.fullRange(), bookmarkType);
+
+  for (var i = 0; i < markers.length; i++) {
+    var position = markers[i].position();
+    var text = markers[i]._marker.widgetNode.firstChild.textContent;
+    TestRunner.addResult('Line ' + position.startLine + ', Column ' + position.startColumn + ': ' + text);
+  }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/module.json b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/module.json
index 147271d..b9ceb99 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/module.json
@@ -3,9 +3,26 @@
     "test_runner",
     "integration_test_runner",
     "sources",
-    "workspace"
+    "workspace",
+    "source_frame",
+    "text_utils"
   ],
   "scripts": [
-    "SourcesTestRunner.js"
+    "SourcesTestRunner.js",
+    "DebuggerTestRunner.js",
+    "LiveEditTestRunner.js",
+    "SearchTestRunner.js",
+    "EditorTestRunner.js",
+    "AutocompleteTestRunner.js",
+    "BreakpointManagerTestRunner.js"
+  ],
+  "skip_compilation": [
+    "DebuggerTestRunner.js",
+    "LiveEditTestRunner.js",
+    "SearchTestRunner.js",
+    "SourcesTestRunner.js",
+    "EditorTestRunner.js",
+    "AutocompleteTestRunner.js",
+    "BreakpointManagerTestRunner.js"
   ]
-}
\ No newline at end of file
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/test_runner/TestRunner.js b/third_party/WebKit/Source/devtools/front_end/test_runner/TestRunner.js
index b03973c..d394f2d 100644
--- a/third_party/WebKit/Source/devtools/front_end/test_runner/TestRunner.js
+++ b/third_party/WebKit/Source/devtools/front_end/test_runner/TestRunner.js
@@ -11,7 +11,15 @@
   const testScriptURL = /** @type {string} */ (Runtime.queryParam('test'));
   fetch(testScriptURL)
       .then(data => data.text())
-      .then(testScript => eval(`(function test(){${testScript}})()\n//# sourceURL=${testScriptURL}`))
+      .then(testScript => {
+        if (!self.testRunner || Runtime.queryParam('debugFrontend')) {
+          self.eval(`function test(){${testScript}}\n//# sourceURL=${testScriptURL}`);
+          TestRunner.addResult = console.log;
+          TestRunner.completeTest = () => console.log('Test completed');
+          return;
+        }
+        eval(`(function test(){${testScript}})()\n//# sourceURL=${testScriptURL}`);
+      })
       .catch(error => {
         TestRunner.addResult(`Unable to execute test script because of error: ${error}`);
         TestRunner.completeTest();
@@ -22,10 +30,6 @@
 TestRunner._results = [];
 
 TestRunner.completeTest = function() {
-  if (!self.testRunner) {
-    console.log('Test Done');
-    return;
-  }
   TestRunner.flushResults();
   self.testRunner.notifyDone();
 };
@@ -54,10 +58,7 @@
  * @param {*} text
  */
 TestRunner.addResult = function(text) {
-  if (self.testRunner)
-    TestRunner._results.push(String(text));
-  else
-    console.log(text);
+  TestRunner._results.push(String(text));
 };
 
 /**
@@ -270,17 +271,6 @@
   return buffer;
 };
 
-/**
- * @param {!Function} testFunction
- * @return {!Function}
- */
-function debugTest(testFunction) {
-  self.test = testFunction;
-  TestRunner.addResult = console.log;
-  TestRunner.completeTest = () => console.log('Test completed');
-  return () => {};
-}
-
 (function() {
   /**
    * @param {string|!Event} message
diff --git a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
index 5a783d4..e1d82786 100755
--- a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
+++ b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
@@ -258,6 +258,8 @@
 def check_conditional_dependencies(modules_by_name):
     errors_found = False
     for name in modules_by_name:
+        if 'test_runner' in name:
+            continue
         for dep_name in modules_by_name[name].get('dependencies', []):
             dependency = modules_by_name[dep_name]
             if dependency.get('experiment') or dependency.get('condition'):
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/glob.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/glob.js
new file mode 100644
index 0000000..c5ea1b9
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/glob.js
@@ -0,0 +1,45 @@
+// 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.
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+
+const utils = require('../utils');
+
+const TESTS_PATH = path.resolve(__dirname, 'tests.txt');
+
+function main() {
+  const files = process.argv.slice(2);
+  const inputPaths = files.map(p => path.isAbsolute(p) ? p : path.resolve(process.cwd(), p));
+  let globbedPaths = [];
+  for (const p of inputPaths) {
+    glob(p);
+  }
+  let contents = fs.readFileSync(TESTS_PATH, 'utf-8');
+  const tests = new Set(contents.split('\n').map(l => l.split(' ')[0]));
+  for (const p of globbedPaths) {
+    const relativePath = p.slice(p.indexOf('LayoutTests') + 'LayoutTests'.length + 1);
+    if (!tests.has(relativePath))
+      contents += relativePath + '\n'
+  }
+  console.log('contents', contents);
+  fs.writeFileSync(TESTS_PATH, contents, 'utf-8');
+
+
+  function glob(globPath) {
+    for (const filename of fs.readdirSync(globPath)) {
+      const p = path.resolve(globPath, filename);
+      if (utils.isDir(p) && filename !== 'resources') {
+        glob(p);
+      }
+      if (utils.isFile(p) && p.endsWith('.html')) {
+        globbedPaths.push(p);
+      }
+    }
+  }
+}
+
+main();
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_helpers.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_helpers.js
new file mode 100644
index 0000000..4fe0ca9
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_helpers.js
@@ -0,0 +1,519 @@
+// 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.
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+
+const recast = require('recast');
+const types = recast.types;
+const b = recast.types.builders;
+
+const utils = require('../utils');
+const migrateUtils = require('./migrate_utils');
+/**
+ * propertyToNamespace {[testProperty:string]: {namespace: string, done: boolean}}
+ * Moved file
+ * {ETR.something: ETR}
+ * Existing helper module
+ * {ConsoleTestRunner.something: CTR}
+ */
+const BUILD_GN_PATH = path.resolve(__dirname, '..', '..', 'BUILD.gn');
+const DRY_RUN = process.env.DRY_RUN || false;
+const FRONT_END_PATH = path.resolve(__dirname, '..', '..', 'front_end');
+
+function main() {
+  const helperPaths = scanHelpers();
+  const helpers = mapTestFilesToHelperModules(helperPaths);
+  transformHelperCode(helpers);
+  if (DRY_RUN) {
+    return;
+  }
+  updateModuleDescriptors(helpers);
+  const newModules = new Set();
+  const helperFiles = [];
+  for (const h of helpers) {
+    newModules.add(h.helperModule);
+    helperFiles.push(h.newPath);
+  }
+  const existingModulesJson = require(path.resolve(FRONT_END_PATH, 'integration_test_runner.json'));
+  for (const module of existingModulesJson.modules) {
+    newModules.delete(module.name)
+  }
+  updateApplicationDescriptor('integration_test_runner.json', newModules);
+  updateBuildGN(newModules, helperFiles);
+  writeNewHelpers(helpers);
+}
+
+function updateBuildGN(newModuleSet, helperFiles) {
+  let content = fs.readFileSync(BUILD_GN_PATH).toString();
+  helperFiles =
+      helperFiles.map(p => p.split('/').slice(-3).join('/')).filter(p => content.indexOf(p) === -1).map(p => `"${p}",`);
+
+  let newContent = addContentToLinesInSortedOrder({
+    content,
+    startLine: 'all_devtools_files = [',
+    endLine: ']',
+    linesToInsert:
+        [...newModuleSet].map(module => `"front_end/${module}/module.json",`).filter(p => content.indexOf(p) === -1),
+  });
+
+  newContent = addContentToLinesInSortedOrder({
+    content: newContent,
+    startLine: 'all_devtools_files = [',
+    endLine: ']',
+    linesToInsert: helperFiles,
+  });
+
+  fs.writeFileSync(BUILD_GN_PATH, newContent);
+
+  function top(array) {
+    return array[array.length - 1];
+  }
+
+  function addContentToLinesInSortedOrder({content, startLine, endLine, linesToInsert}) {
+    if (linesToInsert.length === 0)
+      return content;
+    let lines = content.split('\n');
+    let seenStartLine = false;
+    let contentStack = linesToInsert.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).reverse();
+    for (var i = 0; i < lines.length; i++) {
+      let line = lines[i].trim();
+      let nextLine = lines[i + 1].trim();
+      if (line === startLine)
+        seenStartLine = true;
+
+      if (line === endLine && seenStartLine)
+        break;
+
+      if (!seenStartLine)
+        continue;
+
+      const nextContent = top(contentStack) ? top(contentStack).toLowerCase() : '';
+      if ((line === startLine || nextContent >= line.toLowerCase()) &&
+          (nextLine === endLine || nextContent <= nextLine.toLowerCase()))
+        lines.splice(i + 1, 0, contentStack.pop());
+    }
+    if (contentStack.length)
+      lines.splice(i, 0, ...contentStack);
+    return lines.join('\n');
+  }
+}
+
+function updateApplicationDescriptor(descriptorFileName, newModuleSet) {
+  let descriptorPath = path.join(FRONT_END_PATH, descriptorFileName);
+  let newModules = [...newModuleSet];
+  if (newModules.length === 0)
+    return;
+  let includeNewModules = (acc, line) => {
+    if (line === '  "modules" : [') {
+      acc.push(line);
+      return acc.concat(newModules.map((m, i) => {
+        // Need spacing to preserve indentation
+        let string;
+        string = `    { "name": "${m}" },`;
+        return string;
+      }));
+    }
+    return acc.concat([line]);
+  };
+  let lines = fs.readFileSync(descriptorPath).toString().split('\n').reduce(includeNewModules, []);
+  fs.writeFileSync(descriptorPath, lines.join('\n'));
+}
+
+function updateModuleDescriptors(helpers) {
+  const dependenciesByModule = {
+    application_test_runner: ['resources', 'console_test_runner', 'sources', 'sources_test_runner'],
+    audits_test_runner: ['audits'],
+    bindings_test_runner: ['workspace', 'diff', 'bindings', 'persistence'],
+    coverage_test_runner: ['coverage', 'sources_test_runner'],
+    device_mode_test_runner: ['emulation'],
+    elements_test_runner: ['animation'],
+    accessibility_test_runner: ['accessibility', 'elements_test_runner'],
+    extensions_test_runner: ['extensions'],
+    layers_test_runner: ['layers', 'components'],
+    network_test_runner: ['product_registry_impl', 'console_test_runner'],
+    performance_test_runner: ['timeline_model', 'timeline'],
+    profiler_test_runner: ['profiler', 'data_grid', 'heap_snapshot_worker'],
+    security_test_runner: ['security'],
+    sources_test_runner: ['source_frame', 'text_utils'],
+    sass_test_runner: ['sass'],
+  };
+
+  for (const helper of helpers) {
+    const parentPath = path.dirname(helper.newPath);
+    if (!utils.isDir(parentPath))
+      fs.mkdirSync(parentPath);
+    const modulePath = path.resolve(parentPath, 'module.json');
+    const additionalDependencies = dependenciesByModule[helper.helperModule] || [];
+    let contents = {
+      dependencies: ['test_runner', 'integration_test_runner'],
+      scripts: [],
+    };
+    if (utils.isFile(modulePath)) {
+      contents = JSON.parse(fs.readFileSync(modulePath, 'utf-8'));
+    }
+    contents.dependencies = unique(contents.dependencies.concat(additionalDependencies));
+    const filename = path.basename(helper.newPath);
+    if (contents.scripts.indexOf(filename) === -1)
+      contents.scripts.push(filename);
+    contents.skip_compilation = contents.skip_compilation || [];
+    contents.skip_compilation.push(filename);
+    fs.writeFileSync(modulePath, stringifyJSON(contents));
+  }
+
+  function unique(array) {
+    const set = new Set();
+    for (const el of array)
+      set.add(el);
+    return Array.from(set);
+  }
+
+  function stringifyJSON(obj) {
+    return unicodeEscape(JSON.stringify(obj, null, 2) + '\n');
+  }
+
+  // http://stackoverflow.com/questions/7499473/need-to-escape-non-ascii-characters-in-javascript
+  function unicodeEscape(string) {
+    function padWithLeadingZeros(string) {
+      return new Array(5 - string.length).join('0') + string;
+    }
+
+    function unicodeCharEscape(charCode) {
+      return '\\u' + padWithLeadingZeros(charCode.toString(16));
+    }
+
+    return string.split('')
+        .map(function(char) {
+          var charCode = char.charCodeAt(0);
+          return charCode > 127 ? unicodeCharEscape(charCode) : char;
+        })
+        .join('');
+  }
+}
+
+function transformHelperCode(helpers) {
+  const boilerplate = `// Copyright 2017 The Chromium Authors. All
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview using private properties isn't a Closure violation in tests.
+ * @suppress {accessControls}
+ */
+
+`;
+
+  const propertyToNamespace = scrapeTestProperties(helpers);
+
+  for (const helper of helpers) {
+    const ast = recast.parse(helper.originalContents);
+    unwrapInitializeFunction(ast);
+
+    recast.visit(ast, {
+      /**
+     * Remove all deprecated API calls
+     */
+      visitIdentifier: function(path) {
+        if (path.parentPath && path.parentPath.value && path.parentPath.value.object &&
+            path.parentPath.value.object.name === 'InspectorTest' && path.value.name !== 'InspectorTest') {
+          const identifier = path.value.name;
+          if (identifier === 'preloadPanel' || identifier === 'preloadModule') {
+            console.log('helper: ', helper.originalPath, recast.print(path.parentPath.parentPath.value).code)
+            //  Removes these types of calls: InspectorTest.preloadPanel("elements");
+            path.parentPath.parentPath.prune();
+          }
+        }
+        return false;
+      },
+      /**
+     * Skip already ported API calls
+     */
+      visitAssignmentExpression: function(path) {
+        if (!path.value.left.object) {
+          return false;
+        }
+        const namespace = path.value.left.object.name;
+        const propertyName = path.value.left.property.name;
+        if (namespace === 'InspectorTest' && propertyToNamespace.get(propertyName).done) {
+          // console.log('pruning', propertyName);
+          path.prune();
+        }
+        this.traverse(path);
+      },
+    });
+
+    /**
+   * Migrate all the call sites from InspectorTest to .*TestRunner
+   */
+    recast.visit(ast, {
+      visitIdentifier: function(path) {
+        if (path.parentPath && path.parentPath.value && path.parentPath.value.object &&
+            path.parentPath.value.object.name === 'InspectorTest' && path.value.name !== 'InspectorTest') {
+          if (!propertyToNamespace.get(path.value.name)) {
+            throw new Error('Could not find identifier for: ' + path.value.name);
+          }
+          const newParentIdentifier = propertyToNamespace.get(path.value.name).namespace;
+          path.parentPath.value.object.name = newParentIdentifier;
+        }
+        return false;
+      }
+    });
+
+    /**
+   * Migrate all the .bind call sites
+   * Example: SourcesTestRunner.waitUntilPaused.bind(InspectorTest, didPause)
+   */
+    recast.visit(ast, {
+      visitCallExpression: function(path) {
+        const node = path.value;
+        if (node.callee.property && node.callee.property.name === 'bind') {
+          const code = recast.prettyPrint(node);
+          if (node.arguments[0].name === 'InspectorTest') {
+            node.arguments[0].name = node.callee.object.object.name;
+          }
+        }
+        this.traverse(path);
+      }
+    });
+
+
+    const existingContents = utils.isFile(helper.newPath) ? fs.readFileSync(helper.newPath) : '';
+    if (existingContents)
+      helper.contents = existingContents + '\n' + print(ast);
+    else
+      helper.contents = boilerplate + print(ast);
+  }
+
+  function unwrapInitializeFunction(ast) {
+    // Handle function expression
+    let index = ast.program.body.findIndex(
+        n => n.type === 'VariableDeclaration' && n.declarations[0].id.name.indexOf('initialize_') !== -1);
+    if (index > -1) {
+      const otherNodes = ast.program.body.filter((_, i) => i !== index);
+      const testFunctionNode = ast.program.body[index];
+      ast.program.body = testFunctionNode.declarations[0].init.body.body;
+      inspectedPageNodes(otherNodes);
+      return;
+    }
+
+    // Handle function declaration
+    index =
+        ast.program.body.findIndex(n => n.type === 'FunctionDeclaration' && n.id.name.indexOf('initialize_') !== -1);
+    if (index > -1) {
+      const otherNodes = ast.program.body.filter((_, i) => i !== index);
+      const testFunctionNode = ast.program.body[index];
+      ast.program.body.splice(index, 1);
+      ast.program.body = testFunctionNode.body.body;
+      inspectedPageNodes(otherNodes);
+    }
+
+    function inspectedPageNodes(otherNodes) {
+      if (!otherNodes.length)
+        return;
+      const code = otherNodes.map(node => print(node)).join('\n').slice(0, -1);
+      ast.program.body.push(createAwaitExpressionNode(`await TestRunner.evaluateInPagePromise(\`
+${code.split('\n').map(x => x.length ? '    ' + x : x).join('\n')}
+  \`)`));
+    }
+  }
+}
+
+function print(ast) {
+  /**
+   * Not using clang-format because certain tests look bad when formatted by it.
+   * Recast pretty print is smarter about preserving existing spacing.
+   */
+  let code = recast.prettyPrint(ast, {tabWidth: 2, wrapColumn: 120, quote: 'single'}).code;
+  code = code.replace(/(\/\/\#\s*sourceURL=[\w-]+)\.html/, '$1.js');
+  code = code.replace(/\s*\$\$SECRET_IDENTIFIER_FOR_LINE_BREAK\$\$\(\);/g, '\n');
+  return code + '\n';
+}
+
+function scrapeTestProperties(helpers) {
+  const propertyToNamespace = new Map();
+  scrapeOriginalHelpers(propertyToNamespace, helpers);
+  scrapeMovedHelpers(propertyToNamespace);
+
+  // Manual overrides
+  propertyToNamespace.set('consoleModel', {
+    namespace: 'ConsoleModel',
+    done: true,
+  });
+  propertyToNamespace.set('networkLog', {
+    namespace: 'NetworkLog',
+    done: true,
+  });
+  return propertyToNamespace;
+}
+
+function scrapeMovedHelpers(propertyToNamespace) {
+  const testRunnerPaths = fs.readdirSync(FRONT_END_PATH)
+                              .filter(folder => folder.indexOf('test_runner') !== -1)
+                              .map(folder => path.resolve(FRONT_END_PATH, folder))
+                              .filter(file => utils.isDir(file));
+
+  testRunnerPaths.forEach((helperPath) => {
+    const files = fs.readdirSync(helperPath)
+                      .filter(file => file.indexOf('TestRunner') !== -1)
+                      .map(file => path.resolve(helperPath, file));
+    files.forEach(file => scrapeTestHelperIdentifiers(file));
+  });
+
+  function scrapeTestHelperIdentifiers(filePath) {
+    var content = fs.readFileSync(filePath).toString();
+    var lines = content.split('\n');
+    for (var line of lines) {
+      var line = line.trim();
+      if (line.indexOf('TestRunner.') === -1)
+        continue;
+      var match = line.match(/^\s*(\b\w*TestRunner.[a-z_A-Z0-9]+)\s*(\=[^,}]|[;])/) ||
+          line.match(/^(TestRunner.[a-z_A-Z0-9]+)\s*\=$/);
+      if (!match)
+        continue;
+      var name = match[1];
+      var components = name.split('.');
+      if (components.length !== 2)
+        continue;
+      propertyToNamespace.set(components[1], {
+        namespace: components[0],
+        done: true,
+      });
+    }
+  }
+}
+
+function scrapeOriginalHelpers(propertyToNamespace, helpers) {
+  const testRunnerPaths = fs.readdirSync(FRONT_END_PATH)
+                              .filter(folder => folder.indexOf('test_runner') !== -1)
+                              .map(folder => path.resolve(FRONT_END_PATH, folder));
+
+  for (const helper of helpers) {
+    scrapeTestHelperIdentifiers(helper.originalContents, helper.namespace);
+  }
+
+  function scrapeTestHelperIdentifiers(content, namespace) {
+    var lines = content.split('\n');
+    for (var line of lines) {
+      var line = line.trim();
+      if (line.indexOf('InspectorTest.') === -1)
+        continue;
+      var match = line.match(/^\s*(\b\w*InspectorTest.[a-z_A-Z0-9]+)\s*(\=[^,}]|[;])/) ||
+          line.match(/^(InspectorTest.[a-z_A-Z0-9]+)\s*\=$/);
+      if (!match)
+        continue;
+      var name = match[1];
+      var components = name.split('.');
+      if (components.length !== 2)
+        continue;
+      propertyToNamespace.set(components[1], {
+        namespace: namespace,
+        done: false,
+      });
+    }
+  }
+}
+
+// Causes too much diff in test expectations
+// function appendComment(helpers) {
+//   for (const {originalPath, originalContents, helperModule, filename} of helpers) {
+//     const comment = `// This file is being deprecated and is moving to front_end/${helperModule}/${filename}
+// // Please see crbug.com/667560 for more details\n\n`;
+//     fs.writeFileSync(originalPath, comment + originalContents);
+//   }
+// }
+
+function writeNewHelpers(helpers) {
+  for (const {contents, helperModule, filename} of helpers) {
+    const modulePath = path.resolve(FRONT_END_PATH, helperModule);
+    if (!utils.isDir(modulePath))
+      fs.mkdirSync(modulePath);
+    const destPath = path.resolve(modulePath, filename);
+    fs.writeFileSync(destPath, contents);
+  }
+}
+
+function mapTestFilesToHelperModules(helperPaths) {
+  const helpers = new Set();
+  for (const p of helperPaths) {
+    const inputFilename = path.basename(p);
+    let namespacePrefix = path.basename(p)
+                              .split('-test')[0]
+                              .split('-')
+                              .map(a => a.substring(0, 1).toUpperCase() + a.substring(1))
+                              .join('');
+    let filenamePrefix = namespacePrefix;
+
+    // Already migrated or n/a
+    if (namespacePrefix === 'Inspector' || namespacePrefix === 'Console' || namespacePrefix === 'Protocol' ||
+        namespacePrefix === 'ExampleFilesetFor') {
+      continue;
+    }
+
+    // Needs to be manually migrated
+    if (namespacePrefix === 'CspInline' || namespacePrefix === 'Stacktrace')
+      continue;
+
+    const res = migrateUtils.mapTestFilename(inputFilename);
+    namespacePrefix = res.namespacePrefix;
+    filenamePrefix = res.filenamePrefix;
+
+    const contents = fs.readFileSync(p, 'utf-8');
+    const namespace = namespacePrefix + 'TestRunner';
+    const helperModule = namespacePrefix === 'SASS' ?
+        'sass_test_runner' :
+        namespacePrefix === '' ? 'integration_test_runner' :
+                                 namespace.replace(/([A-Z])/g, '_$1').replace(/^_/, '').toLowerCase();
+    const filename = filenamePrefix + 'TestRunner.js';
+    helpers.add({
+      originalPath: p,
+      newPath: path.resolve(FRONT_END_PATH, helperModule, filename),
+      namespace,
+      helperModule,
+      filename,
+      originalContents: contents,
+      contents,
+    });
+  }
+  return helpers;
+}
+
+main();
+
+function scanHelpers() {
+  const paths = [];
+
+  const http_root = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', 'http', 'tests', 'inspector');
+  const non_http_root = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', 'inspector');
+  scan(http_root);
+  scan(non_http_root);
+
+  function scan(p) {
+    const files = fs.readdirSync(p).map(file => path.resolve(p, file));
+    for (const file of files) {
+      if (utils.isDir(file))
+        scan(file);
+      else if (
+          file.indexOf('-test.js') !== -1 || file.indexOf('syntax-highlight.js') !== -1 ||
+          file.indexOf('timeline-data.js') !== -1 || file.indexOf('styles-update-links.js') !== -1 ||
+          file.indexOf('breakpoint-manager.js') !== -1 ||
+          file.indexOf('elements-panel-shadow-selection-on-refresh.js') !== -1 || file.indexOf('page-mock.js') !== -1)
+        paths.push(file);
+    }
+  }
+
+  return paths;
+}
+
+/**
+ * Hack to quickly create an AST node
+ */
+function createAwaitExpressionNode(code) {
+  return recast
+      .parse(`(async function(){
+  ${code}
+  })()`)
+      .program.body[0];
+}
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js
index f9face9..99774b7 100644
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_test.js
@@ -9,6 +9,7 @@
 
 const cheerio = require('cheerio');
 const mkdirp = require('mkdirp');
+const prettier = require('prettier');
 const recast = require('recast');
 const types = recast.types;
 const b = recast.types.builders;
@@ -24,6 +25,7 @@
   const files = process.argv.slice(2);
   const inputPaths = files.map(p => path.isAbsolute(p) ? p : path.resolve(process.cwd(), p));
   const identifierMap = generateTestHelperMap();
+
   for (const inputPath of inputPaths) {
     migrateTest(inputPath, identifierMap);
   }
@@ -36,11 +38,12 @@
   console.log('Starting to migrate: ', inputPath);
   const htmlTestFile = fs.readFileSync(inputPath, 'utf-8');
   const $ = cheerio.load(htmlTestFile);
+  const onloadFunctionName = $('body')[0].attribs.onload ? $('body')[0].attribs.onload.slice(0, -2) : '';
   const javascriptFixtures = [];
   const inputCode = $('script:not([src])')
                         .toArray()
                         .map(n => n.children[0].data)
-                        .map(code => processScriptCode(code, javascriptFixtures))
+                        .map(code => processScriptCode(code, javascriptFixtures, onloadFunctionName))
                         .filter(x => !!x)
                         .join('\n');
   const bodyText = $('body').text().trim();
@@ -56,12 +59,6 @@
     helperScripts.push(filename);
   });
 
-  const testsPath = path.resolve(__dirname, 'tests.txt');
-  const newToOldTests = new Map(fs.readFileSync(testsPath, 'utf-8').split('\n').map(line => line.split(' ').reverse()));
-  const originalTestPath = path.resolve(
-      __dirname, '..', '..', '..', '..', 'LayoutTests', newToOldTests.get(inputPath.slice(inputPath.indexOf('http/'))));
-
-  const srcResourcePaths = resourceScripts.map(s => path.resolve(path.dirname(originalTestPath), s));
   const destResourcePaths = resourceScripts.map(s => path.resolve(path.dirname(inputPath), s));
   const relativeResourcePaths = destResourcePaths.map(p => p.slice(p.indexOf('/http/tests') + '/http/tests'.length));
 
@@ -74,14 +71,17 @@
                          // Tries to remove it if it has it's own line
                          .replace(bodyText + '\n', '')
                          // Tries to remove it if it's inline
-                         .replace(bodyText, '');
-    if (/<p>\s*<\/p>/.test(domFixture)) {
-      domFixture = undefined;
-    }
-    const onloadFunctionName = $('body')[0].attribs.onload.slice(0, -2);
+                         .replace(bodyText, '')
+                         .replace(/<p>\s*<\/p>/, '')
+                         .replace(/<div>\s*<\/div>/, '')
+                         .trim();
+    const docType = htmlTestFile.match(/<!DOCTYPE.*>/) ? htmlTestFile.match(/<!DOCTYPE.*>/)[0] : '';
+    if (docType)
+      domFixture = docType + (domFixture.length ? '\n' : '') + domFixture;
     outputCode = transformTestScript(
         inputCode, bodyText, identifierMap, testHelpers, javascriptFixtures, getPanel(inputPath), domFixture,
         onloadFunctionName, relativeResourcePaths);
+    outputCode = prettier.format(outputCode, {tabWidth: 2, printWidth: 120, singleQuote: true});
   } catch (err) {
     console.log('Unable to migrate: ', inputPath);
     console.log('ERROR: ', err);
@@ -90,13 +90,22 @@
 
   console.log(outputCode);
   if (!DRY_RUN) {
+    const testsPath = path.resolve(__dirname, 'tests.txt');
+    const newToOldTests =
+        new Map(fs.readFileSync(testsPath, 'utf-8').split('\n').map(line => line.split(' ').reverse()));
+    const originalTestPath = path.resolve(
+        __dirname, '..', '..', '..', '..', 'LayoutTests',
+        newToOldTests.get(inputPath.slice(inputPath.indexOf('http/'))));
+
+    const srcResourcePaths = resourceScripts.map(s => path.resolve(path.dirname(originalTestPath), s));
+
     fs.writeFileSync(inputPath, outputCode);
-    copyResourceScripts(srcResourcePaths, destResourcePaths);
+    copyResourceScripts(srcResourcePaths, destResourcePaths, inputPath);
     console.log('Migrated: ', inputPath);
   }
 }
 
-function copyResourceScripts(srcResourcePaths, destResourcePaths) {
+function copyResourceScripts(srcResourcePaths, destResourcePaths, inputPath) {
   destResourcePaths.forEach((p, i) => {
     mkdirp.sync(path.dirname(p));
     if (!utils.isFile(p)) {
@@ -131,8 +140,8 @@
     nonTestNodes.push(node);
   }
 
-  unwrapTestFunctionExpressionIfNecessary(ast);
-  unwrapTestFunctionDeclarationIfNecessary(ast);
+  unwrapFunctionExpression(ast, 'test');
+  unwrapFunctionDeclaration(ast, 'test');
 
 
   /**
@@ -159,6 +168,24 @@
     }
   });
 
+  /**
+   * Migrate all the .bind call sites
+   * Example: SourcesTestRunner.waitUntilPaused.bind(InspectorTest, didPause)
+   */
+  recast.visit(ast, {
+    visitCallExpression: function(path) {
+      const node = path.value;
+      if (node.callee.property && node.callee.property.name === 'bind') {
+        const code = recast.prettyPrint(node);
+        if (node.arguments[0].name === 'InspectorTest') {
+          node.arguments[0].name = node.callee.object.object.name;
+        }
+      }
+      this.traverse(path);
+    }
+  });
+
+
   const allTestHelpers = new Set();
 
   for (const helper of explicitTestHelpers) {
@@ -181,7 +208,8 @@
   for (const helper of allTestHelpers) {
     headerLines.push(createAwaitExpressionNode(`await TestRunner.loadModule('${helper}');`));
   }
-  headerLines.push(createAwaitExpressionNode(`await TestRunner.showPanel('${panel}');`));
+  if (panel)
+    headerLines.push(createAwaitExpressionNode(`await TestRunner.showPanel('${panel}');`));
 
   if (domFixture) {
     headerLines.push(createAwaitExpressionNode(`await TestRunner.loadHTML(\`
@@ -200,15 +228,15 @@
   }
 
   let nonTestCode = nonTestNodes.reduce((acc, node) => {
-    let code = recast.print(node).code.split('\n').map(line => '    ' + line).join('\n');
-    if (node.id && node.id.name === onloadFunctionName) {
-      code = code.replace('        runTest();\n', '');
-      code = `    (${code.trimLeft()})();`;
-    };
+    const ast = recast.parse(recast.print(node).code);
+    unwrapFunctionExpression(ast, onloadFunctionName);
+    unwrapFunctionDeclaration(ast, onloadFunctionName);
+    let code = recast.print(ast).code.split('\n').map(line => '    ' + line).join('\n');
+    code = code.replace(/\s*runTest\(\);?\s*/, '');
+    code = `    ${code.trim()};`;
     return acc + '\n' + code;
   }, '');
-
-  nonTestCode = nonTestCode.startsWith('\n') && nonTestCode.slice(2);
+  nonTestCode = '  ' + nonTestCode.trimLeft();
   if (nonTestCode) {
     headerLines.push((createAwaitExpressionNode(`await TestRunner.evaluateInPagePromise(\`
   ${nonTestCode}
@@ -238,7 +266,7 @@
  * If the <script></script> block doesn't contain a test function
  * assume that it needs to be serialized
  */
-function processScriptCode(code, additionalHelperBlocks) {
+function processScriptCode(code, javascriptFixtures, onloadFunctionName) {
   const ast = recast.parse(code);
   const testFunctionExpression =
       ast.program.body.find(n => n.type === 'VariableDeclaration' && n.declarations[0].id.name === 'test');
@@ -246,8 +274,16 @@
   if (testFunctionExpression || testFunctionDeclaration) {
     return code;
   }
-  const formattedCode = code.trimRight().split('\n').map(line => '    ' + line).join('\n');
-  additionalHelperBlocks.push(createAwaitExpressionNode(`await TestRunner.evaluateInPagePromise(\`${formattedCode}
+  unwrapFunctionExpression(ast, onloadFunctionName);
+  unwrapFunctionDeclaration(ast, onloadFunctionName);
+  const formattedCode = recast.print(ast)
+                            .code.trimRight()
+                            .split('\n')
+                            .map(line => '    ' + line)
+                            .join('\n')
+                            .replace(/\s*runTest\(\);?\s*/, '');
+
+  javascriptFixtures.push(createAwaitExpressionNode(`await TestRunner.evaluateInPagePromise(\`${formattedCode}
   \`)`));
   return;
 }
@@ -256,9 +292,9 @@
  * Unwrap test if it's a function expression
  * var test = function () {...}
  */
-function unwrapTestFunctionExpressionIfNecessary(ast) {
+function unwrapFunctionExpression(ast, functionName) {
   const index =
-      ast.program.body.findIndex(n => n.type === 'VariableDeclaration' && n.declarations[0].id.name === 'test');
+      ast.program.body.findIndex(n => n.type === 'VariableDeclaration' && n.declarations[0].id.name === functionName);
   if (index > -1) {
     const testFunctionNode = ast.program.body[index];
     ast.program.body = testFunctionNode.declarations[0].init.body.body;
@@ -270,8 +306,8 @@
  * Unwrap test if it's a function declaration
  * function test () {...}
  */
-function unwrapTestFunctionDeclarationIfNecessary(ast) {
-  const index = ast.program.body.findIndex(n => n.type === 'FunctionDeclaration' && n.id.name === 'test');
+function unwrapFunctionDeclaration(ast, functionName) {
+  const index = ast.program.body.findIndex(n => n.type === 'FunctionDeclaration' && n.id.name === functionName);
   if (index > -1) {
     const testFunctionNode = ast.program.body[index];
     ast.program.body.splice(index, 1);
@@ -318,6 +354,8 @@
 
   const components = inputPath.slice(inputPath.indexOf('LayoutTests/')).split('/');
   const folder = inputPath.indexOf('LayoutTests/inspector') === -1 ? components[4] : components[2];
+  if (folder.endsWith('.html'))
+    return;
   const panel = panelByFolder[folder];
   if (!panel) {
     throw new Error('Could not figure out which panel to map folder: ' + folder);
@@ -326,24 +364,18 @@
 }
 
 function mapTestHelpers(testHelpers) {
-  const SKIP = 'SKIP';
-  const testHelperMap = {
-    'inspector-test': SKIP,
-    'console-test': 'console_test_runner',
-    'elements-test': 'elements_test_runner',
-    'sources-test': 'sources_test_runner',
-  };
-  const mappedHelpers = [];
+  const mappedHelpers = new Set();
   for (const helper of testHelpers) {
-    const mappedHelper = testHelperMap[helper];
+    const namespace = migrateUtils.mapTestFilename(helper).namespacePrefix + 'TestRunner';
+    const mappedHelper = namespace.replace(/([A-Z])/g, '_$1').replace(/^_/, '').toLowerCase();
     if (!mappedHelper) {
       throw Error('Could not map helper ' + helper);
     }
-    if (mappedHelper !== SKIP) {
-      mappedHelpers.push(mappedHelper);
+    if (mappedHelper !== 'inspector_test_runner') {
+      mappedHelpers.add(mappedHelper);
     }
   }
-  return mappedHelpers;
+  return Array.from(mappedHelpers);
 }
 
 function generateTestHelperMap() {
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_utils.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_utils.js
index be774e4..97d8124 100644
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_utils.js
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/migrate_utils.js
@@ -18,6 +18,92 @@
   return out;
 }
 
+
+function mapTestFilename(filename) {
+  let namespacePrefix = path.basename(filename, '.js')
+                            .split('-test')[0]
+                            .split('-')
+                            .map(a => a.substring(0, 1).toUpperCase() + a.substring(1))
+                            .join('');
+  let filenamePrefix = namespacePrefix;
+  if (namespacePrefix === 'PageMock')
+    namespacePrefix = '';
+  if (namespacePrefix === 'SyntaxHighlight')
+    namespacePrefix = '';
+  if (namespacePrefix === 'Datagrid') {
+    namespacePrefix = 'DataGrid';
+    filenamePrefix = 'DataGrid';
+  }
+  if (namespacePrefix === 'TimelineData')
+    namespacePrefix = 'Performance';
+  if (namespacePrefix === 'StylesUpdateLinks')
+    namespacePrefix = 'Elements';
+  if (namespacePrefix === 'BreakpointManager')
+    namespacePrefix = 'Sources';
+  if (namespacePrefix === 'ElementsPanelShadowSelectionOnRefresh')
+    namespacePrefix = 'Elements';
+
+  if (namespacePrefix === 'ExtensionsNetwork')
+    namespacePrefix = 'Extensions';
+  if (namespacePrefix === 'CspInline')
+    namespacePrefix = 'CSPInline';
+  if (namespacePrefix === 'Debugger')
+    namespacePrefix = 'Sources';
+  if (namespacePrefix === 'Resources') {
+    namespacePrefix = 'Application';
+  }
+  if (namespacePrefix === 'Appcache')
+    namespacePrefix = 'Application';
+  if (namespacePrefix === 'ResourceTree')
+    namespacePrefix = 'Application';
+  if (namespacePrefix === 'ServiceWorkers')
+    namespacePrefix = 'Application';
+  if (namespacePrefix === 'CacheStorage')
+    namespacePrefix = 'Application';
+  if (namespacePrefix === 'Indexeddb') {
+    namespacePrefix = 'Application';
+    filenamePrefix = 'IndexedDB';
+  }
+  if (namespacePrefix === 'Timeline')
+    namespacePrefix = 'Performance';
+  if (namespacePrefix === 'ProductRegistry')
+    namespacePrefix = 'Network';
+  if (namespacePrefix === 'Editor')
+    namespacePrefix = 'Sources';
+  if (namespacePrefix === 'Search')
+    namespacePrefix = 'Sources';
+  if (namespacePrefix === 'LiveEdit')
+    namespacePrefix = 'Sources';
+  if (namespacePrefix === 'Autocomplete')
+    namespacePrefix = 'Sources';
+  if (namespacePrefix === 'Changes')
+    namespacePrefix = 'Sources';
+  if (namespacePrefix === 'Persistence')
+    namespacePrefix = 'Bindings';
+  if (namespacePrefix === 'IsolatedFilesystem')
+    namespacePrefix = 'Bindings';
+  if (namespacePrefix === 'Automapping')
+    namespacePrefix = 'Bindings';
+  if (namespacePrefix === 'AccessibilityPane')
+    namespacePrefix = 'Accessibility';
+  if (namespacePrefix === 'EditDom') {
+    namespacePrefix = 'Elements';
+    filenamePrefix = 'EditDOM';
+  }
+  if (namespacePrefix === 'SetOuterHtml') {
+    namespacePrefix = 'Elements';
+    filenamePrefix = 'SetOuterHTML';
+  }
+  if (namespacePrefix === 'HeapSnapshot')
+    namespacePrefix = 'Profiler';
+  if (namespacePrefix === 'Sass') {
+    namespacePrefix = 'SASS'
+    filenamePrefix = 'SASS'
+  }
+  return {namespacePrefix, filenamePrefix};
+}
+
 module.exports = {
   getOutPath,
+  mapTestFilename,
 };
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/move.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/move.js
index 21b6270..5bfecfa 100644
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/move.js
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/move.js
@@ -15,8 +15,9 @@
 
 const MIGRATE_TEST_PATH = path.resolve(__dirname, 'migrate_test.js');
 const TESTS_PATH = path.resolve(__dirname, 'tests.txt');
-const TEST_EXPECTATIONS_PATH = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', 'TestExpectations');
-const FLAG_EXPECTATIONS_PATH = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', 'FlagExpectations');
+const LAYOUT_TESTS_PATH = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests');
+const TEST_EXPECTATIONS_PATH = path.resolve(LAYOUT_TESTS_PATH, 'TestExpectations');
+const FLAG_EXPECTATIONS_PATH = path.resolve(LAYOUT_TESTS_PATH, 'FlagExpectations');
 
 function main() {
   const originalTests = fs.readFileSync(TESTS_PATH, 'utf-8').split('\n').map(line => line.split(' ')[0]);
@@ -62,30 +63,78 @@
 
   const newTestPaths = Array.from(oldToNewTestPath.values()).filter(x => x);
 
+  const TestExpectationFailureTypes =
+      ['Crash', 'Failure', 'Rebaseline', 'Skip', 'Timeout', 'WontFix', 'Missing', 'NeedsManualRebaseline'];
+
+  const testsAlreadyExempted = new Set();
+
   // Update TestExpectations
   const testExpectations = fs.readFileSync(TEST_EXPECTATIONS_PATH, 'utf-8');
-  const updatedTestExpecations = testExpectations.split('\n').map(line => {
+  let updatedTestExpecations = testExpectations.split('\n').map(line => {
     for (const [oldTestPath, newTestPath] of oldToNewTestPath) {
       if (!newTestPath)
         continue;
-      if (line.indexOf(oldTestPath) !== -1)
+      if (line.indexOf(oldTestPath) !== -1) {
+        if (TestExpectationFailureTypes.some(x => line.indexOf(x) !== -1)) {
+          testsAlreadyExempted.add(newTestPath);
+        }
         return line.replace(oldTestPath, newTestPath);
+      }
+    }
+    return line;
+  });
+
+  updatedTestExpecations = updatedTestExpecations.map(line => {
+    for (const [oldTestPath, newTestPath] of oldToNewTestPath) {
+      if (!newTestPath)
+        continue;
       if (line === '# See crbug.com/667560 for details') {
-        return line + '\n' + Array.from(newTestPaths).map(x => `crbug.com/667560 ${x} [ Skip ]`).join('\n');
+        return line + '\n' +
+            Array.from(newTestPaths)
+                .filter(t => !testsAlreadyExempted.has(t))
+                .map(x => `crbug.com/667560 ${x} [ Skip ]`)
+                .join('\n');
       }
       if (line === '### virtual/mojo-loading/http/tests/devtools') {
         return line + '\n' +
-            Array.from(newTestPaths).map(x => `crbug.com/667560 virtual/mojo-loading/${x} [ Skip ]`).join('\n');
+            Array.from(newTestPaths)
+                .filter(t => !testsAlreadyExempted.has(t))
+                .map(x => `crbug.com/667560 virtual/mojo-loading/${x} [ Skip ]`)
+                .join('\n');
+      }
+
+      // Put mojo tests here so we don't re-enable the test after migrating
+      if (line === '### Manually fix after migration') {
+        return line + '\n' +
+            Array.from(newTestPaths)
+                .filter(t => testsAlreadyExempted.has(t))
+                .map(x => `crbug.com/667560 virtual/mojo-loading/${x} [ Skip ]`)
+                .join('\n');
       }
     }
     return line;
   });
   fs.writeFileSync(TEST_EXPECTATIONS_PATH, updatedTestExpecations.join('\n'));
 
+  // Update additional test expectations
+  for (const filename
+           of ['ASANExpectations', 'LeakExpectations', 'MSANExpectations', 'NeverFixTests', 'SlowTests', 'SmokeTests',
+               'StaleTestExpectations']) {
+    const filePath = path.resolve(LAYOUT_TESTS_PATH, filename);
+    updateExpectationsFile(filePath);
+  }
+
   // Update FlagExpectations
-  for (const folder of fs.readdirSync(FLAG_EXPECTATIONS_PATH)) {
-    const flagFilePath = path.resolve(FLAG_EXPECTATIONS_PATH, folder);
-    const expectations = fs.readFileSync(flagFilePath, 'utf-8');
+  for (const filename of fs.readdirSync(FLAG_EXPECTATIONS_PATH)) {
+    const filePath = path.resolve(FLAG_EXPECTATIONS_PATH, filename);
+    updateExpectationsFile(filePath);
+  }
+
+  for (const [oldResourcesPath, newResourcesPath] of oldToNewResourcesPath)
+    utils.copyRecursive(oldResourcesPath, path.dirname(newResourcesPath));
+
+  function updateExpectationsFile(filePath) {
+    const expectations = fs.readFileSync(filePath, 'utf-8');
     const updatedExpectations = expectations.split('\n').map(line => {
       for (const [oldTestPath, newTestPath] of oldToNewTestPath) {
         if (!newTestPath)
@@ -96,11 +145,8 @@
       }
       return line;
     });
-    fs.writeFileSync(flagFilePath, updatedExpectations.join('\n'));
+    fs.writeFileSync(filePath, updatedExpectations.join('\n'));
   }
-
-  for (const [oldResourcesPath, newResourcesPath] of oldToNewResourcesPath)
-    utils.copyRecursive(oldResourcesPath, path.dirname(newResourcesPath));
 }
 
 main();
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/package.json b/third_party/WebKit/Source/devtools/scripts/migrate_test/package.json
index a5279af..8eb135ec 100644
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/package.json
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/package.json
@@ -6,6 +6,7 @@
   "dependencies": {
     "cheerio": "^1.0.0-rc.2",
     "mkdirp": "^0.5.1",
+    "prettier": "^1.5.3",
     "recast": "^0.12.6"
   }
 }
diff --git a/third_party/WebKit/Source/devtools/scripts/migrate_test/transform.js b/third_party/WebKit/Source/devtools/scripts/migrate_test/transform.js
index 2cf305b..a27cb643 100644
--- a/third_party/WebKit/Source/devtools/scripts/migrate_test/transform.js
+++ b/third_party/WebKit/Source/devtools/scripts/migrate_test/transform.js
@@ -27,7 +27,7 @@
       continue;
     const fullTestPath = path.resolve(__dirname, '..', '..', '..', '..', 'LayoutTests', testPath);
     try {
-      childProcess.execSync(`node ${MIGRATE_SCRIPT_PATH} ${fullTestPath}`)
+      childProcess.execSync(`node ${MIGRATE_SCRIPT_PATH} ${fullTestPath}`);
     } catch (err) {
       console.log(err.stdout.toString());
       continue;
@@ -56,7 +56,7 @@
       updatedTestExpecationLines.push(line);
       continue;
     }
-    if (line === '# ====== DevTools test migration failures until here ======') {
+    if (line === '### Manually fix after migration') {
       seenEndSentinel = true;
       updatedTestExpecationLines.push(line);
       continue;
diff --git a/third_party/WebKit/Source/devtools/scripts/special_case_namespaces.json b/third_party/WebKit/Source/devtools/scripts/special_case_namespaces.json
index a973224..ff555c9 100644
--- a/third_party/WebKit/Source/devtools/scripts/special_case_namespaces.json
+++ b/third_party/WebKit/Source/devtools/scripts/special_case_namespaces.json
@@ -4,5 +4,6 @@
   "object_ui": "ObjectUI",
   "perf_ui": "PerfUI",
   "css_tracker": "CSSTracker",
-  "har_importer": "HARImporter"
+  "har_importer": "HARImporter",
+  "sass_test_runner": "SASSTestRunner"
 }
\ No newline at end of file
diff --git a/third_party/WebKit/Source/modules/mediasource/TrackDefault.cpp b/third_party/WebKit/Source/modules/mediasource/TrackDefault.cpp
index 19326c2..9c0453f 100644
--- a/third_party/WebKit/Source/modules/mediasource/TrackDefault.cpp
+++ b/third_party/WebKit/Source/modules/mediasource/TrackDefault.cpp
@@ -5,9 +5,11 @@
 #include "modules/mediasource/TrackDefault.h"
 
 #include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/ToV8ForCore.h"
 #include "core/html/track/AudioTrack.h"
 #include "core/html/track/TextTrack.h"
 #include "core/html/track/VideoTrack.h"
+#include "platform/bindings/ScriptState.h"
 
 namespace blink {
 
@@ -26,6 +28,10 @@
   return text;
 }
 
+ScriptValue TrackDefault::kinds(ScriptState* script_state) const {
+  return ScriptValue(script_state, ToV8(kinds_, script_state));
+}
+
 TrackDefault* TrackDefault::Create(const AtomicString& type,
                                    const String& language,
                                    const String& label,
diff --git a/third_party/WebKit/Source/modules/mediasource/TrackDefault.h b/third_party/WebKit/Source/modules/mediasource/TrackDefault.h
index c25e151f..6fb1926 100644
--- a/third_party/WebKit/Source/modules/mediasource/TrackDefault.h
+++ b/third_party/WebKit/Source/modules/mediasource/TrackDefault.h
@@ -5,12 +5,14 @@
 #ifndef TrackDefault_h
 #define TrackDefault_h
 
+#include "bindings/core/v8/ScriptValue.h"
 #include "platform/bindings/ScriptWrappable.h"
 #include "platform/wtf/text/WTFString.h"
 
 namespace blink {
 
 class ExceptionState;
+class ScriptState;
 
 class TrackDefault final : public GarbageCollectedFinalized<TrackDefault>,
                            public ScriptWrappable {
@@ -35,7 +37,7 @@
   String byteStreamTrackID() const { return byte_stream_track_id_; }
   String language() const { return language_; }
   String label() const { return label_; }
-  const Vector<String>& kinds() const { return kinds_; }
+  ScriptValue kinds(ScriptState*) const;
 
   DEFINE_INLINE_TRACE() {}
 
diff --git a/third_party/WebKit/Source/modules/mediasource/TrackDefault.idl b/third_party/WebKit/Source/modules/mediasource/TrackDefault.idl
index 89ae52a..8a8e11a9 100644
--- a/third_party/WebKit/Source/modules/mediasource/TrackDefault.idl
+++ b/third_party/WebKit/Source/modules/mediasource/TrackDefault.idl
@@ -16,9 +16,10 @@
     readonly attribute DOMString byteStreamTrackID;
     readonly attribute DOMString language;
     readonly attribute DOMString label;
-    // TODO(wolenetz): |kinds| should be [SameObject] FrozenArray<DOMString> or
-    // a getKinds() method returning sequence<DOMString>.
-    // https://github.com/w3c/media-source/issues/91
-    // https://crbug.com/619665
-    readonly attribute DOMString[] kinds;
+    // TrackDefault was removed from the spec in
+    // https://github.com/w3c/media-source/pull/138, and the last version to
+    // have it had getKinds() instead of |kinds|. Use an object here to avoid
+    // WebIDL arrays (which no longer exist) while not breaking compatibility
+    // See also: https://crbug.com/619665.
+    [CallWith=ScriptState] readonly attribute object kinds;
 };
diff --git a/third_party/WebKit/Source/platform/bindings/ScopedPersistent.h b/third_party/WebKit/Source/platform/bindings/ScopedPersistent.h
index 11e12a0..559b82f 100644
--- a/third_party/WebKit/Source/platform/bindings/ScopedPersistent.h
+++ b/third_party/WebKit/Source/platform/bindings/ScopedPersistent.h
@@ -61,7 +61,7 @@
       handle_.Reset(isolate, local);
   }
 
-  virtual ~ScopedPersistent() { Clear(); }
+  ~ScopedPersistent() { Clear(); }
 
   ALWAYS_INLINE v8::Local<T> NewLocal(v8::Isolate* isolate) const {
     return v8::Local<T>::New(isolate, handle_);
@@ -85,7 +85,7 @@
   bool IsEmpty() const { return handle_.IsEmpty(); }
   bool IsWeak() const { return handle_.IsWeak(); }
 
-  virtual void Set(v8::Isolate* isolate, v8::Local<T> handle) {
+  void Set(v8::Isolate* isolate, v8::Local<T> handle) {
     handle_.Reset(isolate, handle);
   }
 
diff --git a/third_party/WebKit/Source/platform/bindings/ScriptState.h b/third_party/WebKit/Source/platform/bindings/ScriptState.h
index e2e75ee..c6c9e71 100644
--- a/third_party/WebKit/Source/platform/bindings/ScriptState.h
+++ b/third_party/WebKit/Source/platform/bindings/ScriptState.h
@@ -125,7 +125,7 @@
     // ScriptState::from() must not be called for a context that does not have
     // valid embedder data in the embedder field.
     SECURITY_CHECK(script_state);
-    SECURITY_CHECK(script_state->GetContext() == context);
+    SECURITY_CHECK(script_state->context_ == context);
     return script_state;
   }
 
diff --git a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
index b0bdd75..315de8d 100644
--- a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
@@ -68,6 +68,18 @@
                             std::move(request));
   }
 
+  void ReadRange(uint64_t offset,
+                 uint64_t length,
+                 mojo::ScopedDataPipeProducerHandle,
+                 storage::mojom::blink::BlobReaderClientPtr) override {
+    NOTREACHED();
+  }
+
+  void ReadAll(mojo::ScopedDataPipeProducerHandle,
+               storage::mojom::blink::BlobReaderClientPtr) override {
+    NOTREACHED();
+  }
+
   void GetInternalUUID(GetInternalUUIDCallback callback) override {
     std::move(callback).Run(uuid_);
   }
diff --git a/third_party/WebKit/Source/platform/graphics/StrokeData.cpp b/third_party/WebKit/Source/platform/graphics/StrokeData.cpp
index 2c909df..f8f4d81 100644
--- a/third_party/WebKit/Source/platform/graphics/StrokeData.cpp
+++ b/third_party/WebKit/Source/platform/graphics/StrokeData.cpp
@@ -117,7 +117,6 @@
     SkScalar intervals[2] = {0, gap + dash_width - kEpsilon};
     flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
   } else {
-    // TODO(schenney): WavyStroke https://crbug.com/229574
     flags->setPathEffect(0);
   }
 }
diff --git a/third_party/WebKit/Source/platform/heap/HeapAllocator.h b/third_party/WebKit/Source/platform/heap/HeapAllocator.h
index 42dd648..117d9ba1 100644
--- a/third_party/WebKit/Source/platform/heap/HeapAllocator.h
+++ b/third_party/WebKit/Source/platform/heap/HeapAllocator.h
@@ -329,23 +329,22 @@
   // Use the payload size as recorded by the heap to determine how many
   // elements to finalize.
   size_t length = header->PayloadSize() / sizeof(T);
-  T* buffer = reinterpret_cast<T*>(pointer);
+  char* payload = static_cast<char*>(pointer);
 #ifdef ANNOTATE_CONTIGUOUS_CONTAINER
+  ANNOTATE_CHANGE_SIZE(payload, length * sizeof(T), 0, length * sizeof(T));
+#endif
   // As commented above, HeapVectorBacking calls finalizers for unused slots
   // (which are already zeroed out).
-  ANNOTATE_CHANGE_SIZE(buffer, length, 0, length);
-#endif
   if (std::is_polymorphic<T>::value) {
-    char* pointer = reinterpret_cast<char*>(buffer);
     for (unsigned i = 0; i < length; ++i) {
-      char* element = pointer + i * sizeof(T);
+      char* element = payload + i * sizeof(T);
       if (blink::VTableInitialized(element))
         reinterpret_cast<T*>(element)->~T();
     }
   } else {
-    for (unsigned i = 0; i < length; ++i) {
+    T* buffer = reinterpret_cast<T*>(payload);
+    for (unsigned i = 0; i < length; ++i)
       buffer[i].~T();
-    }
   }
 }
 
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
index 39c96cd..3d0015b 100644
--- a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
+++ b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
@@ -835,8 +835,8 @@
 
     HeaderFieldTokenizer tokenizer(headerValue);
     while (!tokenizer.IsConsumed()) {
-      StringView metric;
-      if (!tokenizer.ConsumeToken(Mode::kNormal, metric)) {
+      StringView name;
+      if (!tokenizer.ConsumeToken(Mode::kNormal, name)) {
         break;
       }
 
@@ -853,7 +853,7 @@
       }
 
       headers->push_back(WTF::MakeUnique<ServerTimingHeader>(
-          metric.ToString(), value, description));
+          name.ToString(), value, description));
 
       if (!tokenizer.Consume(',')) {
         break;
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsers.h b/third_party/WebKit/Source/platform/network/HTTPParsers.h
index b52261c..cd6fff7b 100644
--- a/third_party/WebKit/Source/platform/network/HTTPParsers.h
+++ b/third_party/WebKit/Source/platform/network/HTTPParsers.h
@@ -84,12 +84,12 @@
 };
 
 struct ServerTimingHeader {
-  String metric;
-  double value;
+  String name;
+  double duration;
   String description;
 
-  ServerTimingHeader(String metric, double value, String description)
-      : metric(metric), value(value), description(description) {}
+  ServerTimingHeader(String name, double duration, String description)
+      : name(name), duration(duration), description(description) {}
 };
 
 using ServerTimingHeaderVector = Vector<std::unique_ptr<ServerTimingHeader>>;
@@ -158,11 +158,11 @@
                                                    ResourceResponse*,
                                                    size_t* end);
 
-// Parses a header value containing JSON data, according to
+// Parses a header duration containing JSON data, according to
 // https://tools.ietf.org/html/draft-ietf-httpbis-jfv-01
 // Returns an empty unique_ptr if the header cannot be parsed as JSON. JSON
 // strings which represent object nested deeper than |maxParseDepth| will also
-// cause an empty return value.
+// cause an empty return duration.
 PLATFORM_EXPORT std::unique_ptr<JSONArray> ParseJSONHeader(const String& header,
                                                            int max_parse_depth);
 
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsersTest.cpp b/third_party/WebKit/Source/platform/network/HTTPParsersTest.cpp
index 436ffa4..ffd03ea 100644
--- a/third_party/WebKit/Source/platform/network/HTTPParsersTest.cpp
+++ b/third_party/WebKit/Source/platform/network/HTTPParsersTest.cpp
@@ -501,8 +501,8 @@
   unsigned i = 0;
   for (const auto& header : *results) {
     Vector<String> expectedResult = expectedResults[i++];
-    EXPECT_EQ(header->metric, expectedResult[0]);
-    EXPECT_EQ(header->value, expectedResult[1].ToDouble());
+    EXPECT_EQ(header->name, expectedResult[0]);
+    EXPECT_EQ(header->duration, expectedResult[1].ToDouble());
     EXPECT_EQ(header->description, expectedResult[2]);
   }
 }
diff --git a/third_party/WebKit/Source/platform/scheduler/BUILD.gn b/third_party/WebKit/Source/platform/scheduler/BUILD.gn
index 7a26319..2ac5798 100644
--- a/third_party/WebKit/Source/platform/scheduler/BUILD.gn
+++ b/third_party/WebKit/Source/platform/scheduler/BUILD.gn
@@ -88,6 +88,8 @@
     "renderer/render_widget_scheduling_state.cc",
     "renderer/render_widget_signals.cc",
     "renderer/render_widget_signals.h",
+    "renderer/renderer_metrics_helper.cc",
+    "renderer/renderer_metrics_helper.h",
     "renderer/renderer_scheduler.cc",
     "renderer/renderer_scheduler_impl.cc",
     "renderer/renderer_scheduler_impl.h",
@@ -187,6 +189,7 @@
     "renderer/deadline_task_runner_unittest.cc",
     "renderer/idle_time_estimator_unittest.cc",
     "renderer/render_widget_signals_unittest.cc",
+    "renderer/renderer_metrics_helper_unittest.cc",
     "renderer/renderer_scheduler_impl_unittest.cc",
     "renderer/task_cost_estimator_unittest.cc",
     "renderer/task_duration_metric_reporter_unittest.cc",
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper.cc
new file mode 100644
index 0000000..0680913
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper.cc
@@ -0,0 +1,359 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/scheduler/renderer/renderer_metrics_helper.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "platform/scheduler/renderer/renderer_scheduler_impl.h"
+#include "public/platform/scheduler/renderer_process_type.h"
+
+namespace blink {
+namespace scheduler {
+
+#define TASK_DURATION_METRIC_NAME "RendererScheduler.TaskDurationPerQueueType2"
+#define TASK_COUNT_METRIC_NAME "RendererScheduler.TaskCountPerQueueType"
+#define MAIN_THREAD_LOAD_METRIC_NAME "RendererScheduler.RendererMainThreadLoad5"
+#define EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME \
+  MAIN_THREAD_LOAD_METRIC_NAME ".Extension"
+
+namespace {
+
+constexpr base::TimeDelta kThreadLoadTrackerReportingInterval =
+    base::TimeDelta::FromSeconds(1);
+// Threshold for discarding ultra-long tasks. Is it assumed that ultra-long
+// tasks are reporting glitches (e.g. system falling asleep in the middle
+// of the task).
+constexpr base::TimeDelta kLongTaskDiscardingThreshold =
+    base::TimeDelta::FromSeconds(30);
+constexpr base::TimeDelta kLongIdlePeriodDiscardingThreshold =
+    base::TimeDelta::FromMinutes(3);
+
+}  // namespace
+
+RendererMetricsHelper::RendererMetricsHelper(
+    RendererSchedulerImpl* renderer_scheduler,
+    base::TimeTicks now,
+    bool renderer_backgrounded)
+    : renderer_scheduler_(renderer_scheduler),
+      main_thread_load_tracker(
+          now,
+          base::Bind(&RendererMetricsHelper::RecordMainThreadTaskLoad,
+                     base::Unretained(this)),
+          kThreadLoadTrackerReportingInterval),
+      background_main_thread_load_tracker(
+          now,
+          base::Bind(&RendererMetricsHelper::RecordBackgroundMainThreadTaskLoad,
+                     base::Unretained(this)),
+          kThreadLoadTrackerReportingInterval),
+      foreground_main_thread_load_tracker(
+          now,
+          base::Bind(&RendererMetricsHelper::RecordForegroundMainThreadTaskLoad,
+                     base::Unretained(this)),
+          kThreadLoadTrackerReportingInterval),
+      task_duration_reporter(TASK_DURATION_METRIC_NAME),
+      foreground_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                        ".Foreground"),
+      foreground_first_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                                     ".Foreground.FirstMinute"),
+      foreground_second_minute_task_duration_reporter(
+          TASK_DURATION_METRIC_NAME ".Foreground.SecondMinute"),
+      foreground_third_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                                     ".Foreground.ThirdMinute"),
+      foreground_after_third_minute_task_duration_reporter(
+          TASK_DURATION_METRIC_NAME ".Foreground.AfterThirdMinute"),
+      background_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                        ".Background"),
+      background_first_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                                     ".Background.FirstMinute"),
+      background_second_minute_task_duration_reporter(
+          TASK_DURATION_METRIC_NAME ".Background.SecondMinute"),
+      background_third_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                                     ".Background.ThirdMinute"),
+      background_fourth_minute_task_duration_reporter(
+          TASK_DURATION_METRIC_NAME ".Background.FourthMinute"),
+      background_fifth_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                                     ".Background.FifthMinute"),
+      background_after_fifth_minute_task_duration_reporter(
+          TASK_DURATION_METRIC_NAME ".Background.AfterFifthMinute"),
+      hidden_task_duration_reporter(TASK_DURATION_METRIC_NAME ".Hidden"),
+      visible_task_duration_reporter(TASK_DURATION_METRIC_NAME ".Visible"),
+      hidden_music_task_duration_reporter(TASK_DURATION_METRIC_NAME
+                                          ".HiddenMusic") {
+  main_thread_load_tracker.Resume(now);
+  if (renderer_backgrounded) {
+    background_main_thread_load_tracker.Resume(now);
+  } else {
+    foreground_main_thread_load_tracker.Resume(now);
+  }
+}
+
+RendererMetricsHelper::~RendererMetricsHelper() {}
+
+void RendererMetricsHelper::OnRendererForegrounded(base::TimeTicks now) {
+  foreground_main_thread_load_tracker.Resume(now);
+  background_main_thread_load_tracker.Pause(now);
+}
+
+void RendererMetricsHelper::OnRendererBackgrounded(base::TimeTicks now) {
+  foreground_main_thread_load_tracker.Pause(now);
+  background_main_thread_load_tracker.Resume(now);
+}
+
+void RendererMetricsHelper::OnRendererShutdown(base::TimeTicks now) {
+  foreground_main_thread_load_tracker.RecordIdle(now);
+  background_main_thread_load_tracker.RecordIdle(now);
+  main_thread_load_tracker.RecordIdle(now);
+}
+
+namespace {
+
+// Calculates the length of the intersection of two given time intervals.
+base::TimeDelta DurationOfIntervalOverlap(base::TimeTicks start1,
+                                          base::TimeTicks end1,
+                                          base::TimeTicks start2,
+                                          base::TimeTicks end2) {
+  DCHECK_LE(start1, end1);
+  DCHECK_LE(start2, end2);
+  return std::max(std::min(end1, end2) - std::max(start1, start2),
+                  base::TimeDelta());
+}
+
+}  // namespace
+
+void RendererMetricsHelper::RecordTaskMetrics(
+    MainThreadTaskQueue::QueueType queue_type,
+    base::TimeTicks start_time,
+    base::TimeTicks end_time) {
+  base::TimeDelta duration = end_time - start_time;
+  if (duration > kLongTaskDiscardingThreshold)
+    return;
+
+  // Discard anomalously long idle periods.
+  if (last_reported_task_ && start_time - last_reported_task_.value() >
+                                 kLongIdlePeriodDiscardingThreshold) {
+    main_thread_load_tracker.Reset(end_time);
+    foreground_main_thread_load_tracker.Reset(end_time);
+    background_main_thread_load_tracker.Reset(end_time);
+    return;
+  }
+
+  last_reported_task_ = end_time;
+
+  UMA_HISTOGRAM_CUSTOM_COUNTS("RendererScheduler.TaskTime2",
+                              duration.InMicroseconds(), 1, 1000 * 1000, 50);
+
+  // We want to measure thread time here, but for efficiency reasons
+  // we stick with wall time.
+  main_thread_load_tracker.RecordTaskTime(start_time, end_time);
+  foreground_main_thread_load_tracker.RecordTaskTime(start_time, end_time);
+  background_main_thread_load_tracker.RecordTaskTime(start_time, end_time);
+
+  UMA_HISTOGRAM_ENUMERATION(
+      TASK_COUNT_METRIC_NAME, static_cast<int>(queue_type),
+      static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
+
+  if (duration >= base::TimeDelta::FromMilliseconds(16)) {
+    UMA_HISTOGRAM_ENUMERATION(
+        TASK_COUNT_METRIC_NAME ".LongerThan16ms", static_cast<int>(queue_type),
+        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
+  }
+
+  if (duration >= base::TimeDelta::FromMilliseconds(50)) {
+    UMA_HISTOGRAM_ENUMERATION(
+        TASK_COUNT_METRIC_NAME ".LongerThan50ms", static_cast<int>(queue_type),
+        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
+  }
+
+  if (duration >= base::TimeDelta::FromMilliseconds(100)) {
+    UMA_HISTOGRAM_ENUMERATION(
+        TASK_COUNT_METRIC_NAME ".LongerThan100ms", static_cast<int>(queue_type),
+        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
+  }
+
+  if (duration >= base::TimeDelta::FromMilliseconds(150)) {
+    UMA_HISTOGRAM_ENUMERATION(
+        TASK_COUNT_METRIC_NAME ".LongerThan150ms", static_cast<int>(queue_type),
+        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
+  }
+
+  if (duration >= base::TimeDelta::FromSeconds(1)) {
+    UMA_HISTOGRAM_ENUMERATION(
+        TASK_COUNT_METRIC_NAME ".LongerThan1s", static_cast<int>(queue_type),
+        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
+  }
+
+  task_duration_reporter.RecordTask(queue_type, duration);
+
+  if (renderer_scheduler_->main_thread_only().renderer_backgrounded) {
+    background_task_duration_reporter.RecordTask(queue_type, duration);
+
+    // Collect detailed breakdown for first five minutes given that we stop
+    // timers on mobile after five minutes.
+    base::TimeTicks backgrounded_at =
+        renderer_scheduler_->main_thread_only().background_status_changed_at;
+
+    background_first_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time, backgrounded_at,
+                        backgrounded_at + base::TimeDelta::FromMinutes(1)));
+
+    background_second_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time,
+                        backgrounded_at + base::TimeDelta::FromMinutes(1),
+                        backgrounded_at + base::TimeDelta::FromMinutes(2)));
+
+    background_third_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time,
+                        backgrounded_at + base::TimeDelta::FromMinutes(2),
+                        backgrounded_at + base::TimeDelta::FromMinutes(3)));
+
+    background_fourth_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time,
+                        backgrounded_at + base::TimeDelta::FromMinutes(3),
+                        backgrounded_at + base::TimeDelta::FromMinutes(4)));
+
+    background_fifth_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time,
+                        backgrounded_at + base::TimeDelta::FromMinutes(4),
+                        backgrounded_at + base::TimeDelta::FromMinutes(5)));
+
+    background_after_fifth_minute_task_duration_reporter.RecordTask(
+        queue_type,
+        DurationOfIntervalOverlap(
+            start_time, end_time,
+            backgrounded_at + base::TimeDelta::FromMinutes(5),
+            std::max(backgrounded_at + base::TimeDelta::FromMinutes(5),
+                     end_time)));
+  } else {
+    foreground_task_duration_reporter.RecordTask(queue_type, duration);
+
+    // For foreground tabs we do not expect such a notable difference as it is
+    // the case with background tabs, so we limit breakdown to three minutes.
+    base::TimeTicks foregrounded_at =
+        renderer_scheduler_->main_thread_only().background_status_changed_at;
+
+    foreground_first_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time, foregrounded_at,
+                        foregrounded_at + base::TimeDelta::FromMinutes(1)));
+
+    foreground_second_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time,
+                        foregrounded_at + base::TimeDelta::FromMinutes(1),
+                        foregrounded_at + base::TimeDelta::FromMinutes(2)));
+
+    foreground_third_minute_task_duration_reporter.RecordTask(
+        queue_type, DurationOfIntervalOverlap(
+                        start_time, end_time,
+                        foregrounded_at + base::TimeDelta::FromMinutes(2),
+                        foregrounded_at + base::TimeDelta::FromMinutes(3)));
+
+    foreground_after_third_minute_task_duration_reporter.RecordTask(
+        queue_type,
+        DurationOfIntervalOverlap(
+            start_time, end_time,
+            foregrounded_at + base::TimeDelta::FromMinutes(3),
+            std::max(foregrounded_at + base::TimeDelta::FromMinutes(3),
+                     end_time)));
+  }
+
+  if (renderer_scheduler_->main_thread_only().renderer_hidden) {
+    hidden_task_duration_reporter.RecordTask(queue_type, duration);
+
+    if (renderer_scheduler_->ShouldDisableThrottlingBecauseOfAudio(
+            start_time)) {
+      hidden_music_task_duration_reporter.RecordTask(queue_type, duration);
+    }
+  } else {
+    visible_task_duration_reporter.RecordTask(queue_type, duration);
+  }
+}
+
+void RendererMetricsHelper::RecordMainThreadTaskLoad(base::TimeTicks time,
+                                                     double load) {
+  int load_percentage = static_cast<int>(load * 100);
+  DCHECK_LE(load_percentage, 100);
+
+  UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME, load_percentage);
+
+  if (renderer_scheduler_->main_thread_only().process_type ==
+      RendererProcessType::kExtensionRenderer) {
+    UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME,
+                             load_percentage);
+  }
+
+  TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+                 "RendererScheduler.RendererMainThreadLoad", load_percentage);
+}
+
+void RendererMetricsHelper::RecordForegroundMainThreadTaskLoad(
+    base::TimeTicks time,
+    double load) {
+  int load_percentage = static_cast<int>(load * 100);
+  DCHECK_LE(load_percentage, 100);
+
+  switch (renderer_scheduler_->main_thread_only().process_type) {
+    case RendererProcessType::kExtensionRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
+                               ".Foreground",
+                               load_percentage);
+      break;
+    case RendererProcessType::kRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Foreground",
+                               load_percentage);
+
+      if (time - renderer_scheduler_->main_thread_only()
+                     .background_status_changed_at >
+          base::TimeDelta::FromMinutes(1)) {
+        UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
+                                 ".Foreground.AfterFirstMinute",
+                                 load_percentage);
+      }
+      break;
+  }
+
+  TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+                 "RendererScheduler.RendererMainThreadLoad.Foreground",
+                 load_percentage);
+}
+
+void RendererMetricsHelper::RecordBackgroundMainThreadTaskLoad(
+    base::TimeTicks time,
+    double load) {
+  int load_percentage = static_cast<int>(load * 100);
+  DCHECK_LE(load_percentage, 100);
+
+  switch (renderer_scheduler_->main_thread_only().process_type) {
+    case RendererProcessType::kExtensionRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
+                               ".Background",
+                               load_percentage);
+      break;
+    case RendererProcessType::kRenderer:
+      UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Background",
+                               load_percentage);
+
+      if (time - renderer_scheduler_->main_thread_only()
+                     .background_status_changed_at >
+          base::TimeDelta::FromMinutes(1)) {
+        UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
+                                 ".Background.AfterFirstMinute",
+                                 load_percentage);
+      }
+      break;
+  }
+
+  TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+                 "RendererScheduler.RendererMainThreadLoad.Background",
+                 load_percentage);
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper.h b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper.h
new file mode 100644
index 0000000..6ad79c03
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper.h
@@ -0,0 +1,75 @@
+// 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 THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
+#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "platform/PlatformExport.h"
+#include "platform/scheduler/base/thread_load_tracker.h"
+#include "platform/scheduler/renderer/main_thread_task_queue.h"
+#include "platform/scheduler/renderer/task_duration_metric_reporter.h"
+
+namespace blink {
+namespace scheduler {
+
+class RendererSchedulerImpl;
+
+// Helper class to take care of metrics on behalf of RendererScheduler.
+// This class should be used only on the main thread.
+class PLATFORM_EXPORT RendererMetricsHelper {
+ public:
+  RendererMetricsHelper(RendererSchedulerImpl* renderer_scheduler,
+                        base::TimeTicks now,
+                        bool renderer_backgrounded);
+  ~RendererMetricsHelper();
+
+  void RecordTaskMetrics(MainThreadTaskQueue::QueueType queue_type,
+                         base::TimeTicks start_time,
+                         base::TimeTicks end_time);
+
+  void OnRendererForegrounded(base::TimeTicks now);
+  void OnRendererBackgrounded(base::TimeTicks now);
+  void OnRendererShutdown(base::TimeTicks now);
+
+  void RecordMainThreadTaskLoad(base::TimeTicks time, double load);
+  void RecordForegroundMainThreadTaskLoad(base::TimeTicks time, double load);
+  void RecordBackgroundMainThreadTaskLoad(base::TimeTicks time, double load);
+
+ private:
+  RendererSchedulerImpl* renderer_scheduler_;  // NOT OWNED
+
+  base::Optional<base::TimeTicks> last_reported_task_;
+
+  ThreadLoadTracker main_thread_load_tracker;
+  ThreadLoadTracker background_main_thread_load_tracker;
+  ThreadLoadTracker foreground_main_thread_load_tracker;
+
+  TaskDurationMetricReporter task_duration_reporter;
+  TaskDurationMetricReporter foreground_task_duration_reporter;
+  TaskDurationMetricReporter foreground_first_minute_task_duration_reporter;
+  TaskDurationMetricReporter foreground_second_minute_task_duration_reporter;
+  TaskDurationMetricReporter foreground_third_minute_task_duration_reporter;
+  TaskDurationMetricReporter
+      foreground_after_third_minute_task_duration_reporter;
+  TaskDurationMetricReporter background_task_duration_reporter;
+  TaskDurationMetricReporter background_first_minute_task_duration_reporter;
+  TaskDurationMetricReporter background_second_minute_task_duration_reporter;
+  TaskDurationMetricReporter background_third_minute_task_duration_reporter;
+  TaskDurationMetricReporter background_fourth_minute_task_duration_reporter;
+  TaskDurationMetricReporter background_fifth_minute_task_duration_reporter;
+  TaskDurationMetricReporter
+      background_after_fifth_minute_task_duration_reporter;
+  TaskDurationMetricReporter hidden_task_duration_reporter;
+  TaskDurationMetricReporter visible_task_duration_reporter;
+  TaskDurationMetricReporter hidden_music_task_duration_reporter;
+
+  DISALLOW_COPY_AND_ASSIGN(RendererMetricsHelper);
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc
new file mode 100644
index 0000000..18e7557
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc
@@ -0,0 +1,176 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/scheduler/renderer/renderer_metrics_helper.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "components/viz/test/ordered_simple_task_runner.h"
+#include "platform/scheduler/base/test_time_source.h"
+#include "platform/scheduler/child/scheduler_tqm_delegate_for_test.h"
+#include "platform/scheduler/renderer/renderer_scheduler_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+using QueueType = MainThreadTaskQueue::QueueType;
+using testing::ElementsAre;
+using base::Bucket;
+
+class RendererMetricsHelperTest : public ::testing::Test {
+ public:
+  RendererMetricsHelperTest() {}
+  ~RendererMetricsHelperTest() {}
+
+  void SetUp() {
+    histogram_tester_.reset(new base::HistogramTester());
+    clock_ = base::MakeUnique<base::SimpleTestTickClock>();
+    mock_task_runner_ =
+        make_scoped_refptr(new cc::OrderedSimpleTaskRunner(clock_.get(), true));
+    delegate_ = SchedulerTqmDelegateForTest::Create(
+        mock_task_runner_, base::MakeUnique<TestTimeSource>(clock_.get()));
+    scheduler_ = base::MakeUnique<RendererSchedulerImpl>(delegate_);
+    metrics_helper_ = &scheduler_->main_thread_only().metrics_helper;
+  }
+
+  void TearDown() {
+    scheduler_->Shutdown();
+    scheduler_.reset();
+  }
+
+  void RunTask(MainThreadTaskQueue::QueueType queue_type,
+               base::TimeTicks start,
+               base::TimeDelta duration) {
+    DCHECK_LE(clock_->NowTicks(), start);
+    clock_->SetNowTicks(start + duration);
+    metrics_helper_->RecordTaskMetrics(queue_type, start, start + duration);
+  }
+
+  base::TimeTicks Microseconds(int microseconds) {
+    return base::TimeTicks() + base::TimeDelta::FromMicroseconds(microseconds);
+  }
+
+  base::TimeTicks Milliseconds(int milliseconds) {
+    return base::TimeTicks() + base::TimeDelta::FromMilliseconds(milliseconds);
+  }
+
+  std::unique_ptr<base::SimpleTestTickClock> clock_;
+  scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+  scoped_refptr<SchedulerTqmDelegate> delegate_;
+  std::unique_ptr<RendererSchedulerImpl> scheduler_;
+  RendererMetricsHelper* metrics_helper_;  // NOT OWNED
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+
+  DISALLOW_COPY_AND_ASSIGN(RendererMetricsHelperTest);
+};
+
+TEST_F(RendererMetricsHelperTest, Metrics) {
+  // QueueType::DEFAULT is checking sub-millisecond task aggregation,
+  // FRAME_* tasks are checking normal task aggregation and other
+  // queue types have a single task.
+  RunTask(QueueType::DEFAULT, Milliseconds(1),
+          base::TimeDelta::FromMicroseconds(700));
+  RunTask(QueueType::DEFAULT, Milliseconds(2),
+          base::TimeDelta::FromMicroseconds(700));
+  RunTask(QueueType::DEFAULT, Milliseconds(3),
+          base::TimeDelta::FromMicroseconds(700));
+
+  RunTask(QueueType::DEFAULT_LOADING, Milliseconds(200),
+          base::TimeDelta::FromMilliseconds(20));
+  RunTask(QueueType::CONTROL, Milliseconds(400),
+          base::TimeDelta::FromMilliseconds(30));
+  RunTask(QueueType::DEFAULT_TIMER, Milliseconds(600),
+          base::TimeDelta::FromMilliseconds(5));
+  RunTask(QueueType::FRAME_LOADING, Milliseconds(800),
+          base::TimeDelta::FromMilliseconds(70));
+  RunTask(QueueType::FRAME_UNTHROTTLED, Milliseconds(1000),
+          base::TimeDelta::FromMilliseconds(20));
+  RunTask(QueueType::COMPOSITOR, Milliseconds(1200),
+          base::TimeDelta::FromMilliseconds(25));
+  RunTask(QueueType::TEST, Milliseconds(1600),
+          base::TimeDelta::FromMilliseconds(85));
+
+  scheduler_->SetRendererBackgrounded(true);
+
+  RunTask(QueueType::CONTROL, Milliseconds(2000),
+          base::TimeDelta::FromMilliseconds(25));
+  RunTask(QueueType::FRAME_TIMER, Milliseconds(2600),
+          base::TimeDelta::FromMilliseconds(175));
+  RunTask(QueueType::UNTHROTTLED, Milliseconds(2800),
+          base::TimeDelta::FromMilliseconds(25));
+  RunTask(QueueType::FRAME_LOADING, Milliseconds(3000),
+          base::TimeDelta::FromMilliseconds(35));
+  RunTask(QueueType::FRAME_TIMER, Milliseconds(3200),
+          base::TimeDelta::FromMilliseconds(5));
+  RunTask(QueueType::COMPOSITOR, Milliseconds(3400),
+          base::TimeDelta::FromMilliseconds(20));
+  RunTask(QueueType::IDLE, Milliseconds(3600),
+          base::TimeDelta::FromMilliseconds(50));
+  RunTask(QueueType::FRAME_LOADING_CONTROL, Milliseconds(4000),
+          base::TimeDelta::FromMilliseconds(5));
+  RunTask(QueueType::CONTROL, Milliseconds(4200),
+          base::TimeDelta::FromMilliseconds(20));
+  RunTask(QueueType::FRAME_TIMER, Milliseconds(4400),
+          base::TimeDelta::FromMilliseconds(115));
+  RunTask(QueueType::FRAME_UNTHROTTLED, Milliseconds(4600),
+          base::TimeDelta::FromMilliseconds(175));
+  RunTask(QueueType::IDLE, Milliseconds(5000),
+          base::TimeDelta::FromMilliseconds(1600));
+
+  std::vector<base::Bucket> expected_samples = {
+      {static_cast<int>(QueueType::CONTROL), 75},
+      {static_cast<int>(QueueType::DEFAULT), 2},
+      {static_cast<int>(QueueType::DEFAULT_LOADING), 20},
+      {static_cast<int>(QueueType::DEFAULT_TIMER), 5},
+      {static_cast<int>(QueueType::UNTHROTTLED), 25},
+      {static_cast<int>(QueueType::FRAME_LOADING), 105},
+      {static_cast<int>(QueueType::FRAME_TIMER), 295},
+      {static_cast<int>(QueueType::FRAME_UNTHROTTLED), 195},
+      {static_cast<int>(QueueType::COMPOSITOR), 45},
+      {static_cast<int>(QueueType::IDLE), 1650},
+      {static_cast<int>(QueueType::TEST), 85},
+      {static_cast<int>(QueueType::FRAME_LOADING_CONTROL), 5}};
+  EXPECT_THAT(histogram_tester_->GetAllSamples(
+                  "RendererScheduler.TaskDurationPerQueueType2"),
+              testing::ContainerEq(expected_samples));
+
+  EXPECT_THAT(
+      histogram_tester_->GetAllSamples(
+          "RendererScheduler.TaskDurationPerQueueType2.Foreground"),
+      ElementsAre(Bucket(static_cast<int>(QueueType::CONTROL), 30),
+                  Bucket(static_cast<int>(QueueType::DEFAULT), 2),
+                  Bucket(static_cast<int>(QueueType::DEFAULT_LOADING), 20),
+                  Bucket(static_cast<int>(QueueType::DEFAULT_TIMER), 5),
+                  Bucket(static_cast<int>(QueueType::FRAME_LOADING), 70),
+                  Bucket(static_cast<int>(QueueType::FRAME_UNTHROTTLED), 20),
+                  Bucket(static_cast<int>(QueueType::COMPOSITOR), 25),
+                  Bucket(static_cast<int>(QueueType::TEST), 85)));
+
+  EXPECT_THAT(
+      histogram_tester_->GetAllSamples(
+          "RendererScheduler.TaskDurationPerQueueType2.Background"),
+      ElementsAre(
+          Bucket(static_cast<int>(QueueType::CONTROL), 45),
+          Bucket(static_cast<int>(QueueType::UNTHROTTLED), 25),
+          Bucket(static_cast<int>(QueueType::FRAME_LOADING), 35),
+          Bucket(static_cast<int>(QueueType::FRAME_TIMER), 295),
+          Bucket(static_cast<int>(QueueType::FRAME_UNTHROTTLED), 175),
+          Bucket(static_cast<int>(QueueType::COMPOSITOR), 20),
+          Bucket(static_cast<int>(QueueType::IDLE), 1650),
+          Bucket(static_cast<int>(QueueType::FRAME_LOADING_CONTROL), 5)));
+}
+
+// TODO(crbug.com/754656): Add tests for NthMinute and AfterNthMinute
+// histograms.
+
+// TODO(crbug.com/754656): Add tests for TaskDuration.Hidden/Visible histograms.
+
+// TODO(crbug.com/754656): Add tests for non-TaskDuration histograms.
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
index a5ddef2..9769f9f 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
@@ -45,20 +45,11 @@
 // Amount of idle time left in a frame (as a ratio of the vsync interval) above
 // which main thread compositing can be considered fast.
 const double kFastCompositingIdleTimeThreshold = .2;
-constexpr base::TimeDelta kThreadLoadTrackerReportingInterval =
-    base::TimeDelta::FromSeconds(1);
 // We do not throttle anything while audio is played and shortly after that.
 constexpr base::TimeDelta kThrottlingDelayAfterAudioIsPlayed =
     base::TimeDelta::FromSeconds(5);
 constexpr base::TimeDelta kQueueingTimeWindowDuration =
     base::TimeDelta::FromSeconds(1);
-// Threshold for discarding ultra-long tasks. It is assumed that ultra-long
-// tasks are reporting glitches (e.g. system falling asleep in the middle
-// of the task).
-constexpr base::TimeDelta kLongTaskDiscardingThreshold =
-    base::TimeDelta::FromSeconds(30);
-constexpr base::TimeDelta kLongIdlePeriodDiscardingThreshold =
-    base::TimeDelta::FromMinutes(3);
 
 }  // namespace
 
@@ -164,12 +155,6 @@
   DCHECK(main_thread_only().was_shutdown);
 }
 
-#define TASK_DURATION_METRIC_NAME "RendererScheduler.TaskDurationPerQueueType2"
-#define TASK_COUNT_METRIC_NAME "RendererScheduler.TaskCountPerQueueType"
-#define MAIN_THREAD_LOAD_METRIC_NAME "RendererScheduler.RendererMainThreadLoad5"
-#define EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME \
-  MAIN_THREAD_LOAD_METRIC_NAME ".Extension"
-
 RendererSchedulerImpl::MainThreadOnly::MainThreadOnly(
     RendererSchedulerImpl* renderer_scheduler_impl,
     const scoped_refptr<MainThreadTaskQueue>& compositor_task_runner,
@@ -185,21 +170,6 @@
                           time_source,
                           kShortIdlePeriodDurationSampleCount,
                           kShortIdlePeriodDurationPercentile),
-      main_thread_load_tracker(
-          now,
-          base::Bind(&RendererSchedulerImpl::RecordMainThreadTaskLoad,
-                     base::Unretained(renderer_scheduler_impl)),
-          kThreadLoadTrackerReportingInterval),
-      background_main_thread_load_tracker(
-          now,
-          base::Bind(&RendererSchedulerImpl::RecordBackgroundMainThreadTaskLoad,
-                     base::Unretained(renderer_scheduler_impl)),
-          kThreadLoadTrackerReportingInterval),
-      foreground_main_thread_load_tracker(
-          now,
-          base::Bind(&RendererSchedulerImpl::RecordForegroundMainThreadTaskLoad,
-                     base::Unretained(renderer_scheduler_impl)),
-          kThreadLoadTrackerReportingInterval),
       current_use_case(UseCase::NONE),
       timer_queue_pause_count(0),
       navigation_task_expected_count(0),
@@ -227,39 +197,8 @@
       background_status_changed_at(now),
       rail_mode_observer(nullptr),
       wake_up_budget_pool(nullptr),
-      task_duration_reporter(TASK_DURATION_METRIC_NAME),
-      foreground_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                        ".Foreground"),
-      foreground_first_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                                     ".Foreground.FirstMinute"),
-      foreground_second_minute_task_duration_reporter(
-          TASK_DURATION_METRIC_NAME ".Foreground.SecondMinute"),
-      foreground_third_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                                     ".Foreground.ThirdMinute"),
-      foreground_after_third_minute_task_duration_reporter(
-          TASK_DURATION_METRIC_NAME ".Foreground.AfterThirdMinute"),
-      background_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                        ".Background"),
-      background_first_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                                     ".Background.FirstMinute"),
-      background_second_minute_task_duration_reporter(
-          TASK_DURATION_METRIC_NAME ".Background.SecondMinute"),
-      background_third_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                                     ".Background.ThirdMinute"),
-      background_fourth_minute_task_duration_reporter(
-          TASK_DURATION_METRIC_NAME ".Background.FourthMinute"),
-      background_fifth_minute_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                                     ".Background.FifthMinute"),
-      background_after_fifth_minute_task_duration_reporter(
-          TASK_DURATION_METRIC_NAME ".Background.AfterFifthMinute"),
-      hidden_task_duration_reporter(TASK_DURATION_METRIC_NAME ".Hidden"),
-      visible_task_duration_reporter(TASK_DURATION_METRIC_NAME ".Visible"),
-      hidden_music_task_duration_reporter(TASK_DURATION_METRIC_NAME
-                                          ".HiddenMusic"),
-      process_type(RendererProcessType::kRenderer) {
-  main_thread_load_tracker.Resume(now);
-  foreground_main_thread_load_tracker.Resume(now);
-}
+      metrics_helper(renderer_scheduler_impl, now, renderer_backgrounded),
+      process_type(RendererProcessType::kRenderer) {}
 
 RendererSchedulerImpl::MainThreadOnly::~MainThreadOnly() {}
 
@@ -283,8 +222,7 @@
 
 void RendererSchedulerImpl::Shutdown() {
   base::TimeTicks now = tick_clock()->NowTicks();
-  main_thread_only().background_main_thread_load_tracker.RecordIdle(now);
-  main_thread_only().foreground_main_thread_load_tracker.RecordIdle(now);
+  main_thread_only().metrics_helper.OnRendererShutdown(now);
 
   task_queue_throttler_.reset();
   helper_.Shutdown();
@@ -612,11 +550,9 @@
 
   base::TimeTicks now = tick_clock()->NowTicks();
   if (backgrounded) {
-    main_thread_only().foreground_main_thread_load_tracker.Pause(now);
-    main_thread_only().background_main_thread_load_tracker.Resume(now);
+    main_thread_only().metrics_helper.OnRendererBackgrounded(now);
   } else {
-    main_thread_only().foreground_main_thread_load_tracker.Resume(now);
-    main_thread_only().background_main_thread_load_tracker.Pause(now);
+    main_thread_only().metrics_helper.OnRendererForegrounded(now);
   }
 }
 
@@ -1969,271 +1905,8 @@
   task_queue_throttler()->OnTaskRunTimeReported(queue, start, end);
 
   // TODO(altimin): Per-page metrics should also be considered.
-  RecordTaskMetrics(queue->queue_type(), start, end);
-}
-
-namespace {
-
-// Calculates the length of the intersection of two given time intervals.
-base::TimeDelta DurationOfIntervalOverlap(base::TimeTicks start1,
-                                          base::TimeTicks end1,
-                                          base::TimeTicks start2,
-                                          base::TimeTicks end2) {
-  DCHECK_LE(start1, end1);
-  DCHECK_LE(start2, end2);
-  return std::max(std::min(end1, end2) - std::max(start1, start2),
-                  base::TimeDelta());
-}
-
-}  // namespace
-
-void RendererSchedulerImpl::RecordTaskMetrics(
-    MainThreadTaskQueue::QueueType queue_type,
-    base::TimeTicks start_time,
-    base::TimeTicks end_time) {
-  base::TimeDelta duration = end_time - start_time;
-  if (duration > kLongTaskDiscardingThreshold)
-    return;
-
-  // Discard anomalously long idle periods.
-  if (main_thread_only().last_reported_task &&
-      start_time - main_thread_only().last_reported_task.value() >
-          kLongIdlePeriodDiscardingThreshold) {
-    main_thread_only().main_thread_load_tracker.Reset(end_time);
-    main_thread_only().foreground_main_thread_load_tracker.Reset(end_time);
-    main_thread_only().background_main_thread_load_tracker.Reset(end_time);
-    return;
-  }
-
-  main_thread_only().last_reported_task = end_time;
-
-  UMA_HISTOGRAM_CUSTOM_COUNTS("RendererScheduler.TaskTime2",
-                              duration.InMicroseconds(), 1, 1000 * 1000, 50);
-
-  // We want to measure thread time here, but for efficiency reasons
-  // we stick with wall time.
-  main_thread_only().main_thread_load_tracker.RecordTaskTime(start_time,
-                                                             end_time);
-  main_thread_only().foreground_main_thread_load_tracker.RecordTaskTime(
-      start_time, end_time);
-  main_thread_only().background_main_thread_load_tracker.RecordTaskTime(
-      start_time, end_time);
-
-  UMA_HISTOGRAM_ENUMERATION(
-      TASK_COUNT_METRIC_NAME, static_cast<int>(queue_type),
-      static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
-
-  if (duration >= base::TimeDelta::FromMilliseconds(16)) {
-    UMA_HISTOGRAM_ENUMERATION(
-        TASK_COUNT_METRIC_NAME ".LongerThan16ms", static_cast<int>(queue_type),
-        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
-  }
-
-  if (duration >= base::TimeDelta::FromMilliseconds(50)) {
-    UMA_HISTOGRAM_ENUMERATION(
-        TASK_COUNT_METRIC_NAME ".LongerThan50ms", static_cast<int>(queue_type),
-        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
-  }
-
-  if (duration >= base::TimeDelta::FromMilliseconds(100)) {
-    UMA_HISTOGRAM_ENUMERATION(
-        TASK_COUNT_METRIC_NAME ".LongerThan100ms", static_cast<int>(queue_type),
-        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
-  }
-
-  if (duration >= base::TimeDelta::FromMilliseconds(150)) {
-    UMA_HISTOGRAM_ENUMERATION(
-        TASK_COUNT_METRIC_NAME ".LongerThan150ms", static_cast<int>(queue_type),
-        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
-  }
-
-  if (duration >= base::TimeDelta::FromSeconds(1)) {
-    UMA_HISTOGRAM_ENUMERATION(
-        TASK_COUNT_METRIC_NAME ".LongerThan1s", static_cast<int>(queue_type),
-        static_cast<int>(MainThreadTaskQueue::QueueType::COUNT));
-  }
-
-  main_thread_only().task_duration_reporter.RecordTask(queue_type, duration);
-
-  if (main_thread_only().renderer_backgrounded) {
-    main_thread_only().background_task_duration_reporter.RecordTask(queue_type,
-                                                                    duration);
-
-    // Collect detailed breakdown for first five minutes given that we stop
-    // timers on mobile after five minutes.
-    base::TimeTicks backgrounded_at =
-        main_thread_only().background_status_changed_at;
-
-    main_thread_only()
-        .background_first_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time, backgrounded_at,
-                            backgrounded_at + base::TimeDelta::FromMinutes(1)));
-
-    main_thread_only()
-        .background_second_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time,
-                            backgrounded_at + base::TimeDelta::FromMinutes(1),
-                            backgrounded_at + base::TimeDelta::FromMinutes(2)));
-
-    main_thread_only()
-        .background_third_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time,
-                            backgrounded_at + base::TimeDelta::FromMinutes(2),
-                            backgrounded_at + base::TimeDelta::FromMinutes(3)));
-
-    main_thread_only()
-        .background_fourth_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time,
-                            backgrounded_at + base::TimeDelta::FromMinutes(3),
-                            backgrounded_at + base::TimeDelta::FromMinutes(4)));
-
-    main_thread_only()
-        .background_fifth_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time,
-                            backgrounded_at + base::TimeDelta::FromMinutes(4),
-                            backgrounded_at + base::TimeDelta::FromMinutes(5)));
-
-    main_thread_only()
-        .background_after_fifth_minute_task_duration_reporter.RecordTask(
-            queue_type,
-            DurationOfIntervalOverlap(
-                start_time, end_time,
-                backgrounded_at + base::TimeDelta::FromMinutes(5),
-                std::max(backgrounded_at + base::TimeDelta::FromMinutes(5),
-                         end_time)));
-  } else {
-    main_thread_only().foreground_task_duration_reporter.RecordTask(queue_type,
-                                                                    duration);
-
-    // For foreground tabs we do not expect such a notable difference as it is
-    // the case with background tabs, so we limit breakdown to three minutes.
-    base::TimeTicks foregrounded_at =
-        main_thread_only().background_status_changed_at;
-
-    main_thread_only()
-        .foreground_first_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time, foregrounded_at,
-                            foregrounded_at + base::TimeDelta::FromMinutes(1)));
-
-    main_thread_only()
-        .foreground_second_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time,
-                            foregrounded_at + base::TimeDelta::FromMinutes(1),
-                            foregrounded_at + base::TimeDelta::FromMinutes(2)));
-
-    main_thread_only()
-        .foreground_third_minute_task_duration_reporter.RecordTask(
-            queue_type, DurationOfIntervalOverlap(
-                            start_time, end_time,
-                            foregrounded_at + base::TimeDelta::FromMinutes(2),
-                            foregrounded_at + base::TimeDelta::FromMinutes(3)));
-
-    main_thread_only()
-        .foreground_after_third_minute_task_duration_reporter.RecordTask(
-            queue_type,
-            DurationOfIntervalOverlap(
-                start_time, end_time,
-                foregrounded_at + base::TimeDelta::FromMinutes(3),
-                std::max(foregrounded_at + base::TimeDelta::FromMinutes(3),
-                         end_time)));
-  }
-
-  if (main_thread_only().renderer_hidden) {
-    main_thread_only().hidden_task_duration_reporter.RecordTask(queue_type,
-                                                                duration);
-
-    if (ShouldDisableThrottlingBecauseOfAudio(start_time)) {
-      main_thread_only().hidden_music_task_duration_reporter.RecordTask(
-          queue_type, duration);
-    }
-  } else {
-    main_thread_only().visible_task_duration_reporter.RecordTask(queue_type,
-                                                                 duration);
-  }
-}
-
-void RendererSchedulerImpl::RecordMainThreadTaskLoad(base::TimeTicks time,
-                                                     double load) {
-  int load_percentage = static_cast<int>(load * 100);
-  DCHECK_LE(load_percentage, 100);
-
-  UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME, load_percentage);
-
-  if (main_thread_only().process_type ==
-      RendererProcessType::kExtensionRenderer) {
-    UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME,
-                             load_percentage);
-  }
-
-  TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
-                 "RendererScheduler.RendererMainThreadLoad", load_percentage);
-}
-
-void RendererSchedulerImpl::RecordForegroundMainThreadTaskLoad(
-    base::TimeTicks time,
-    double load) {
-  int load_percentage = static_cast<int>(load * 100);
-  DCHECK_LE(load_percentage, 100);
-
-  switch (main_thread_only().process_type) {
-    case RendererProcessType::kExtensionRenderer:
-      UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
-                               ".Foreground",
-                               load_percentage);
-      break;
-    case RendererProcessType::kRenderer:
-      UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Foreground",
-                               load_percentage);
-
-      if (time - main_thread_only().background_status_changed_at >
-          base::TimeDelta::FromMinutes(1)) {
-        UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
-                                 ".Foreground.AfterFirstMinute",
-                                 load_percentage);
-      }
-      break;
-  }
-
-  TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
-                 "RendererScheduler.RendererMainThreadLoad.Foreground",
-                 load_percentage);
-}
-
-void RendererSchedulerImpl::RecordBackgroundMainThreadTaskLoad(
-    base::TimeTicks time,
-    double load) {
-  int load_percentage = static_cast<int>(load * 100);
-  DCHECK_LE(load_percentage, 100);
-
-  switch (main_thread_only().process_type) {
-    case RendererProcessType::kExtensionRenderer:
-      UMA_HISTOGRAM_PERCENTAGE(EXTENSIONS_MAIN_THREAD_LOAD_METRIC_NAME
-                               ".Background",
-                               load_percentage);
-      break;
-    case RendererProcessType::kRenderer:
-      UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME ".Background",
-                               load_percentage);
-
-      if (time - main_thread_only().background_status_changed_at >
-          base::TimeDelta::FromMinutes(1)) {
-        UMA_HISTOGRAM_PERCENTAGE(MAIN_THREAD_LOAD_METRIC_NAME
-                                 ".Background.AfterFirstMinute",
-                                 load_percentage);
-      }
-      break;
-  }
-
-  TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
-                 "RendererScheduler.RendererMainThreadLoad.Background",
-                 load_percentage);
+  main_thread_only().metrics_helper.RecordTaskMetrics(queue->queue_type(),
+                                                      start, end);
 }
 
 void RendererSchedulerImpl::OnBeginNestedRunLoop() {
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
index d314ef9..eb11315 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
@@ -14,10 +14,10 @@
 #include "base/synchronization/lock.h"
 #include "base/trace_event/trace_log.h"
 #include "device/base/synchronization/shared_memory_seqlock_buffer.h"
+#include "platform/PlatformExport.h"
 #include "platform/scheduler/base/pollable_thread_safe_flag.h"
 #include "platform/scheduler/base/queueing_time_estimator.h"
 #include "platform/scheduler/base/task_time_observer.h"
-#include "platform/scheduler/base/thread_load_tracker.h"
 #include "platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
 #include "platform/scheduler/child/idle_helper.h"
 #include "platform/scheduler/renderer/deadline_task_runner.h"
@@ -25,8 +25,8 @@
 #include "platform/scheduler/renderer/main_thread_scheduler_helper.h"
 #include "platform/scheduler/renderer/main_thread_task_queue.h"
 #include "platform/scheduler/renderer/render_widget_signals.h"
+#include "platform/scheduler/renderer/renderer_metrics_helper.h"
 #include "platform/scheduler/renderer/task_cost_estimator.h"
-#include "platform/scheduler/renderer/task_duration_metric_reporter.h"
 #include "platform/scheduler/renderer/user_model.h"
 #include "platform/scheduler/renderer/web_view_scheduler_impl.h"
 #include "public/platform/scheduler/renderer/renderer_scheduler.h"
@@ -253,9 +253,12 @@
   scoped_refptr<base::SingleThreadTaskRunner> TimerTaskRunner() override;
 
  private:
-  friend class RendererSchedulerImplTest;
-  friend class RendererSchedulerImplForTest;
   friend class RenderWidgetSchedulingState;
+  friend class RendererMetricsHelper;
+
+  friend class RendererMetricsHelperTest;
+  friend class RendererSchedulerImplForTest;
+  friend class RendererSchedulerImplTest;
   FRIEND_TEST_ALL_PREFIXES(RendererSchedulerImplTest, Tracing);
 
   enum class ExpensiveTaskPolicy { RUN, BLOCK, THROTTLE };
@@ -505,14 +508,6 @@
 
   void AddQueueToWakeUpBudgetPool(MainThreadTaskQueue* queue);
 
-  void RecordTaskMetrics(MainThreadTaskQueue::QueueType queue_type,
-                         base::TimeTicks start_time,
-                         base::TimeTicks end_time);
-
-  void RecordMainThreadTaskLoad(base::TimeTicks time, double load);
-  void RecordForegroundMainThreadTaskLoad(base::TimeTicks time, double load);
-  void RecordBackgroundMainThreadTaskLoad(base::TimeTicks time, double load);
-
   MainThreadSchedulerHelper helper_;
   IdleHelper idle_helper_;
   IdleCanceledDelayedTaskSweeper idle_canceled_delayed_task_sweeper_;
@@ -560,9 +555,6 @@
     TaskCostEstimator loading_task_cost_estimator;
     TaskCostEstimator timer_task_cost_estimator;
     IdleTimeEstimator idle_time_estimator;
-    ThreadLoadTracker main_thread_load_tracker;
-    ThreadLoadTracker background_main_thread_load_tracker;
-    ThreadLoadTracker foreground_main_thread_load_tracker;
     UseCase current_use_case;
     Policy current_policy;
     base::TimeTicks current_policy_expiration_time;
@@ -601,25 +593,7 @@
     std::set<WebViewSchedulerImpl*> web_view_schedulers;  // Not owned.
     RAILModeObserver* rail_mode_observer;                 // Not owned.
     WakeUpBudgetPool* wake_up_budget_pool;                // Not owned.
-    base::Optional<base::TimeTicks> last_reported_task;
-    TaskDurationMetricReporter task_duration_reporter;
-    TaskDurationMetricReporter foreground_task_duration_reporter;
-    TaskDurationMetricReporter foreground_first_minute_task_duration_reporter;
-    TaskDurationMetricReporter foreground_second_minute_task_duration_reporter;
-    TaskDurationMetricReporter foreground_third_minute_task_duration_reporter;
-    TaskDurationMetricReporter
-        foreground_after_third_minute_task_duration_reporter;
-    TaskDurationMetricReporter background_task_duration_reporter;
-    TaskDurationMetricReporter background_first_minute_task_duration_reporter;
-    TaskDurationMetricReporter background_second_minute_task_duration_reporter;
-    TaskDurationMetricReporter background_third_minute_task_duration_reporter;
-    TaskDurationMetricReporter background_fourth_minute_task_duration_reporter;
-    TaskDurationMetricReporter background_fifth_minute_task_duration_reporter;
-    TaskDurationMetricReporter
-        background_after_fifth_minute_task_duration_reporter;
-    TaskDurationMetricReporter hidden_task_duration_reporter;
-    TaskDurationMetricReporter visible_task_duration_reporter;
-    TaskDurationMetricReporter hidden_music_task_duration_reporter;
+    RendererMetricsHelper metrics_helper;
     RendererProcessType process_type;
   };
 
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 7799699..fb46e7d1 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -474,7 +474,6 @@
     "web/WebColorChooser.h",
     "web/WebColorChooserClient.h",
     "web/WebColorSuggestion.h",
-    "web/WebCompositionUnderline.h",
     "web/WebConsoleMessage.h",
     "web/WebContextFeatures.h",
     "web/WebContextMenuData.h",
@@ -528,6 +527,7 @@
     "web/WebIconURL.h",
     "web/WebImageCache.h",
     "web/WebImageDecoder.h",
+    "web/WebImeTextSpan.h",
     "web/WebInputElement.h",
     "web/WebInputMethodController.h",
     "web/WebKit.h",
diff --git a/third_party/WebKit/public/web/WebCompositionUnderline.h b/third_party/WebKit/public/web/WebImeTextSpan.h
similarity index 80%
rename from third_party/WebKit/public/web/WebCompositionUnderline.h
rename to third_party/WebKit/public/web/WebImeTextSpan.h
index 0b7f83e..53b1314 100644
--- a/third_party/WebKit/public/web/WebCompositionUnderline.h
+++ b/third_party/WebKit/public/web/WebImeTextSpan.h
@@ -28,41 +28,37 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef WebCompositionUnderline_h
-#define WebCompositionUnderline_h
+#ifndef WebImeTextSpan_h
+#define WebImeTextSpan_h
 
 #include "public/platform/WebColor.h"
 
 namespace blink {
 
-// Class WebCompositionUnderline is intended to be used with WebWidget's
+// Class WebImeTextSpan is intended to be used with WebWidget's
 // setComposition() method.
-struct WebCompositionUnderline {
-  WebCompositionUnderline()
+struct WebImeTextSpan {
+  WebImeTextSpan()
       : start_offset(0),
         end_offset(0),
         color(0),
         thick(false),
         background_color(0) {}
 
-  WebCompositionUnderline(unsigned s,
-                          unsigned e,
-                          WebColor c,
-                          bool t,
-                          WebColor bc)
+  WebImeTextSpan(unsigned s, unsigned e, WebColor c, bool t, WebColor bc)
       : start_offset(s),
         end_offset(e),
         color(c),
         thick(t),
         background_color(bc) {}
 
-  bool operator<(const WebCompositionUnderline& other) const {
+  bool operator<(const WebImeTextSpan& other) const {
     return start_offset != other.start_offset
                ? start_offset < other.start_offset
                : end_offset < other.end_offset;
   }
 
-  // Need to update IPC_STRUCT_TRAITS_BEGIN(blink::WebCompositionUnderline)
+  // Need to update IPC_STRUCT_TRAITS_BEGIN(blink::WebImeTextSpan)
   // if members change.
   unsigned start_offset;
   unsigned end_offset;
diff --git a/third_party/WebKit/public/web/WebInputMethodController.h b/third_party/WebKit/public/web/WebInputMethodController.h
index d95ee7e..161d47f 100644
--- a/third_party/WebKit/public/web/WebInputMethodController.h
+++ b/third_party/WebKit/public/web/WebInputMethodController.h
@@ -5,9 +5,9 @@
 #ifndef WebInputMethodController_h
 #define WebInputMethodController_h
 
-#include "public/platform/WebTextInputInfo.h"
-#include "WebCompositionUnderline.h"
+#include "WebImeTextSpan.h"
 #include "WebWidget.h"
+#include "public/platform/WebTextInputInfo.h"
 
 namespace blink {
 
@@ -31,19 +31,18 @@
   // text will be canceled. |replacementRange| (when not null) is the range in
   // current text which should be replaced by |text|. Returns true if the
   // composition text was set successfully.
-  virtual bool SetComposition(
-      const WebString& text,
-      const WebVector<WebCompositionUnderline>& underlines,
-      const WebRange& replacement_range,
-      int selection_start,
-      int selection_end) = 0;
+  virtual bool SetComposition(const WebString& text,
+                              const WebVector<WebImeTextSpan>& ime_text_spans,
+                              const WebRange& replacement_range,
+                              int selection_start,
+                              int selection_end) = 0;
 
   // Called to inform the controller to delete the ongoing composition if any,
   // insert |text|, and move the caret according to |relativeCaretPosition|.
   // |replacementRange| (when not null) is the range in current text which
   // should be replaced by |text|.
   virtual bool CommitText(const WebString& text,
-                          const WebVector<WebCompositionUnderline>& underlines,
+                          const WebVector<WebImeTextSpan>& ime_text_spans,
                           const WebRange& replacement_range,
                           int relative_caret_position) = 0;
 
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index b9ef892..d14a18de 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -8,10 +8,10 @@
 #include <memory>
 #include <set>
 
-#include "WebCompositionUnderline.h"
 #include "WebFrame.h"
 #include "WebFrameLoadType.h"
 #include "WebHistoryItem.h"
+#include "WebImeTextSpan.h"
 #include "public/platform/WebCachePolicy.h"
 #include "public/platform/WebFocusType.h"
 #include "public/platform/WebSize.h"
@@ -503,7 +503,7 @@
   virtual bool SetCompositionFromExistingText(
       int composition_start,
       int composition_end,
-      const WebVector<WebCompositionUnderline>& underlines) = 0;
+      const WebVector<WebImeTextSpan>& ime_text_spans) = 0;
   virtual void ExtendSelectionAndDelete(int before, int after) = 0;
 
   virtual void SetCaretVisible(bool) = 0;
diff --git a/third_party/WebKit/public/web/WebPlugin.h b/third_party/WebKit/public/web/WebPlugin.h
index ff9fe31..1db7778 100644
--- a/third_party/WebKit/public/web/WebPlugin.h
+++ b/third_party/WebKit/public/web/WebPlugin.h
@@ -47,7 +47,7 @@
 class WebDragData;
 class WebPluginContainer;
 class WebURLResponse;
-struct WebCompositionUnderline;
+struct WebImeTextSpan;
 struct WebCursorInfo;
 struct WebPrintParams;
 struct WebPrintPresetOptions;
@@ -170,12 +170,11 @@
   // Sets composition text from input method, and returns true if the
   // composition is set successfully. If |replacementRange| is not null, the
   // text inside |replacementRange| will be replaced by |text|
-  virtual bool SetComposition(
-      const WebString& text,
-      const WebVector<WebCompositionUnderline>& underlines,
-      const WebRange& replacement_range,
-      int selection_start,
-      int selection_end) {
+  virtual bool SetComposition(const WebString& text,
+                              const WebVector<WebImeTextSpan>& ime_text_spans,
+                              const WebRange& replacement_range,
+                              int selection_start,
+                              int selection_end) {
     return false;
   }
 
@@ -183,7 +182,7 @@
   // moves the caret according to relativeCaretPosition. If |replacementRange|
   // is not null, the text inside |replacementRange| will be replaced by |text|.
   virtual bool CommitText(const WebString& text,
-                          const WebVector<WebCompositionUnderline>& underlines,
+                          const WebVector<WebImeTextSpan>& ime_text_spans,
                           const WebRange& replacement_range,
                           int relative_caret_position) {
     return false;
diff --git a/third_party/WebKit/public/web/WebWidget.h b/third_party/WebKit/public/web/WebWidget.h
index 2a34655..bf139ef3 100644
--- a/third_party/WebKit/public/web/WebWidget.h
+++ b/third_party/WebKit/public/web/WebWidget.h
@@ -41,7 +41,7 @@
 #include "public/platform/WebRect.h"
 #include "public/platform/WebSize.h"
 #include "public/platform/WebTextInputInfo.h"
-#include "public/web/WebCompositionUnderline.h"
+#include "public/web/WebImeTextSpan.h"
 #include "public/web/WebRange.h"
 #include "public/web/WebTextDirection.h"
 
diff --git a/third_party/bazel/desugar/BUILD.gn b/third_party/bazel/desugar/BUILD.gn
new file mode 100644
index 0000000..bff746ab
--- /dev/null
+++ b/third_party/bazel/desugar/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+import("//build/config/android/rules.gni")
+
+# Provides runtime support for desugar transformations.
+java_prebuilt("desugar_runtime_java") {
+  supports_android = true
+  jar_path = "Desugar-runtime.jar"
+}
diff --git a/third_party/bazel/desugar/Desugar-runtime.jar b/third_party/bazel/desugar/Desugar-runtime.jar
new file mode 100644
index 0000000..b04c6b83
--- /dev/null
+++ b/third_party/bazel/desugar/Desugar-runtime.jar
Binary files differ
diff --git a/third_party/bazel/desugar/README.chromium b/third_party/bazel/desugar/README.chromium
index 8243288..368566f 100644
--- a/third_party/bazel/desugar/README.chromium
+++ b/third_party/bazel/desugar/README.chromium
@@ -14,8 +14,10 @@
 such as lambda experssions for Chrome on Android.
 
 Local Modifications:
-Desugar.jar is the "Desugar_deploy.jar" target defined in
-[bazel]src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
+* Desugar.jar is the "Desugar_deploy.jar" target defined in
+  [bazel]src/tools/android/java/com/google/devtools/build/android/desugar/BUILD
+* Desugar-runtime.jar is derived from Desugar.jar (refer to update
+  instructions).
 
 Update instructions (requires @google.com account for uploading):
 - Check out Bazel from https://github.com/bazelbuild/bazel
@@ -30,4 +32,7 @@
 $ download_from_google_storage --config
 - Upload new jar to gcloud. In third_party/bazel/desugar, run
 $ upload_to_google_storage.py -b chromium-android-tools/bazel/desugar Desugar.jar
-
+- Update Desugar-runtime.jar:
+$ unzip Desugar.jar "com/google/devtools/build/android/desugar/runtime*"
+$ zip -rD0 Desugar-runtime.jar com
+$ rm -r com
diff --git a/third_party/gvr-android-sdk/BUILD.gn b/third_party/gvr-android-sdk/BUILD.gn
index 223ba74..b00908d 100644
--- a/third_party/gvr-android-sdk/BUILD.gn
+++ b/third_party/gvr-android-sdk/BUILD.gn
@@ -7,6 +7,9 @@
 android_aar_prebuilt("controller_test_api_java") {
   aar_path = "test-libraries/controller_test_api.aar"
   proguard_configs = [ "test-libraries/proguard.txt" ]
+
+  # Jar includes conflicting copies of Desugar-runtime.jar classes.
+  jar_excluded_patterns = [ "*ThrowableExtension*.class" ]
 }
 
 android_aar_prebuilt("gvr_common_java") {
diff --git a/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt b/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
index 9200f48..f70d4aa 100644
--- a/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
+++ b/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
@@ -7,3 +7,4 @@
 v1.5 a02e2f95aa6f741f2be0ae03a4829e21fd749cf3
 v1.6 43b876df3398687dfa1ae059ef2f64009c76254e
 v1.7 d5c72438acffe723e7717c45deedd8431bc613e7
+v1.8 cbc81aa4db5986f25d2c967f9b1e247cf07cd75e
diff --git a/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1 b/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
index 31836cd7..11fa641 100644
--- a/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
+++ b/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
@@ -1 +1 @@
-d5c72438acffe723e7717c45deedd8431bc613e7
\ No newline at end of file
+cbc81aa4db5986f25d2c967f9b1e247cf07cd75e
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt b/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
index 4831ae6..2b7c0f5c 100644
--- a/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
+++ b/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
@@ -10,3 +10,4 @@
 v1.5 5d6d55728c7c728cef5416f37b0b71615719474e
 v1.6 abcdae2281956a76aa3b98d2e8f05a1975170dd0
 v1.7 fcfa178173a2c0cab9c7d51829c2ee76ab66e1d9
+v1.8 f8f45ebf1963c5f9862218baca120ea974b87a08
diff --git a/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1 b/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
index fb68207..c4102cf 100644
--- a/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
+++ b/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
@@ -1 +1 @@
-fcfa178173a2c0cab9c7d51829c2ee76ab66e1d9
\ No newline at end of file
+f8f45ebf1963c5f9862218baca120ea974b87a08
\ No newline at end of file
diff --git a/tools/json_schema_compiler/test/features_generation_unittest.cc b/tools/json_schema_compiler/test/features_generation_unittest.cc
index 28a90ed..81ab003 100644
--- a/tools/json_schema_compiler/test/features_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/features_generation_unittest.cc
@@ -35,7 +35,7 @@
   explicit FeatureComparator(const std::string& name);
   ~FeatureComparator();
 
-  void CompareFeature(SimpleFeature* feature);
+  void CompareFeature(const SimpleFeature* feature);
 
   std::string name;
   std::vector<std::string> blacklist;
@@ -66,7 +66,7 @@
 
 FeatureComparator::~FeatureComparator() {}
 
-void FeatureComparator::CompareFeature(SimpleFeature* feature) {
+void FeatureComparator::CompareFeature(const SimpleFeature* feature) {
   ASSERT_TRUE(feature);
   EXPECT_EQ(name, feature->name());
   ExpectVectorsEqual(blacklist, feature->blacklist(), name);
@@ -95,22 +95,22 @@
   CompilerTestFeatureProvider provider;
 
   auto GetAsSimpleFeature = [&provider](const std::string& name) {
-    Feature* feature = provider.GetFeature(name);
+    const Feature* feature = provider.GetFeature(name);
     // Shame we can't test this more safely, but if our feature is declared as
     // the wrong class, things should blow up in a spectacular fashion.
-    return static_cast<SimpleFeature*>(feature);
+    return static_cast<const SimpleFeature*>(feature);
   };
 
   auto GetAsComplexFeature = [&provider](const std::string& name) {
-    Feature* feature = provider.GetFeature(name);
+    const Feature* feature = provider.GetFeature(name);
     // Shame we can't test this more safely, but if our feature is declared as
     // the wrong class, things should blow up in a spectacular fashion.
-    return static_cast<ComplexFeature*>(feature);
+    return static_cast<const ComplexFeature*>(feature);
   };
 
   // Check some simple features for accuracy.
   {
-    SimpleFeature* feature = GetAsSimpleFeature("alpha");
+    const SimpleFeature* feature = GetAsSimpleFeature("alpha");
     FeatureComparator comparator("alpha");
     comparator.dependencies = {"permission:alpha"};
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
@@ -120,7 +120,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    SimpleFeature* feature = GetAsSimpleFeature("beta");
+    const SimpleFeature* feature = GetAsSimpleFeature("beta");
     FeatureComparator comparator("beta");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
     comparator.channel.reset(
@@ -134,7 +134,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    SimpleFeature* feature = GetAsSimpleFeature("gamma");
+    const SimpleFeature* feature = GetAsSimpleFeature("gamma");
     FeatureComparator comparator("gamma");
     comparator.channel.reset(
         new version_info::Channel(version_info::Channel::BETA));
@@ -157,7 +157,7 @@
   {
     // Features that specify 'noparent' should not inherit features from any
     // other feature.
-    SimpleFeature* feature = GetAsSimpleFeature("gamma.unparented");
+    const SimpleFeature* feature = GetAsSimpleFeature("gamma.unparented");
     FeatureComparator comparator("gamma.unparented");
     comparator.blacklist = {"ddd"};
     comparator.contexts = {Feature::UNBLESSED_EXTENSION_CONTEXT};
@@ -166,7 +166,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    ComplexFeature* complex_feature =
+    const ComplexFeature* complex_feature =
         GetAsComplexFeature("gamma.complex_unparented");
     FeatureComparator comparator("gamma.complex_unparented");
     comparator.contexts = {Feature::UNBLESSED_EXTENSION_CONTEXT};
@@ -178,7 +178,7 @@
       comparator.CompareFeature(static_cast<SimpleFeature*>(feature.get()));
   }
   {
-    SimpleFeature* feature = GetAsSimpleFeature("delta");
+    const SimpleFeature* feature = GetAsSimpleFeature("delta");
     FeatureComparator comparator("delta");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT,
                            Feature::WEBUI_CONTEXT};
@@ -190,7 +190,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    SimpleFeature* feature = GetAsSimpleFeature("allEnum");
+    const SimpleFeature* feature = GetAsSimpleFeature("allEnum");
     FeatureComparator comparator("allEnum");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT,
                            Feature::BLESSED_WEB_PAGE_CONTEXT,
@@ -210,7 +210,7 @@
   }
   {
     // Omega is imported from a second .json file.
-    SimpleFeature* feature = GetAsSimpleFeature("omega");
+    const SimpleFeature* feature = GetAsSimpleFeature("omega");
     FeatureComparator comparator("omega");
     comparator.contexts = {Feature::WEB_PAGE_CONTEXT};
     comparator.channel.reset(
@@ -220,24 +220,24 @@
   }
   {
     // Features specifying 'nocompile' should not be generated at all.
-    SimpleFeature* feature = GetAsSimpleFeature("uncompiled");
+    const SimpleFeature* feature = GetAsSimpleFeature("uncompiled");
     EXPECT_FALSE(feature);
   }
 
   // Test complex features.
   {
-    ComplexFeature* feature = GetAsComplexFeature("complex");
+    const ComplexFeature* feature = GetAsComplexFeature("complex");
     ASSERT_TRUE(feature);
     EXPECT_EQ(2u, feature->features_.size());
     // Find the default parent. This is a little tedious because it might not
     // be guaranteed that the default_parent is in a specific index, but it
     // specifies channel as 'stable'.
-    SimpleFeature* default_parent = nullptr;
-    SimpleFeature* other_parent = nullptr;
+    const SimpleFeature* default_parent = nullptr;
+    const SimpleFeature* other_parent = nullptr;
     {
-      SimpleFeature* parent1 =
+      const SimpleFeature* parent1 =
           static_cast<SimpleFeature*>(feature->features_[0].get());
-      SimpleFeature* parent2 =
+      const SimpleFeature* parent2 =
           static_cast<SimpleFeature*>(feature->features_[1].get());
       if (parent1->channel() == version_info::Channel::STABLE) {
         default_parent = parent1;
@@ -257,7 +257,7 @@
       comparator.CompareFeature(default_parent);
       // Check the child of the complex feature. It should inherit its
       // properties from the default parent.
-      SimpleFeature* child_feature = GetAsSimpleFeature("complex.child");
+      const SimpleFeature* child_feature = GetAsSimpleFeature("complex.child");
       comparator.name = "complex.child";
       comparator.platforms = {Feature::WIN_PLATFORM};
       comparator.dependencies = {"permission:complex.child"};
@@ -277,7 +277,7 @@
 
   // Test API aliases.
   {
-    SimpleFeature* feature = GetAsSimpleFeature("alias");
+    const SimpleFeature* feature = GetAsSimpleFeature("alias");
     FeatureComparator comparator("alias");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
     comparator.channel.reset(
@@ -286,7 +286,7 @@
     comparator.CompareFeature(feature);
   }
   {
-    SimpleFeature* feature = GetAsSimpleFeature("alias_source");
+    const SimpleFeature* feature = GetAsSimpleFeature("alias_source");
     FeatureComparator comparator("alias_source");
     comparator.contexts = {Feature::BLESSED_EXTENSION_CONTEXT};
     comparator.channel.reset(
@@ -295,37 +295,37 @@
     comparator.CompareFeature(feature);
   }
   {
-    Feature* feature = provider.GetFeature("complex_alias");
+    const Feature* feature = provider.GetFeature("complex_alias");
     ASSERT_EQ("", feature->alias());
     ASSERT_EQ("complex_alias_source", feature->source());
   }
   {
-    Feature* feature = provider.GetFeature("complex_alias_source");
+    const Feature* feature = provider.GetFeature("complex_alias_source");
     ASSERT_EQ("complex_alias", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = provider.GetFeature("parent_source");
+    const Feature* feature = provider.GetFeature("parent_source");
     ASSERT_EQ("parent_source_alias", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = provider.GetFeature("parent_source.child");
+    const Feature* feature = provider.GetFeature("parent_source.child");
     ASSERT_EQ("parent_source_alias", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = provider.GetFeature("parent_source.child_source");
+    const Feature* feature = provider.GetFeature("parent_source.child_source");
     ASSERT_EQ("parent_source_child_alias", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = provider.GetFeature("alias_parent");
+    const Feature* feature = provider.GetFeature("alias_parent");
     ASSERT_EQ("", feature->alias());
     ASSERT_EQ("", feature->source());
   }
   {
-    Feature* feature = provider.GetFeature("alias_parent.child");
+    const Feature* feature = provider.GetFeature("alias_parent.child");
     ASSERT_EQ("", feature->alias());
     ASSERT_EQ("child_source", feature->source());
   }
diff --git a/tools/luci-go/linux64/isolate.sha1 b/tools/luci-go/linux64/isolate.sha1
index acecc79..f923147 100644
--- a/tools/luci-go/linux64/isolate.sha1
+++ b/tools/luci-go/linux64/isolate.sha1
@@ -1 +1 @@
-21410c557b49620e8a44ec0f861f94605bdc6d5c
+48ffe036be8eff7d39ebbdbb705bd26f0ec6f404
diff --git a/tools/luci-go/mac64/isolate.sha1 b/tools/luci-go/mac64/isolate.sha1
index 16a7dd6..328cc06 100644
--- a/tools/luci-go/mac64/isolate.sha1
+++ b/tools/luci-go/mac64/isolate.sha1
@@ -1 +1 @@
-1966687828a068eee4c5da45bbb8afd91cddda6f
+f0d9ea71e7059a164962658b588286ebf262c5dd
diff --git a/tools/luci-go/win64/isolate.exe.sha1 b/tools/luci-go/win64/isolate.exe.sha1
index 6f5491d..8038c79 100644
--- a/tools/luci-go/win64/isolate.exe.sha1
+++ b/tools/luci-go/win64/isolate.exe.sha1
@@ -1 +1 @@
-35482264cea0f9b9dd2efe0a01620557fc15b7c1
+40790017e9b7856009c36768bf9244a4182ad5d1
diff --git a/tools/mb/docs/user_guide.md b/tools/mb/docs/user_guide.md
index d31ea67..0abb235 100644
--- a/tools/mb/docs/user_guide.md
+++ b/tools/mb/docs/user_guide.md
@@ -167,6 +167,18 @@
 This is mostly useful as a presubmit check and for verifying changes to
 the config file.
 
+### `mb gerrit-buildbucket-config`
+
+Generates a gerrit buildbucket configuration file and prints it to
+stdout. This file contains the list of trybots shown in gerrit's UI.
+
+The master copy of the buildbucket.config file lives
+in a separate branch of the chromium repository. Run `mb
+gerrit-buildbucket-config > buildbucket.config.new && git fetch origin
+refs/meta/config:refs/remotes/origin/meta/config && git checkout
+-t -b meta_config origin/meta/config && mv buildbucket.config.new
+buildbucket.config` to update the file.
+
 ## Isolates and Swarming
 
 `mb gen` is also responsible for generating the `.isolate` and
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 6905bcc..90c42bf 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -56,6 +56,7 @@
     self.sep = os.sep
     self.args = argparse.Namespace()
     self.configs = {}
+    self.luci_tryservers = {}
     self.masters = {}
     self.mixins = {}
 
@@ -235,6 +236,14 @@
                            ' do compiles')
     subp.set_defaults(func=self.CmdAudit)
 
+    subp = subps.add_parser('gerrit-buildbucket-config',
+                            help='Print buildbucket.config for gerrit '
+                            '(see MB user guide)')
+    subp.add_argument('-f', '--config-file', metavar='PATH',
+                      default=self.default_config,
+                      help='path to config file (default is %(default)s)')
+    subp.set_defaults(func=self.CmdBuildbucket)
+
     subp = subps.add_parser('help',
                             help='Get help on a subcommand.')
     subp.add_argument(nargs='?', action='store', dest='subcommand',
@@ -368,6 +377,25 @@
 
     return ret
 
+  def CmdBuildbucket(self):
+    self.ReadConfigFile()
+
+    self.Print('# This file was generated using '
+               '"tools/mb/mb.py gerrit-buildbucket-config".')
+
+    for luci_tryserver in sorted(self.luci_tryservers):
+      self.Print('[bucket "luci.%s"]' % luci_tryserver)
+      for bot in sorted(self.luci_tryservers[luci_tryserver]):
+        self.Print('\tbuilder = %s' % bot)
+
+    for master in sorted(self.masters):
+      if master.startswith('tryserver.'):
+        self.Print('[bucket "master.%s"]' % master)
+        for bot in sorted(self.masters[master]):
+          self.Print('\tbuilder = %s' % bot)
+
+    return 0
+
   def CmdValidate(self, print_ok=True):
     errs = []
 
@@ -674,6 +702,7 @@
                  (self.args.config_file, e))
 
     self.configs = contents['configs']
+    self.luci_tryservers = contents.get('luci_tryservers', {})
     self.masters = contents['masters']
     self.mixins = contents['mixins']
 
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 063438b..32c68f6 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -525,6 +525,7 @@
       'cast_shell_audio_linux': 'cast_audio_release_trybot',
       'chromeos_amd64-generic_chromium_compile_only_ng': 'cros_chrome_sdk',
       'chromeos_daisy_chromium_compile_only_ng': 'cros_chrome_sdk',
+      'chromium_presubmit': 'presubmit',
       'closure_compilation': 'closure_compilation',
       'fuchsia': 'release_trybot_fuchsia',
       'fuchsia_compile': 'release_trybot_fuchsia',
@@ -1415,6 +1416,11 @@
       'ozone_linux', 'release_trybot',
     ],
 
+    'presubmit': [
+      # The chromium_presubmit bot does not use mb.
+      'error',
+    ],
+
     'release_afl_asan': [
       'release', 'afl', 'asan', 'chromeos_codecs', 'pdf_xfa', 'disable_nacl',
     ],
@@ -1995,4 +2001,8 @@
       'gn_args': 'target_cpu="x86"',
     },
   },
+
+  'luci_tryservers': {
+    'chromium.try': [ 'LUCI linux_chromium_rel_ng' ],
+  },
 }
diff --git a/tools/mb/mb_unittest.py b/tools/mb/mb_unittest.py
index 29b75cf..73c3e9c 100755
--- a/tools/mb/mb_unittest.py
+++ b/tools/mb/mb_unittest.py
@@ -220,6 +220,28 @@
 }
 """
 
+TRYSERVER_CONFIG = """\
+{
+  'masters': {
+    'not_a_tryserver': {
+      'fake_builder': 'fake_config',
+    },
+    'tryserver.chromium.linux': {
+      'try_builder': 'fake_config',
+    },
+    'tryserver.chromium.mac': {
+      'try_builder2': 'fake_config',
+    },
+  },
+  'luci_tryservers': {
+    'luci_tryserver1': ['luci_builder1'],
+    'luci_tryserver2': ['luci_builder2'],
+  },
+  'configs': {},
+  'mixins': {},
+}
+"""
+
 
 class UnitTest(unittest.TestCase):
   def fake_mbw(self, files=None, win32=False):
@@ -555,6 +577,22 @@
                     "LLVM_FORCE_HEAD_REVISION=1\n"
                     "python build/gyp_chromium -G output_dir=_path_\n"))
 
+  def test_buildbucket(self):
+    mbw = self.fake_mbw()
+    mbw.files[mbw.default_config] = TRYSERVER_CONFIG
+    self.check(['gerrit-buildbucket-config'], mbw=mbw,
+               ret=0,
+               out=('# This file was generated using '
+                    '"tools/mb/mb.py gerrit-buildbucket-config".\n'
+                    '[bucket "luci.luci_tryserver1"]\n'
+                    '\tbuilder = luci_builder1\n'
+                    '[bucket "luci.luci_tryserver2"]\n'
+                    '\tbuilder = luci_builder2\n'
+                    '[bucket "master.tryserver.chromium.linux"]\n'
+                    '\tbuilder = try_builder\n'
+                    '[bucket "master.tryserver.chromium.mac"]\n'
+                    '\tbuilder = try_builder2\n'))
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 3b98f244..03704cd 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22641,6 +22641,23 @@
   <int value="4" label="Success"/>
 </enum>
 
+<enum name="LockScreenActionAvailability">
+  <int value="0" label="Available"/>
+  <int value="1" label="Not available - no action handler app"/>
+  <int value="2" label="Not available - app with no lock screen support"/>
+  <int value="3" label="Not available - action not enabled on lock screen"/>
+  <int value="4" label="Not available - disallowed by policy"/>
+</enum>
+
+<enum name="LockScreenAppSessionState">
+  <int value="0" label="Not launched"/>
+  <int value="1" label="Launching"/>
+  <int value="2" label="App window created"/>
+  <int value="3" label="App window shown"/>
+  <int value="4" label="App window in foreground"/>
+  <int value="5" label="App window in background"/>
+</enum>
+
 <enum name="LockScreenDataItemOperationResult">
   <int value="0" label="Success"/>
   <int value="1" label="Operation failed"/>
@@ -22651,6 +22668,22 @@
   <int value="6" label="Wrong encryption key"/>
 </enum>
 
+<enum name="LockScreenNoteTakingExitReason">
+  <int value="0" label="Session was unlocked"/>
+  <int value="1" label="Session shutdown"/>
+  <int value="2" label="Screen turned off"/>
+  <int value="3" label="Device suspended"/>
+  <int value="4" label="App window closed"/>
+  <int value="5" label="Lock screen note taking disabled"/>
+</enum>
+
+<enum name="LockScreenNoteTakingUnlockUIAction">
+  <int value="0" label="User unlocked the session"/>
+  <int value="1" label="Session unlock was cancelled"/>
+  <int value="2" label="Device shut down"/>
+  <int value="3" label="User signed out"/>
+</enum>
+
 <enum name="LockScreenProgress">
   <int value="0" label="Start screen lock"/>
   <int value="1" label="Enter password correctly"/>
@@ -22670,6 +22703,7 @@
   <int value="-2143328006"
       label="enable-fill-on-account-select-no-highlighting"/>
   <int value="-2143113994" label="enable-ephemeral-apps-in-webstore"/>
+  <int value="-2141661938" label="AnimatedAppMenuIcon:enabled"/>
   <int value="-2134717874" label="Multidevice:disabled"/>
   <int value="-2134333982" label="ShowArcFilesApp:enabled"/>
   <int value="-2134244069" label="HttpFormWarning:enabled"/>
@@ -23505,6 +23539,7 @@
   <int value="506680761" label="WebNFC:disabled"/>
   <int value="510814146" label="OfflineBookmarks:enabled"/>
   <int value="513356954" label="InstantTethering:disabled"/>
+  <int value="517568645" label="AnimatedAppMenuIcon:disabled"/>
   <int value="535131384" label="OmniboxTailSuggestions:enabled"/>
   <int value="535976218" label="enable-plugin-power-saver"/>
   <int value="538468149" label="OfflinePagesCT:enabled"/>
@@ -27021,6 +27056,14 @@
   <int value="6" label="Unknown"/>
 </enum>
 
+<enum name="NewLockScreenNoteRequestType">
+  <int value="0" label="System tray action"/>
+  <int value="1" label="Lock screen - button tap"/>
+  <int value="2" label="Lock screen - swipe gesture"/>
+  <int value="3" label="Lock screen - button activated by keyboard"/>
+  <int value="4" label="Stylus ejected"/>
+</enum>
+
 <enum name="NewTabPageActionAndroid">
   <obsolete>
     Deprecated as of 01/2017. Replaced by NewTabPageActionAndroid2.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 375f689..56edb6b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1803,6 +1803,120 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Apps.LockScreen.NoteTakingApp.AppWindowLifeTime"
+    units="ms">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The amount of time a lock screen enabled app window spent in a certain state
+    during the app window activity. The state to which the histogram refers to
+    is defined by the suffix. The metric is logged upon leaving the associated
+    state.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.AvailabilityOnScreenLock"
+    enum="LockScreenActionAvailability">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The note taking action availability state on the lock screen, recorded when
+    the user session is locked.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.FinalAppSessionState"
+    enum="LockScreenAppSessionState">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The state in which lock screen enabled note taking app was when the note
+    taking session ended.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.LaunchDurationAtLaunchCancel"
+    units="ms">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The amount time a lock screen app had been launching when the app launch was
+    canceled. Logged if the lock screen app session ends before the lock screen
+    app window is shown.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.LaunchRequestOrdinalNumber">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    Ordinal number of a note taking app launch request from a lock screen within
+    a lock screen session. If a note taking app is launched n times during a
+    single lock screen session, this histogram will be reported with values 1
+    through n. The launch counter is reset on screen unlock.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.LaunchRequestReason"
+    enum="NewLockScreenNoteRequestType">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The user action that launched note taking from the lock screen.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.LockScreenInstallationDuration"
+    units="ms">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    Amount of time needed to install a copy of a lock screen note taking app
+    into lock screen apps profile.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.NoteTakingExitReason"
+    enum="LockScreenNoteTakingExitReason">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The reason the note taking on lock screen was ended (and lock screen app
+    window was closed).
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.TimeToLoadAppWindowContents"
+    units="ms">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The amount of time needed to load a note taking app window contents on the
+    lock screen - i.e. the time passed from the user requesting an app launch to
+    the app window contents being loaded.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.TimeToShowWindow" units="ms">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The amount of time needed to launch a note taking app window from the lock
+    screen - i.e. the time passed from the user requesting an app launch to the
+    app window appearing on the screen.
+  </summary>
+</histogram>
+
+<histogram name="Apps.LockScreen.NoteTakingApp.UnlockUIAction"
+    enum="LockScreenNoteTakingUnlockUIAction">
+  <owner>tbarzic@chromium.org</owner>
+  <owner>tbuckley@chromium.org</owner>
+  <summary>
+    The action the user took on the lock screen UI shown when lock screen app
+    window is in background, shown under the lock screen.
+  </summary>
+</histogram>
+
 <histogram name="Apps.NoteTakingApp.DefaultLaunchResult"
     enum="NoteTakingAppLaunchResult">
   <owner>derat@chromium.org</owner>
@@ -53133,6 +53247,9 @@
 </histogram>
 
 <histogram name="Parser.AppendBytesDelay" units="ms">
+  <obsolete>
+    Deprecated Aug 2017
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The delay from when bytes are received on the main thread to when the
@@ -53160,6 +53277,9 @@
 </histogram>
 
 <histogram name="Parser.PeakPendingChunkCount" units="chunks">
+  <obsolete>
+    Deprecated Aug 2017
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The maximum number of pending Chunks in the ParsedChunkQueue after the
@@ -53168,6 +53288,9 @@
 </histogram>
 
 <histogram name="Parser.PeakPendingTokenCount" units="tokens">
+  <obsolete>
+    Deprecated Aug 2017
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The maximum number of pending tokens in the ParsedChunkQueue after the
@@ -94223,6 +94346,16 @@
   <affected-histogram name="Apps.LockScreen.DataItemStorage.OperationResult"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="LockScreenNoteTakingAppWindowState" separator=".">
+  <suffix name="Foreground"
+      label="The app window is in foreground (on top of the lock screen)"/>
+  <suffix name="Background"
+      label="The app window is in background (behind the lock screen)"/>
+  <suffix name="TotalActive"
+      label="The total amount of time an app window was active on lock screen"/>
+  <affected-histogram name="Apps.LockScreen.NoteTakingApp.AppWindowLifeTime"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="LowMemoryMargin" separator="_">
   <suffix name="default" label="Low memory margin set to the system default"/>
   <suffix name="off" label="Low memory notification disabled"/>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index db4f23a..91defb5 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -10,6 +10,7 @@
 blink_perf.dom,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",
 blink_perf.events,hayato@chromium.org,
 blink_perf.layout,eae@chromium.org,
+blink_perf.owp_storage,dmurph@chromium.org,
 blink_perf.paint,wangxianzhu@chromium.org,
 blink_perf.parser,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",
 blink_perf.shadow_dom,hayato@chromium.org,
@@ -51,8 +52,8 @@
 power.steady_state,,
 power.trivial_pages,erikchen@chromium.org,
 power.typical_10_mobile,perezju@chromium.org,
-rasterize_and_record_micro.partial_invalidation,,
-rasterize_and_record_micro.top_25,,
+rasterize_and_record_micro.partial_invalidation,"vmpstr@chromium.org, wkorman@chromium.org",Internals>Compositing>Rasterization
+rasterize_and_record_micro.top_25,"vmpstr@chromium.org, wkorman@chromium.org",Internals>Compositing>Rasterization
 resource_sizes,"agrieve@chromium.org, rnephew@chromium.org, perezju@chromium.org",
 scheduler.tough_scheduling_cases,"skyostil@chromium.org, brianderson@chromium.org",
 service_worker.service_worker,horo@chromium.org,
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index 2016be9e6..122785e 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -358,7 +358,10 @@
   def GetExpectations(self):
     class StoryExpectations(story.expectations.StoryExpectations):
       def SetExpectations(self):
-        pass # Nothing disabled.
+        self.DisableStory('structured-clone-long-string-deserialize.html',
+            [story.expectations.ALL_ANDROID], 'crbug.com/733655')
+        self.DisableStory('structured-clone-long-string-serialize.html',
+            [story.expectations.ALL_ANDROID], 'crbug.com/733655')
     return StoryExpectations()
 
 
@@ -444,6 +447,19 @@
     return Expectations()
 
 
+@benchmark.Owner(emails=['dmurph@chromium.org'])
+@benchmark.Disabled('all')
+class BlinkPerfOWPStorage(_BlinkPerfBenchmark):
+  tag = 'owp_storage'
+  subdir = 'OWPStorage'
+
+  def GetExpectations(self):
+    class StoryExpectations(story.expectations.StoryExpectations):
+      def SetExpectations(self):
+        pass # Nothing disabled.
+    return StoryExpectations()
+
+
 @benchmark.Owner(emails=['wangxianzhu@chromium.org'])
 class BlinkPerfPaint(_BlinkPerfBenchmark):
   tag = 'paint'
diff --git a/tools/perf/benchmarks/memory.py b/tools/perf/benchmarks/memory.py
index 5722153..472b0b2 100644
--- a/tools/perf/benchmarks/memory.py
+++ b/tools/perf/benchmarks/memory.py
@@ -132,6 +132,11 @@
     return False
 
   @classmethod
+  def ShouldTearDownStateAfterEachStorySetRun(cls):
+    # Browser will now be closed as instructed by the shared state.
+    return False
+
+  @classmethod
   def ValueCanBeAddedPredicate(cls, value, is_first_result):
     return DefaultValueCanBeAddedPredicateForMemoryMeasurement(value)
 
diff --git a/tools/perf/benchmarks/rasterize_and_record_micro.py b/tools/perf/benchmarks/rasterize_and_record_micro.py
index 1a8ece57..c950055 100644
--- a/tools/perf/benchmarks/rasterize_and_record_micro.py
+++ b/tools/perf/benchmarks/rasterize_and_record_micro.py
@@ -6,6 +6,7 @@
 
 from measurements import rasterize_and_record_micro
 import page_sets
+from telemetry import benchmark
 from telemetry import story
 
 
@@ -43,6 +44,9 @@
         options.record_repeat, options.timeout, options.report_detailed_results)
 
 
+@benchmark.Owner(
+    emails=['vmpstr@chromium.org', 'wkorman@chromium.org'],
+    component='Internals>Compositing>Rasterization')
 class RasterizeAndRecordMicroTop25(_RasterizeAndRecordMicro):
   """Measures rasterize and record performance on the top 25 web pages.
 
@@ -59,11 +63,14 @@
         self.DisableStory('http://www.cnn.com', [story.expectations.ALL],
                           'crbug.com/528472')
         self.DisableStory('https://mail.google.com/mail/',
-                          [story.expectations.ALL_LINUX],
+                          [story.expectations.ALL],
                           'crbug.com/747021')
     return StoryExpectations()
 
 
+@benchmark.Owner(
+    emails=['vmpstr@chromium.org', 'wkorman@chromium.org'],
+    component='Internals>Compositing>Rasterization')
 class RasterizeAndRecordMicroPartialInvalidation(_RasterizeAndRecordMicro):
   """Measures rasterize and record performance for partial inval. on big pages.
 
diff --git a/tools/perf/benchmarks/smoothness.py b/tools/perf/benchmarks/smoothness.py
index 2cbc6f5..a790149 100644
--- a/tools/perf/benchmarks/smoothness.py
+++ b/tools/perf/benchmarks/smoothness.py
@@ -439,7 +439,82 @@
   def GetExpectations(self):
     class StoryExpectations(story_module.expectations.StoryExpectations):
       def SetExpectations(self):
-        pass # Nothing.
+        self.DisableStory('https://www.google.com/#hl=en&q=barack+obama',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('https://mail.google.com/mail/',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('https://www.google.com/calendar/',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('https://www.google.com/search?q=cats&tbm=isch',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://www.youtube.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('Blogger',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('Facebook',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('LinkedIn',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('Wikipedia (1 tab)',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('Twitter',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('ESPN',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://news.yahoo.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://www.cnn.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('Weather.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://www.amazon.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://www.ebay.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://games.yahoo.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://booking.com',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
+        self.DisableStory('http://sports.yahoo.com/',
+                          [story_module.expectations.ALL_ANDROID,
+                          story_module.expectations.ALL_WIN],
+                          'crbug.com/631015')
     return StoryExpectations()
 
 
@@ -480,9 +555,63 @@
     return 'smoothness.gpu_rasterization.tough_pinch_zoom_cases'
 
   def GetExpectations(self):
+
     class StoryExpectations(story_module.expectations.StoryExpectations):
+
       def SetExpectations(self):
-        pass # Nothing.
+        self.DisableStory('https://www.google.com/#hl=en&q=barack+obama',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('https://mail.google.com/mail/',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('https://www.google.com/calendar/',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('https://www.google.com/search?q=cats&tbm=isch',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://www.youtube.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('Blogger', [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('Facebook', [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('LinkedIn', [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('Wikipedia (1 tab)',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('Twitter', [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('ESPN', [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://news.yahoo.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://www.cnn.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('Weather.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://www.amazon.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://www.ebay.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://games.yahoo.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://booking.com',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+        self.DisableStory('http://sports.yahoo.com/',
+                          [story_module.expectations.ALL_ANDROID],
+                          'crbug.com/610021')
+
     return StoryExpectations()
 
 
diff --git a/tools/perf/contrib/memory_extras/memory_extras.py b/tools/perf/contrib/memory_extras/memory_extras.py
index abf810c0..33751abd 100644
--- a/tools/perf/contrib/memory_extras/memory_extras.py
+++ b/tools/perf/contrib/memory_extras/memory_extras.py
@@ -18,18 +18,26 @@
   on a webview-based browser (a stand in for the Search app), and loading
   pages on a select browser.
   """
-  page_set = page_sets.DualBrowserStorySet
   options = {'pageset_repeat': 5}
 
   @classmethod
   def Name(cls):
     return 'memory.dual_browser_test'
 
+  def CreateStorySet(self, options):
+    del options
+    return page_sets.DualBrowserStorySet()
+
   @classmethod
   def ShouldTearDownStateAfterEachStoryRun(cls):
     return False
 
   @classmethod
+  def ShouldTearDownStateAfterEachStorySetRun(cls):
+    # Browser will now be closed as instructed by the shared state.
+    return False
+
+  @classmethod
   def ValueCanBeAddedPredicate(cls, value, is_first_result):
     # TODO(crbug.com/610962): Remove this stopgap when the perf dashboard
     # is able to cope with the data load generated by TBMv2 metrics.
@@ -49,13 +57,16 @@
   Same as memory.dual_browser_test, but the test is run for 60 iterations
   and the browser is *not* restarted between page set repeats.
   """
-  page_set = page_sets.DualBrowserStorySet
   options = {'pageset_repeat': 60}
 
   @classmethod
   def Name(cls):
     return 'memory.long_running_dual_browser_test'
 
+  def CreateStorySet(self, options):
+    del options
+    return page_sets.DualBrowserStorySet(long_running=True)
+
   @classmethod
   def ShouldTearDownStateAfterEachStoryRun(cls):
     return False
diff --git a/tools/perf/page_sets/data/kraken.json b/tools/perf/page_sets/data/kraken.json
index a9b1d44..550d051e 100644
--- a/tools/perf/page_sets/data/kraken.json
+++ b/tools/perf/page_sets/data/kraken.json
@@ -1,7 +1,7 @@
 {
     "archives": {
         "http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html": {
-            "DEFAULT": "kraken_000.wpr"
+            "DEFAULT": "kraken_000.wprgo"
         }
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
diff --git a/tools/perf/page_sets/data/kraken_000.wpr.sha1 b/tools/perf/page_sets/data/kraken_000.wpr.sha1
deleted file mode 100644
index 5b47e88..0000000
--- a/tools/perf/page_sets/data/kraken_000.wpr.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c096ee93213072a013e1799e07e1337cc2a2e806
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/kraken_000.wprgo.sha1 b/tools/perf/page_sets/data/kraken_000.wprgo.sha1
new file mode 100644
index 0000000..dc6a6463f
--- /dev/null
+++ b/tools/perf/page_sets/data/kraken_000.wprgo.sha1
@@ -0,0 +1 @@
+cdf2ba76caaf50bddf279601dbc268c3aee99a3f
\ No newline at end of file
diff --git a/tools/perf/page_sets/desktop_memory.py b/tools/perf/page_sets/desktop_memory.py
index 59f9005..aef9de34d 100644
--- a/tools/perf/page_sets/desktop_memory.py
+++ b/tools/perf/page_sets/desktop_memory.py
@@ -6,17 +6,24 @@
 
 from telemetry.page import page as page_module
 from telemetry.page import shared_page_state
-from telemetry import story
+from telemetry import story as story_module
 
 _DUMP_WAIT_TIME = 3
 _ITERATIONS = 10
 
+
+class DesktopMemorySharedState(shared_page_state.SharedDesktopPageState):
+  def ShouldStopBrowserAfterStoryRun(self, story):
+    del story
+    return False  # Keep the same browser instance open across stories.
+
+
 class DesktopMemoryPage(page_module.Page):
 
   def __init__(self, url, page_set):
     super(DesktopMemoryPage, self).__init__(
         url=url, page_set=page_set,
-        shared_page_state_class=shared_page_state.SharedDesktopPageState,
+        shared_page_state_class=DesktopMemorySharedState,
         name=url)
 
   def _DumpMemory(self, action_runner, phase):
@@ -45,7 +52,7 @@
     self._DumpMemory(action_runner, 'post')
 
 
-class DesktopMemoryPageSet(story.StorySet):
+class DesktopMemoryPageSet(story_module.StorySet):
 
   """ Desktop sites with interesting memory characteristics """
 
diff --git a/tools/perf/page_sets/dual_browser_story.py b/tools/perf/page_sets/dual_browser_story.py
index e50fbe7..6ba5f2f 100644
--- a/tools/perf/page_sets/dual_browser_story.py
+++ b/tools/perf/page_sets/dual_browser_story.py
@@ -179,6 +179,11 @@
     self._current_story.Run(self)
 
   def DidRunStory(self, _):
+    if (not self._story_set.long_running and
+        self._story_set[-1] == self._current_story):
+      # In long_running mode we never close the browsers; otherwise we close
+      # them only after the last story in the set runs.
+      self._CloseAllBrowsers()
     self._current_story = None
 
   def TakeMemoryMeasurement(self):
@@ -238,10 +243,11 @@
 class DualBrowserStorySet(story_module.StorySet):
   """A story set that switches back and forth between two browsers."""
 
-  def __init__(self):
+  def __init__(self, long_running=False):
     super(DualBrowserStorySet, self).__init__(
         archive_data_file='data/dual_browser_story.json',
         cloud_storage_bucket=story_module.PARTNER_BUCKET)
+    self.long_running = long_running
 
     for query, url in zip(SEARCH_QUERIES, URL_LIST):
       # Stories that run on the android-webview browser.
diff --git a/tools/perf/page_sets/memory_top_10_mobile.py b/tools/perf/page_sets/memory_top_10_mobile.py
index 260af35..0a7239d 100644
--- a/tools/perf/page_sets/memory_top_10_mobile.py
+++ b/tools/perf/page_sets/memory_top_10_mobile.py
@@ -6,13 +6,25 @@
 
 from telemetry.page import page as page_module
 from telemetry.page import shared_page_state
-from telemetry import story
+from telemetry import story as story_module
 
 from devil.android.sdk import keyevent # pylint: disable=import-error
 
 from page_sets import top_10_mobile
 
 
+class Top10MobileSharedState(shared_page_state.SharedMobilePageState):
+  def __init__(self, test, finder_options, story_set):
+    super(Top10MobileSharedState, self).__init__(
+        test, finder_options, story_set)
+    self._story_set = story_set
+
+  def ShouldStopBrowserAfterStoryRun(self, story):
+    # Close the browser after the last story in the set.
+    # TODO(crbug.com/750055): Switch to close after each background page.
+    return self._story_set[-1] == story
+
+
 class MemoryMeasurementPage(page_module.Page):
   """Abstract class for measuring memory on a story unit."""
 
@@ -21,7 +33,7 @@
   def __init__(self, story_set, name, url):
     super(MemoryMeasurementPage, self).__init__(
         page_set=story_set, name=name, url=url,
-        shared_page_state_class=shared_page_state.SharedMobilePageState,
+        shared_page_state_class=Top10MobileSharedState,
         grouping_keys={'phase': self._PHASE})
 
 
@@ -61,14 +73,14 @@
         keyevent.KEYCODE_BACK)
 
 
-class MemoryTop10Mobile(story.StorySet):
+class MemoryTop10Mobile(story_module.StorySet):
   """User story to measure foreground/background memory in top 10 mobile."""
   DETERMINISTIC_MODE = True
 
   def __init__(self):
     super(MemoryTop10Mobile, self).__init__(
         archive_data_file='data/memory_top_10_mobile.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
+        cloud_storage_bucket=story_module.PARTNER_BUCKET)
 
     for url in top_10_mobile.URL_LIST:
       # We name pages so their foreground/background counterparts are easy
diff --git a/tools/perf/scripts_smoke_unittest.py b/tools/perf/scripts_smoke_unittest.py
index c6d0fa6..da2e82a 100644
--- a/tools/perf/scripts_smoke_unittest.py
+++ b/tools/perf/scripts_smoke_unittest.py
@@ -6,9 +6,11 @@
 import os
 import subprocess
 import sys
-from telemetry.testing import options_for_unittests
 import unittest
 
+from telemetry import decorators
+from telemetry.testing import options_for_unittests
+
 
 class ScriptsSmokeTest(unittest.TestCase):
 
@@ -58,6 +60,7 @@
     self.assertEquals(return_code, 0, stdout)
     self.assertIn('kraken', stdout)
 
+  @decorators.Disabled('chromeos')  # crbug.com/754913
   def testRunTelemetryBenchmarkAsGoogletest(self):
     options = options_for_unittests.GetCopy()
     browser_type = options.browser_type
diff --git a/tools/perf/unowned_benchmarks.txt b/tools/perf/unowned_benchmarks.txt
index b3d2d7f..2a892f4 100644
--- a/tools/perf/unowned_benchmarks.txt
+++ b/tools/perf/unowned_benchmarks.txt
@@ -2,8 +2,6 @@
 load_library_perf_tests
 power.idle_platform
 power.steady_state
-rasterize_and_record_micro.partial_invalidation
-rasterize_and_record_micro.top_25
 smoothness.tough_image_decode_cases
 start_with_ext.cold.blank_page
 start_with_ext.warm.blank_page
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 2c2bd33..56dab4a 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -69,7 +69,9 @@
       {ui::AX_ROLE_FOOTER, NSAccessibilityGroupRole},
       {ui::AX_ROLE_FORM, NSAccessibilityGroupRole},
       {ui::AX_ROLE_GENERIC_CONTAINER, NSAccessibilityGroupRole},
-      {ui::AX_ROLE_GRID, NSAccessibilityGridRole},
+      // Should be NSAccessibilityGridRole but VoiceOver treating it like
+      // a list as of 10.12.6, so following WebKit and using table role:
+      {ui::AX_ROLE_GRID, NSAccessibilityTableRole},  // crbug.com/753925
       {ui::AX_ROLE_GROUP, NSAccessibilityGroupRole},
       {ui::AX_ROLE_HEADING, @"AXHeading"},
       {ui::AX_ROLE_IFRAME, NSAccessibilityGroupRole},
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index 1b503f8..2117297 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -134,9 +134,24 @@
 const int kSearchBoxPadding = 16;
 const int kSearchBoxTopPadding = 24;
 
+// Bottom padding of search box in peeking state.
+const int kSearchBoxPeekingBottomPadding = 12;
+
+// Bottom padding of search box in fullscreen state.
+const int kSearchBoxFullscreenBottomPadding = 24;
+
 // The background border corner radius of the search box in fullscreen mode.
 const int kSearchBoxBorderCornerRadiusFullscreen = 24;
 
+// Preferred height of search box.
+const int kSearchBoxPreferredHeight = 48;
+
+// The height of the peeking app list from the bottom of the screen.
+const int kPeekingAppListHeight = 320;
+
+// The height/width of the shelf from the bottom/side of the screen.
+const int kShelfSize = 48;
+
 // Max items allowed in a folder.
 size_t kMaxFolderItems = 16;
 
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index 01ab81d..8b96d84 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -99,7 +99,13 @@
 APP_LIST_EXPORT extern const int kAppsGridLeftRightPaddingFullscreen;
 APP_LIST_EXPORT extern const int kSearchBoxPadding;
 APP_LIST_EXPORT extern const int kSearchBoxTopPadding;
+APP_LIST_EXPORT extern const int kSearchBoxPeekingBottomPadding;
+APP_LIST_EXPORT extern const int kSearchBoxFullscreenBottomPadding;
 APP_LIST_EXPORT extern const int kSearchBoxBorderCornerRadiusFullscreen;
+APP_LIST_EXPORT extern const int kSearchBoxPreferredHeight;
+
+APP_LIST_EXPORT extern const int kPeekingAppListHeight;
+APP_LIST_EXPORT extern const int kShelfSize;
 
 APP_LIST_EXPORT extern size_t kMaxFolderItems;
 APP_LIST_EXPORT extern const size_t kNumFolderTopItems;
diff --git a/ui/app_list/presenter/app_list_presenter_impl.cc b/ui/app_list/presenter/app_list_presenter_impl.cc
index 7872f99..c3d1ebb 100644
--- a/ui/app_list/presenter/app_list_presenter_impl.cc
+++ b/ui/app_list/presenter/app_list_presenter_impl.cc
@@ -177,7 +177,9 @@
   views::Widget* widget = view_->GetWidget();
   ui::Layer* layer = GetLayer(widget);
   layer->GetAnimator()->StopAnimating();
-  gfx::Rect target_bounds = widget->GetWindowBoundsInScreen();
+  gfx::Rect target_bounds = is_fullscreen_app_list_enabled_
+                                ? widget->GetNativeView()->bounds()
+                                : widget->GetWindowBoundsInScreen();
   ui::ScopedLayerAnimationSettings animation(layer->GetAnimator());
   aura::Window* root_window = widget->GetNativeView()->GetRootWindow();
   const gfx::Vector2d offset =
@@ -200,6 +202,10 @@
 
   animation.AddObserver(this);
   layer->SetOpacity(is_visible_ ? 1.0 : 0.0);
+  if (is_fullscreen_app_list_enabled_) {
+    widget->GetNativeView()->SetBounds(target_bounds);
+    return;
+  }
   widget->SetBounds(target_bounds);
 }
 
diff --git a/ui/app_list/search/mixer.cc b/ui/app_list/search/mixer.cc
index c7328e60..0e49894 100644
--- a/ui/app_list/search/mixer.cc
+++ b/ui/app_list/search/mixer.cc
@@ -12,6 +12,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/search_provider.h"
 #include "ui/app_list/search_result.h"
 
@@ -47,8 +48,12 @@
 // Used to group relevant providers together for mixing their results.
 class Mixer::Group {
  public:
-  Group(size_t max_results, double multiplier)
-      : max_results_(max_results), multiplier_(multiplier) {}
+  Group(size_t max_results, double multiplier, double boost)
+      : max_results_(max_results),
+        multiplier_(multiplier),
+        boost_(boost),
+        is_fullscreen_app_list_enabled_(
+            features::IsFullscreenAppListEnabled()) {}
   ~Group() {}
 
   void AddProvider(SearchProvider* provider) {
@@ -63,43 +68,44 @@
         DCHECK(!result->id().empty());
 
         // We cannot rely on providers to give relevance scores in the range
-        // [0.0, 1.0] (e.g., PeopleProvider directly gives values from the
-        // Google+ API). Clamp to that range.
+        // [0.0, 1.0]. Clamp to that range.
         const double relevance =
             std::min(std::max(result->relevance(), 0.0), 1.0);
-        double boost = 0.0;
+        double boost = boost_;
 
-        // Recommendations should not be affected by query-to-launch correlation
-        // from KnownResults as it causes recommendations to become dominated by
-        // previously clicked results. This happens because the recommendation
-        // query is the empty string and the clicked results get forever
-        // boosted.
-        if (result->display_type() != SearchResult::DISPLAY_RECOMMENDATION) {
-          KnownResults::const_iterator known_it =
-              known_results.find(result->id());
-          if (known_it != known_results.end()) {
-            switch (known_it->second) {
-              case PERFECT_PRIMARY:
-                boost = 4.0;
-                break;
-              case PREFIX_PRIMARY:
-                boost = 3.75;
-                break;
-              case PERFECT_SECONDARY:
-                boost = 3.25;
-                break;
-              case PREFIX_SECONDARY:
-                boost = 3.0;
-                break;
-              case UNKNOWN_RESULT:
-                NOTREACHED() << "Unknown result in KnownResults?";
-                break;
+        if (!is_fullscreen_app_list_enabled_) {
+          // Recommendations should not be affected by query-to-launch
+          // correlation from KnownResults as it causes recommendations to
+          // become dominated by previously clicked results. This happens
+          // because the recommendation query is the empty string and the
+          // clicked results get forever boosted.
+          if (result->display_type() != SearchResult::DISPLAY_RECOMMENDATION) {
+            KnownResults::const_iterator known_it =
+                known_results.find(result->id());
+            if (known_it != known_results.end()) {
+              switch (known_it->second) {
+                case PERFECT_PRIMARY:
+                  boost = 4.0;
+                  break;
+                case PREFIX_PRIMARY:
+                  boost = 3.75;
+                  break;
+                case PERFECT_SECONDARY:
+                  boost = 3.25;
+                  break;
+                case PREFIX_SECONDARY:
+                  boost = 3.0;
+                  break;
+                case UNKNOWN_RESULT:
+                  NOTREACHED() << "Unknown result in KnownResults?";
+                  break;
+              }
             }
-          }
 
-          // If this is a voice query, voice results receive a massive boost.
-          if (is_voice_query && result->voice_result())
-            boost += 4.0;
+            // If this is a voice query, voice results receive a massive boost.
+            if (is_voice_query && result->voice_result())
+              boost += 4.0;
+          }
         }
 
         results_.emplace_back(result.get(), relevance * multiplier_ + boost);
@@ -117,6 +123,9 @@
   typedef std::vector<SearchProvider*> Providers;
   const size_t max_results_;
   const double multiplier_;
+  const double boost_;
+
+  const bool is_fullscreen_app_list_enabled_;
 
   Providers providers_;  // Not owned.
   SortedResults results_;
@@ -130,8 +139,8 @@
 Mixer::~Mixer() {
 }
 
-size_t Mixer::AddGroup(size_t max_results, double multiplier) {
-  groups_.push_back(base::MakeUnique<Group>(max_results, multiplier));
+size_t Mixer::AddGroup(size_t max_results, double multiplier, double boost) {
+  groups_.push_back(base::MakeUnique<Group>(max_results, multiplier, boost));
   return groups_.size() - 1;
 }
 
diff --git a/ui/app_list/search/mixer.h b/ui/app_list/search/mixer.h
index 253de6fd3..41818396 100644
--- a/ui/app_list/search/mixer.h
+++ b/ui/app_list/search/mixer.h
@@ -38,8 +38,9 @@
   // chosen from this group (if 0, will allow unlimited results from this
   // group). If there aren't enough results from all groups, more than
   // |max_results| may be chosen from this group. Each result in the group will
-  // have its score multiplied by |multiplier|. Returns the group's group_id.
-  size_t AddGroup(size_t max_results, double multiplier);
+  // have its score multiplied by |multiplier| and added by |boost|. Returns the
+  // group's group_id.
+  size_t AddGroup(size_t max_results, double multiplier, double boost);
 
   // Associates a provider with a mixer group.
   void AddProviderToGroup(size_t group_id, SearchProvider* provider);
diff --git a/ui/app_list/search/mixer_unittest.cc b/ui/app_list/search/mixer_unittest.cc
index fb4f91d..66546b3 100644
--- a/ui/app_list/search/mixer_unittest.cc
+++ b/ui/app_list/search/mixer_unittest.cc
@@ -131,9 +131,12 @@
 
     mixer_.reset(new Mixer(results_.get()));
 
-    size_t apps_group_id = mixer_->AddGroup(kMaxAppsGroupResults, 1.0);
-    size_t omnibox_group_id = mixer_->AddGroup(kMaxOmniboxResults, 1.0);
-    size_t webstore_group_id = mixer_->AddGroup(kMaxWebstoreResults, 0.5);
+    // TODO(warx): when fullscreen app list is default enabled, modify this test
+    // to (1) test answer card/apps group having relevance boost, (2) remove
+    // known results boost tests.
+    size_t apps_group_id = mixer_->AddGroup(kMaxAppsGroupResults, 1.0, 0.0);
+    size_t omnibox_group_id = mixer_->AddGroup(kMaxOmniboxResults, 1.0, 0.0);
+    size_t webstore_group_id = mixer_->AddGroup(kMaxWebstoreResults, 0.5, 0.0);
 
     mixer_->AddProviderToGroup(apps_group_id, providers_[0].get());
     mixer_->AddProviderToGroup(omnibox_group_id, providers_[1].get());
diff --git a/ui/app_list/search_controller.cc b/ui/app_list/search_controller.cc
index cae0583..b2628f2 100644
--- a/ui/app_list/search_controller.cc
+++ b/ui/app_list/search_controller.cc
@@ -103,8 +103,10 @@
   result->InvokeAction(action_index, event_flags);
 }
 
-size_t SearchController::AddGroup(size_t max_results, double multiplier) {
-  return mixer_->AddGroup(max_results, multiplier);
+size_t SearchController::AddGroup(size_t max_results,
+                                  double multiplier,
+                                  double boost) {
+  return mixer_->AddGroup(max_results, multiplier, boost);
 }
 
 void SearchController::AddProvider(size_t group_id,
diff --git a/ui/app_list/search_controller.h b/ui/app_list/search_controller.h
index ea57747a..4c6c939 100644
--- a/ui/app_list/search_controller.h
+++ b/ui/app_list/search_controller.h
@@ -42,7 +42,7 @@
                           int event_flags);
 
   // Adds a new mixer group. See Mixer::AddGroup.
-  size_t AddGroup(size_t max_results, double multiplier);
+  size_t AddGroup(size_t max_results, double multiplier, double boost);
 
   // Takes ownership of |provider| and associates it with given mixer group.
   void AddProvider(size_t group_id, std::unique_ptr<SearchProvider> provider);
diff --git a/ui/app_list/views/app_list_page.cc b/ui/app_list/views/app_list_page.cc
index 9799f94..0953b75 100644
--- a/ui/app_list/views/app_list_page.cc
+++ b/ui/app_list/views/app_list_page.cc
@@ -30,6 +30,11 @@
                                      AppListModel::State from_state,
                                      AppListModel::State to_state) {}
 
+gfx::Rect AppListPage::GetPageBoundsDuringDragging(
+    AppListModel::State state) const {
+  return GetPageBoundsForState(state);
+}
+
 gfx::Rect AppListPage::GetSearchBoxBounds() const {
   DCHECK(contents_view_);
   return contents_view_->GetDefaultSearchBoxBounds();
diff --git a/ui/app_list/views/app_list_page.h b/ui/app_list/views/app_list_page.h
index f3c452b..0b07e42 100644
--- a/ui/app_list/views/app_list_page.h
+++ b/ui/app_list/views/app_list_page.h
@@ -36,6 +36,10 @@
   // Returns where this page should move to when the given state is active.
   virtual gfx::Rect GetPageBoundsForState(AppListModel::State state) const = 0;
 
+  // Returns the bounds of the page during dragging.
+  virtual gfx::Rect GetPageBoundsDuringDragging(
+      AppListModel::State state) const;
+
   // Returns where the search box should be when this page is shown. Is at the
   // top of the app list by default, in the contents view's coordinate space.
   virtual gfx::Rect GetSearchBoxBounds() const;
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 7dd66588..6efa0e8 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -58,9 +58,6 @@
 // The margin from the edge to the speech UI.
 constexpr int kSpeechUIMargin = 12;
 
-// The height of the peeking app list from the bottom of the screen.
-constexpr int kPeekingAppListHeight = 320;
-
 // The height of the half app list from the bottom of the screen.
 constexpr int kHalfAppListHeight = 561;
 
@@ -178,16 +175,8 @@
 
 AppListView::AppListView(AppListViewDelegate* delegate)
     : delegate_(delegate),
-      app_list_main_view_(nullptr),
-      speech_view_(nullptr),
-      search_box_focus_host_(nullptr),
-      search_box_widget_(nullptr),
-      search_box_view_(nullptr),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()),
-      processing_scroll_event_series_(false),
-      app_list_state_(PEEKING),
       display_observer_(this),
-      overlay_view_(nullptr),
       animation_observer_(new HideViewAnimationObserver()) {
   CHECK(delegate);
   delegate_->GetSpeechUI()->AddObserver(this);
@@ -432,22 +421,17 @@
                                        int initial_apps_page) {
   const display::Display display_nearest_view = GetDisplayNearestView();
   const gfx::Rect display_work_area_bounds = display_nearest_view.work_area();
-  const int bottom_of_screen = display_nearest_view.size().height();
 
   // Set the widget height to the shelf height to replace the shelf background
   // on show animation with no flicker. In shelf mode we set the bounds to the
   // top of the screen because the widget does not animate.
-  const int overlay_view_bounds_y =
-      is_side_shelf_ ? 0 : (bottom_of_screen - kShelfSize);
-  const int overlay_view_bounds_x =
-      is_side_shelf_ ? 0 : display_work_area_bounds.x();
-  const int overlay_view_bounds_width =
-      display_work_area_bounds.width() + (is_side_shelf_ ? kShelfSize : 0);
-  const int overlay_view_bounds_height =
-      display_work_area_bounds.height() + (is_side_shelf_ ? 0 : kShelfSize);
+  const int overlay_view_bounds_y = is_side_shelf_
+                                        ? display_work_area_bounds.y()
+                                        : display_work_area_bounds.bottom();
   gfx::Rect app_list_overlay_view_bounds(
-      overlay_view_bounds_x, overlay_view_bounds_y, overlay_view_bounds_width,
-      overlay_view_bounds_height);
+      display_nearest_view.bounds().x(), overlay_view_bounds_y,
+      display_nearest_view.bounds().width(),
+      display_nearest_view.bounds().height());
 
   fullscreen_widget_ = new views::Widget;
   views::Widget::InitParams app_list_overlay_view_params(
@@ -458,11 +442,18 @@
   app_list_overlay_view_params.delegate = this;
   app_list_overlay_view_params.opacity =
       views::Widget::InitParams::TRANSLUCENT_WINDOW;
-  app_list_overlay_view_params.bounds = app_list_overlay_view_bounds;
   app_list_overlay_view_params.layer_type = ui::LAYER_SOLID_COLOR;
   fullscreen_widget_->Init(app_list_overlay_view_params);
 
+  // Set bounds directly in screen coordinates to avoid screen position
+  // controller setting bounds in the display where the widget has the largest
+  // intersection.
+  fullscreen_widget_->GetNativeView()->SetBoundsInScreen(
+      app_list_overlay_view_bounds, GetDisplayNearestView());
+
   overlay_view_ = new AppListOverlayView(0 /* no corners */);
+
+  work_area_bottom_ = fullscreen_widget_->GetWorkAreaBoundsInScreen().bottom();
 }
 
 void AppListView::InitializeBubble(gfx::NativeView parent,
@@ -484,10 +475,17 @@
   overlay_view_->SetBoundsRect(GetContentsBounds());
 }
 
-void AppListView::HandleClickOrTap() {
+void AppListView::HandleClickOrTap(ui::LocatedEvent* event) {
   if (!is_fullscreen_app_list_enabled_)
     return;
 
+  // No-op if app list is on fullscreen all apps state and the event location is
+  // within apps grid view's bounds.
+  if (app_list_state_ == FULLSCREEN_ALL_APPS &&
+      GetAppsGridView()->GetBoundsInScreen().Contains(event->location())) {
+    return;
+  }
+
   if (!search_box_view_->is_search_box_active()) {
     SetState(CLOSED);
     return;
@@ -498,17 +496,19 @@
 }
 
 void AppListView::StartDrag(const gfx::Point& location) {
+  // Convert drag point from widget coordinates to screen coordinates because
+  // the widget bounds changes during the dragging.
   initial_drag_point_ = location;
+  ConvertPointToScreen(this, &initial_drag_point_);
+  initial_window_bounds_ = fullscreen_widget_->GetWindowBoundsInScreen();
 }
 
 void AppListView::UpdateDrag(const gfx::Point& location) {
-  // Update the bounds of the widget while maintaining the
-  // relative position of the top of the widget and the mouse/gesture.
-  // Block drags north of 0 and recalculate the initial_drag_point_.
-  int new_y_position = location.y() - initial_drag_point_.y() +
-                       fullscreen_widget_->GetWindowBoundsInScreen().y();
-  if (new_y_position < 0)
-    initial_drag_point_ = location;
+  // Update the widget bounds based on the initial widget bounds and drag delta.
+  gfx::Point location_in_screen_coordinates = location;
+  ConvertPointToScreen(this, &location_in_screen_coordinates);
+  int new_y_position = location_in_screen_coordinates.y() -
+                       initial_drag_point_.y() + initial_window_bounds_.y();
 
   UpdateYPositionAndOpacity(new_y_position,
                             GetAppListBackgroundOpacityDuringDragging(),
@@ -520,8 +520,7 @@
   if (app_list_state_ == CLOSED)
     return;
 
-  // Restores opacity of all the items in app list if dragging ends.
-  UpdateOpacity(kAppListOpacity, true /* is_end_gesture */);
+  DraggingLayout();
   // Change the app list state based on where the drag ended. If fling velocity
   // was over the threshold, snap to the next state in the direction of the
   // fling.
@@ -585,13 +584,15 @@
         break;
     }
 
-    gfx::Point location_in_screen_coordinates = location;
-    ConvertPointToScreen(this, &location_in_screen_coordinates);
-    const int new_y_position =
-        location_in_screen_coordinates.y() - initial_drag_point_.y();
     const int app_list_threshold =
         app_list_height / kAppListThresholdDenominator;
-    const int drag_delta = app_list_y_for_state - new_y_position;
+    gfx::Point location_in_screen_coordinates = location;
+    ConvertPointToScreen(this, &location_in_screen_coordinates);
+    const int drag_delta =
+        initial_drag_point_.y() - location_in_screen_coordinates.y();
+    const int location_y_in_current_display =
+        location_in_screen_coordinates.y() -
+        GetDisplayNearestView().bounds().y();
     switch (app_list_state_) {
       case FULLSCREEN_ALL_APPS:
         if (std::abs(drag_delta) > app_list_threshold)
@@ -608,7 +609,7 @@
       case HALF:
         if (std::abs(drag_delta) > app_list_threshold) {
           SetState(drag_delta > 0 ? FULLSCREEN_SEARCH : CLOSED);
-        } else if (location_in_screen_coordinates.y() >=
+        } else if (location_y_in_current_display >=
                    display_height - kAppListBezelMargin) {
           // If the user drags to the bezel, close the app list.
           SetState(CLOSED);
@@ -619,7 +620,7 @@
       case PEEKING:
         if (std::abs(drag_delta) > app_list_threshold) {
           SetState(drag_delta > 0 ? FULLSCREEN_ALL_APPS : CLOSED);
-        } else if (location_in_screen_coordinates.y() >=
+        } else if (location_y_in_current_display >=
                    display_height - kAppListBezelMargin) {
           // If the user drags to the bezel, close the app list.
           SetState(CLOSED);
@@ -638,6 +639,12 @@
   return display::Screen::GetScreen()->GetDisplayNearestView(parent_window());
 }
 
+AppsGridView* AppListView::GetAppsGridView() const {
+  return app_list_main_view_->contents_view()
+      ->apps_container_view()
+      ->apps_grid_view();
+}
+
 void AppListView::OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                            views::Widget* widget) const {
   if (!params->native_widget) {
@@ -692,7 +699,7 @@
   switch (event->type()) {
     case ui::ET_MOUSE_PRESSED:
       event->SetHandled();
-      HandleClickOrTap();
+      HandleClickOrTap(event);
       break;
     case ui::ET_MOUSEWHEEL:
       if (HandleScroll(event))
@@ -709,32 +716,37 @@
 
   switch (event->type()) {
     case ui::ET_GESTURE_TAP:
-      processing_scroll_event_series_ = false;
+      is_in_drag_ = false;
       event->SetHandled();
-      HandleClickOrTap();
+      HandleClickOrTap(event);
       break;
     case ui::ET_SCROLL_FLING_START:
     case ui::ET_GESTURE_SCROLL_BEGIN:
       if (is_side_shelf_)
         return;
-      processing_scroll_event_series_ = true;
-      StartDrag(event->location());
+      // There may be multiple scroll begin events in one drag because the
+      // relative location of the finger and widget is almost unchanged and
+      // scroll begin event occurs when the relative location changes beyond a
+      // threshold. So avoid resetting the initial drag point in drag.
+      if (!is_in_drag_)
+        StartDrag(event->location());
+      is_in_drag_ = true;
       event->SetHandled();
       break;
     case ui::ET_GESTURE_SCROLL_UPDATE:
       if (is_side_shelf_)
         return;
-      processing_scroll_event_series_ = true;
+      is_in_drag_ = true;
       last_fling_velocity_ = event->details().scroll_y();
       UpdateDrag(event->location());
       event->SetHandled();
       break;
     case ui::ET_GESTURE_END:
-      if (!processing_scroll_event_series_)
+      if (!is_in_drag_)
         break;
       if (is_side_shelf_)
         return;
-      processing_scroll_event_series_ = false;
+      is_in_drag_ = false;
       EndDrag(event->location());
       event->SetHandled();
       break;
@@ -944,7 +956,7 @@
       break;
   }
 
-  gfx::Rect target_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
+  gfx::Rect target_bounds = fullscreen_widget_->GetNativeView()->bounds();
   target_bounds.set_y(target_state_y);
 
   std::unique_ptr<ui::LayerAnimationElement> bounds_animation_element =
@@ -992,22 +1004,24 @@
 void AppListView::UpdateYPositionAndOpacity(int y_position_in_screen,
                                             float background_opacity,
                                             bool is_end_gesture) {
+  is_in_drag_ = !is_end_gesture;
+  background_opacity_ = background_opacity;
   if (is_end_gesture) {
     SetState(FULLSCREEN_ALL_APPS);
   } else {
     gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
-    new_widget_bounds.set_y(std::max(y_position_in_screen, 0));
-    fullscreen_widget_->SetBounds(new_widget_bounds);
+    app_list_y_position_in_screen_ =
+        std::max(y_position_in_screen, GetDisplayNearestView().bounds().y());
+    new_widget_bounds.set_y(app_list_y_position_in_screen_);
+    fullscreen_widget_->GetNativeView()->SetBoundsInScreen(
+        new_widget_bounds, GetDisplayNearestView());
   }
 
-  UpdateOpacity(background_opacity, is_end_gesture);
+  DraggingLayout();
 }
 
-PaginationModel* AppListView::GetAppsPaginationModel() {
-  return app_list_main_view_->contents_view()
-      ->apps_container_view()
-      ->apps_grid_view()
-      ->pagination_model();
+PaginationModel* AppListView::GetAppsPaginationModel() const {
+  return GetAppsGridView()->pagination_model();
 }
 
 void AppListView::OnSpeechRecognitionStateChanged(
@@ -1085,8 +1099,7 @@
     return;
 
   // Set the |fullscreen_widget_| size to fit the new display metrics.
-  gfx::Size size = GetDisplayNearestView().work_area().size();
-  size.Enlarge(0, kShelfSize);
+  gfx::Size size = GetDisplayNearestView().size();
   fullscreen_widget_->SetSize(size);
 
   // Update the |fullscreen_widget_| bounds to accomodate the new work
@@ -1094,28 +1107,25 @@
   SetState(app_list_state_);
 }
 
-void AppListView::UpdateOpacity(float background_opacity, bool is_end_gesture) {
+void AppListView::DraggingLayout() {
   app_list_background_shield_->layer()->SetOpacity(
-      is_end_gesture ? kAppListOpacity : background_opacity);
-  gfx::Rect work_area_bounds = fullscreen_widget_->GetWorkAreaBoundsInScreen();
-  search_box_view_->UpdateOpacity(work_area_bounds.bottom(), is_end_gesture);
-  app_list_main_view_->contents_view()
-      ->apps_container_view()
-      ->apps_grid_view()
-      ->UpdateOpacity(work_area_bounds.bottom(), is_end_gesture);
+      is_in_drag_ ? background_opacity_ : kAppListOpacity);
+
+  // Updates the opacity of the items in the app list.
+  search_box_view_->UpdateOpacity(app_list_y_position_in_screen_);
+  GetAppsGridView()->UpdateOpacity(app_list_y_position_in_screen_);
+
+  app_list_main_view_->contents_view()->Layout();
 
   if (app_list_state_ == PEEKING) {
     app_list_main_view_->contents_view()->start_page_view()->UpdateOpacity(
-        work_area_bounds.bottom(), is_end_gesture);
+        work_area_bottom_, !is_in_drag_);
   }
 }
 
 float AppListView::GetAppListBackgroundOpacityDuringDragging() {
   float top_of_applist = fullscreen_widget_->GetWindowBoundsInScreen().y();
-  float work_area_bottom =
-      fullscreen_widget_->GetWorkAreaBoundsInScreen().bottom();
-
-  float dragging_height = std::max((work_area_bottom - top_of_applist), 0.f);
+  float dragging_height = std::max((work_area_bottom_ - top_of_applist), 0.f);
   float coefficient =
       std::min(dragging_height / (kNumOfShelfSize * kShelfSize), 1.0f);
   return coefficient * kAppListOpacity;
diff --git a/ui/app_list/views/app_list_view.h b/ui/app_list/views/app_list_view.h
index b052b49..4119f77 100644
--- a/ui/app_list/views/app_list_view.h
+++ b/ui/app_list/views/app_list_view.h
@@ -27,6 +27,7 @@
 class AppListMainView;
 class AppListModel;
 class AppListViewDelegate;
+class AppsGridView;
 class HideViewAnimationObserver;
 class PaginationModel;
 class SearchBoxView;
@@ -43,9 +44,6 @@
                                     public display::DisplayObserver,
                                     public AppListViewDelegateObserver {
  public:
-  // The height/width of the shelf from the bottom/side of the screen.
-  static constexpr int kShelfSize = 48;
-
   // Number of the size of shelf. Used to determine the opacity of items in the
   // app list during dragging.
   static constexpr float kNumOfShelfSize = 2.0;
@@ -128,6 +126,11 @@
   void Layout() override;
   void SchedulePaintInRect(const gfx::Rect& rect) override;
 
+  // Overridden from ui::EventHandler:
+  void OnScrollEvent(ui::ScrollEvent* event) override;
+  void OnMouseEvent(ui::MouseEvent* event) override;
+  void OnGestureEvent(ui::GestureEvent* event) override;
+
   // Called when tablet mode starts and ends.
   void OnTabletModeChanged(bool started);
 
@@ -153,7 +156,7 @@
                                  bool is_end_gesture);
 
   // Gets the PaginationModel owned by this view's apps grid.
-  PaginationModel* GetAppsPaginationModel();
+  PaginationModel* GetAppsPaginationModel() const;
 
   views::Widget* get_fullscreen_widget_for_test() const {
     return fullscreen_widget_;
@@ -163,9 +166,9 @@
 
   views::Widget* search_box_widget() const { return search_box_widget_; }
 
-  SearchBoxView* search_box_view() { return search_box_view_; }
+  SearchBoxView* search_box_view() const { return search_box_view_; }
 
-  AppListMainView* app_list_main_view() { return app_list_main_view_; }
+  AppListMainView* app_list_main_view() const { return app_list_main_view_; }
 
   bool is_fullscreen() const {
     return app_list_state_ == FULLSCREEN_ALL_APPS ||
@@ -174,6 +177,14 @@
 
   bool is_tablet_mode() const { return is_tablet_mode_; }
 
+  bool is_in_drag() const { return is_in_drag_; }
+
+  int app_list_y_position_in_screen() const {
+    return app_list_y_position_in_screen_;
+  }
+
+  int work_area_bottom() const { return work_area_bottom_; }
+
  private:
   friend class test::AppListViewTestApi;
 
@@ -189,7 +200,7 @@
 
   // Closes the AppListView when a click or tap event propogates to the
   // AppListView.
-  void HandleClickOrTap();
+  void HandleClickOrTap(ui::LocatedEvent* event);
 
   // Initializes |initial_drag_point_|.
   void StartDrag(const gfx::Point& location);
@@ -205,6 +216,9 @@
   // Gets the display nearest to the parent window.
   display::Display GetDisplayNearestView() const;
 
+  // Gets the apps grid view owned by this view.
+  AppsGridView* GetAppsGridView() const;
+
   // Overridden from views::BubbleDialogDelegateView:
   void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
                                 views::Widget* widget) const override;
@@ -215,11 +229,6 @@
   bool WidgetHasHitTestMask() const override;
   void GetWidgetHitTestMask(gfx::Path* mask) const override;
 
-  // Overridden from ui::EventHandler:
-  void OnScrollEvent(ui::ScrollEvent* event) override;
-  void OnMouseEvent(ui::MouseEvent* event) override;
-  void OnGestureEvent(ui::GestureEvent* event) override;
-
   // Overridden from views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
@@ -232,8 +241,8 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
-  // Updates opacity of both background and items in the app list.
-  void UpdateOpacity(float background_opacity, bool is_end_gesture);
+  // Layouts the app list during dragging.
+  void DraggingLayout();
 
   // Gets app list background opacity during dragging.
   float GetAppListBackgroundOpacityDuringDragging();
@@ -246,13 +255,15 @@
 
   AppListViewDelegate* delegate_;  // Weak. Owned by AppListService.
 
-  AppListMainView* app_list_main_view_;
-  SpeechView* speech_view_;
+  AppListMainView* app_list_main_view_ = nullptr;
+  SpeechView* speech_view_ = nullptr;
   views::Widget* fullscreen_widget_ = nullptr;  // Owned by AppListView.
 
-  views::View* search_box_focus_host_;  // Owned by the views hierarchy.
-  views::Widget* search_box_widget_;    // Owned by the app list's widget.
-  SearchBoxView* search_box_view_;      // Owned by |search_box_widget_|.
+  views::View* search_box_focus_host_ =
+      nullptr;  // Owned by the views hierarchy.
+  views::Widget* search_box_widget_ =
+      nullptr;                                // Owned by the app list's widget.
+  SearchBoxView* search_box_view_ = nullptr;  // Owned by |search_box_widget_|.
   // Owned by the app list's widget. Null if the fullscreen app list is not
   // enabled.
   views::View* app_list_background_shield_ = nullptr;
@@ -261,22 +272,37 @@
   // Whether the shelf is oriented on the side.
   bool is_side_shelf_ = false;
 
-  // The gap between the initial gesture event and the top of the window.
+  // True if the user is in the process of gesture-dragging on opened app list,
+  // or dragging the app list from shelf.
+  bool is_in_drag_ = false;
+
+  // Y position of the app list in screen space coordinate during dragging.
+  int app_list_y_position_in_screen_ = 0;
+
+  // Bottom of work area.
+  int work_area_bottom_ = 0;
+
+  // The opacity of app list background during dragging.
+  float background_opacity_ = 0.f;
+
+  // The location of initial gesture event in screen coordinates.
   gfx::Point initial_drag_point_;
+
+  // The rectangle of initial widget's window in screen coordinates.
+  gfx::Rect initial_window_bounds_;
+
   // The velocity of the gesture event.
   float last_fling_velocity_ = 0;
   // Whether the fullscreen app list feature is enabled.
   const bool is_fullscreen_app_list_enabled_;
-  // Whether a series of scroll events are being processed.
-  bool processing_scroll_event_series_;
   // The state of the app list, controlled via SetState().
-  AppListState app_list_state_;
+  AppListState app_list_state_ = PEEKING;
   // An observer that notifies AppListView when the display has changed.
   ScopedObserver<display::Screen, display::DisplayObserver> display_observer_;
 
   // A semi-transparent white overlay that covers the app list while dialogs
   // are open.
-  views::View* overlay_view_;
+  views::View* overlay_view_ = nullptr;
 
   std::unique_ptr<HideViewAnimationObserver> animation_observer_;
 
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index e4e02a4..fb42ae7 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -584,6 +584,36 @@
             start_page_view()->GetSelectedIndexForTest());
 }
 
+// Tests that when a click or tap event propagates to the AppListView, if the
+// event location is within the bounds of AppsGridView, do not close the
+// AppListView.
+TEST_F(AppListViewFullscreenTest, TapAndClickWithinAppsGridView) {
+  Initialize(0, false, false);
+  delegate_->GetTestModel()->PopulateApps(kInitialItems);
+  Show();
+  view_->SetState(AppListView::FULLSCREEN_ALL_APPS);
+  EXPECT_EQ(AppListView::FULLSCREEN_ALL_APPS, view_->app_list_state());
+  ContentsView* contents_view = view_->app_list_main_view()->contents_view();
+  AppsContainerView* container_view = contents_view->apps_container_view();
+  const gfx::Rect grid_view_bounds =
+      container_view->apps_grid_view()->GetBoundsInScreen();
+  gfx::Point target_point = grid_view_bounds.origin();
+  target_point.Offset(100, 100);
+  ASSERT_TRUE(grid_view_bounds.Contains(target_point));
+
+  // Tests gesture tap within apps grid view doesn't close app list view.
+  ui::GestureEvent tap(target_point.x(), target_point.y(), 0, base::TimeTicks(),
+                       ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+  view_->OnGestureEvent(&tap);
+  EXPECT_EQ(AppListView::FULLSCREEN_ALL_APPS, view_->app_list_state());
+
+  // Tests mouse click within apps grid view doesn't close app list view.
+  ui::MouseEvent mouse_click(ui::ET_MOUSE_PRESSED, target_point, target_point,
+                             base::TimeTicks(), 0, 0);
+  view_->OnMouseEvent(&mouse_click);
+  EXPECT_EQ(AppListView::FULLSCREEN_ALL_APPS, view_->app_list_state());
+}
+
 // Tests displaying the app list and performs a standard set of checks on its
 // top level views. Then closes the window.
 TEST_F(AppListViewTest, DisplayTest) {
diff --git a/ui/app_list/views/apps_container_view.cc b/ui/app_list/views/apps_container_view.cc
index ab652ab5..10ce60e 100644
--- a/ui/app_list/views/apps_container_view.cc
+++ b/ui/app_list/views/apps_container_view.cc
@@ -15,9 +15,11 @@
 #include "ui/app_list/views/app_list_folder_view.h"
 #include "ui/app_list/views/app_list_item_view.h"
 #include "ui/app_list/views/app_list_main_view.h"
+#include "ui/app_list/views/app_list_view.h"
 #include "ui/app_list/views/apps_grid_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/folder_background_view.h"
+#include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/suggestions_container_view.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event.h"
@@ -25,6 +27,15 @@
 
 namespace app_list {
 
+// Initial search box top padding in shelf mode.
+constexpr int kSearchBoxInitalTopPadding = 12;
+
+// Top padding of search box in peeking state.
+constexpr int kSearchBoxPeekingTopPadding = 24;
+
+// Minimum top padding of search box in fullscreen state.
+constexpr int kSearchBoxMinimumTopPadding = 24;
+
 AppsContainerView::AppsContainerView(AppListMainView* app_list_main_view,
                                      AppListModel* model)
     : is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
@@ -163,16 +174,58 @@
   if (!is_fullscreen_app_list_enabled_)
     return AppListPage::GetSearchBoxBounds();
 
-  // Makes search box and content vertically centered in screen.
   gfx::Rect search_box_bounds(contents_view()->GetDefaultSearchBoxBounds());
-  const int total_height =
-      GetDefaultContentsBounds().bottom() - search_box_bounds.y();
-  search_box_bounds.set_y(
-      std::max(search_box_bounds.y(),
-               (contents_view()->GetDisplayHeight() - total_height) / 2));
+  if (contents_view()->app_list_view()->is_in_drag())
+    search_box_bounds.set_y(GetSearchBoxTopPaddingDuringDragging());
+  else
+    search_box_bounds.set_y(GetSearchBoxFinalTopPadding());
+
   return search_box_bounds;
 }
 
+gfx::Rect AppsContainerView::GetPageBoundsDuringDragging(
+    AppListModel::State state) const {
+  float app_list_y_position_in_screen =
+      contents_view()->app_list_view()->app_list_y_position_in_screen();
+  float work_area_bottom = contents_view()->app_list_view()->work_area_bottom();
+  float drag_amount =
+      std::max(0.f, work_area_bottom - app_list_y_position_in_screen);
+
+  float y = 0;
+  float peeking_final_y =
+      kSearchBoxPeekingTopPadding + kSearchBoxPreferredHeight +
+      kSearchBoxPeekingBottomPadding - kSearchBoxFullscreenBottomPadding;
+  if (drag_amount <= (kPeekingAppListHeight - kShelfSize)) {
+    // App list is dragged from collapsed to peeking, which moved up at most
+    // |kPeekingAppListHeight - kShelfSize| (272px). The top padding of apps
+    // container view changes from |-kSearchBoxFullscreenBottomPadding| to
+    // |kSearchBoxPeekingTopPadding + kSearchBoxPreferredHeight +
+    // kSearchBoxPeekingBottomPadding - kSearchBoxFullscreenBottomPadding|.
+    y = std::ceil(
+        ((peeking_final_y + kSearchBoxFullscreenBottomPadding) * drag_amount) /
+            (kPeekingAppListHeight - kShelfSize) -
+        kSearchBoxFullscreenBottomPadding);
+  } else {
+    // App list is dragged from peeking to fullscreen, which moved up at most
+    // |peeking_to_fullscreen_height|. The top padding of apps container view
+    // changes from |peeking_final_y| to |final_y|.
+    float final_y = GetSearchBoxFinalTopPadding() + kSearchBoxPreferredHeight;
+    float peeking_to_fullscreen_height =
+        contents_view()->GetDisplayHeight() - kPeekingAppListHeight;
+    y = std::ceil((final_y - peeking_final_y) *
+                      (drag_amount - (kPeekingAppListHeight - kShelfSize)) /
+                      peeking_to_fullscreen_height +
+                  peeking_final_y);
+    y = std::max(std::min(final_y, y), peeking_final_y);
+  }
+
+  gfx::Rect onscreen_bounds = GetPageBoundsForState(state);
+  if (state == AppListModel::STATE_APPS)
+    onscreen_bounds.set_y(y);
+
+  return onscreen_bounds;
+}
+
 void AppsContainerView::OnTopIconAnimationsComplete() {
   --top_icon_animation_pending_count_;
 
@@ -273,4 +326,49 @@
     apps_grid_view_->activated_folder_item_view()->SetVisible(false);
 }
 
+int AppsContainerView::GetSearchBoxFinalTopPadding() const {
+  gfx::Rect search_box_bounds(contents_view()->GetDefaultSearchBoxBounds());
+  const int total_height =
+      GetDefaultContentsBounds().bottom() - search_box_bounds.y();
+
+  // Makes search box and content vertically centered in contents_view.
+  int y = std::max(search_box_bounds.y(),
+                   (contents_view()->GetDisplayHeight() - total_height) / 2);
+
+  // Top padding of the searchbox should not be smaller than
+  // |kSearchBoxMinimumTopPadding|
+  return std::max(y, kSearchBoxMinimumTopPadding);
+}
+
+int AppsContainerView::GetSearchBoxTopPaddingDuringDragging() const {
+  float searchbox_final_y = GetSearchBoxFinalTopPadding();
+  float peeking_to_fullscreen_height =
+      contents_view()->GetDisplayHeight() - kPeekingAppListHeight;
+  float drag_amount = std::max(
+      0, contents_view()->app_list_view()->work_area_bottom() -
+             contents_view()->app_list_view()->app_list_y_position_in_screen());
+
+  if (drag_amount <= (kPeekingAppListHeight - kShelfSize)) {
+    // App list is dragged from collapsed to peeking, which moved up at most
+    // |kPeekingAppListHeight - kShelfSize| (272px). The top padding of search
+    // box changes from |kSearchBoxInitalTopPadding| to
+    // |kSearchBoxPeekingTopPadding|,
+    return std::ceil(
+        (kSearchBoxPeekingTopPadding - kSearchBoxInitalTopPadding) +
+        ((kSearchBoxPeekingTopPadding - kSearchBoxInitalTopPadding) *
+         drag_amount) /
+            (kPeekingAppListHeight - kShelfSize));
+  } else {
+    // App list is dragged from peeking to fullscreen, which moved up at most
+    // |peeking_to_fullscreen_height|. The top padding of search box changes
+    // from |kSearchBoxPeekingTopPadding| to |searchbox_final_y|.
+    int y = (kSearchBoxPeekingTopPadding +
+             std::ceil((searchbox_final_y - kSearchBoxPeekingTopPadding) *
+                       (drag_amount - (kPeekingAppListHeight - kShelfSize)) /
+                       peeking_to_fullscreen_height));
+    y = std::max(kSearchBoxPeekingTopPadding, y);
+    return y;
+  }
+}
+
 }  // namespace app_list
diff --git a/ui/app_list/views/apps_container_view.h b/ui/app_list/views/apps_container_view.h
index c55654d..934c9529 100644
--- a/ui/app_list/views/apps_container_view.h
+++ b/ui/app_list/views/apps_container_view.h
@@ -73,6 +73,8 @@
   void OnWillBeShown() override;
   gfx::Rect GetPageBoundsForState(AppListModel::State state) const override;
   gfx::Rect GetSearchBoxBounds() const override;
+  gfx::Rect GetPageBoundsDuringDragging(
+      AppListModel::State state) const override;
 
   // TopIconAnimationObserver overrides:
   void OnTopIconAnimationsComplete() override;
@@ -106,6 +108,12 @@
 
   void PrepareToShowApps(AppListFolderItem* folder_item);
 
+  // Gets the final top padding of search box.
+  int GetSearchBoxFinalTopPadding() const;
+
+  // Gets the top padding of search box during dragging.
+  int GetSearchBoxTopPaddingDuringDragging() const;
+
   // The views below are owned by views hierarchy.
   AppsGridView* apps_grid_view_ = nullptr;
   AppListFolderView* app_list_folder_view_ = nullptr;
diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc
index 20811b3..facb7c3 100644
--- a/ui/app_list/views/apps_grid_view.cc
+++ b/ui/app_list/views/apps_grid_view.cc
@@ -97,9 +97,6 @@
 // UI.
 constexpr int kFolderDroppingCircleRadius = 39;
 
-// Bottom padding for search box.
-constexpr int kSearchBoxBottomPadding = 24;
-
 // Padding between suggested apps tiles and all apps indicator.
 constexpr int kSuggestionsAllAppsIndicatorPadding = 16;
 
@@ -109,6 +106,21 @@
 // The height of gradient fade-out zones.
 constexpr int kFadeoutZoneHeight = 24;
 
+// Range of the fraction of app list from collapsed to peeking that suggested
+// apps should change opacity.
+constexpr float kSuggestedAppsOpacityStartFraction = 0.3f;
+constexpr float kSuggestedAppsOpacityEndFraction = 0.7f;
+
+// Range of the fraction of app list from peeking to fullscreen that all apps
+// indictor should change opacity.
+constexpr float kAllAppsIndicatorOpacityStartFraction = 0.7f;
+constexpr float kAllAppsIndicatorOpacityEndFraction = 1.0f;
+
+// Range of the height of centerline above screen bottom that all apps should
+// change opacity.
+constexpr float kAllAppsOpacityStartPx = 8.0f;
+constexpr float kAllAppsOpacityEndPx = 144.0f;
+
 // Returns the size of a tile view excluding its padding.
 gfx::Size GetTileViewSize() {
   if (features::IsFullscreenAppListEnabled())
@@ -774,7 +786,7 @@
 
   if (is_fullscreen_app_list_enabled_) {
     fadeout_layer_delegate_->layer()->SetBounds(layer()->bounds());
-    rect.Inset(0, kSearchBoxBottomPadding, 0, 0);
+    rect.Inset(0, kSearchBoxFullscreenBottomPadding, 0, 0);
   }
 
   if (!folder_delegate_) {
@@ -1618,14 +1630,6 @@
   return folder_delegate_->IsOEMFolder();
 }
 
-void AppsGridView::UpdateOpacityOfItem(views::View* view_item,
-                                       float centroid_y) {
-  float delta_y = std::max(work_area_bottom_ - centroid_y, 0.0f);
-  float opacity = std::min(
-      delta_y / (AppListView::kNumOfShelfSize * AppListView::kShelfSize), 1.0f);
-  view_item->layer()->SetOpacity(is_end_gesture_ ? 1.0f : opacity);
-}
-
 void AppsGridView::DispatchDragEventForReparent(Pointer pointer,
                                                 const gfx::Point& drag_point) {
   folder_delegate_->DispatchDragEventForReparent(pointer, drag_point);
@@ -1689,36 +1693,75 @@
   item_list_ = nullptr;
 }
 
-void AppsGridView::UpdateOpacity(float work_area_bottom, bool is_end_gesture) {
-  work_area_bottom_ = work_area_bottom;
-  is_end_gesture_ = is_end_gesture;
+void AppsGridView::UpdateOpacity(int app_list_y_position_in_screen) {
+  int work_area_bottom = contents_view_->app_list_view()->work_area_bottom();
+  bool is_in_drag = contents_view_->app_list_view()->is_in_drag();
+  // The opacity of suggested apps is a function of the fractional displacement
+  // of the app list from collapsed(0) to peeking(1) state. When the fraction
+  // changes from |kSuggestedAppsOpacityStartFraction| to
+  // |kSuggestedAppsOpacityEndFraction|, the opacity of suggested apps changes
+  // from 0.f to 1.0f.
+  float fraction =
+      std::max<float>(work_area_bottom - app_list_y_position_in_screen, 0) /
+      (kPeekingAppListHeight - kShelfSize);
+  float opacity =
+      std::min(std::max((fraction - kSuggestedAppsOpacityStartFraction) /
+                            (kSuggestedAppsOpacityEndFraction -
+                             kSuggestedAppsOpacityStartFraction),
+                        0.f),
+               1.0f);
+  suggestions_container_->layer()->SetOpacity(is_in_drag ? opacity : 1.0f);
 
-  // Updates the opacity of suggestions container.
-  gfx::Rect suggestions_container_bounds =
-      suggestions_container_->GetBoundsInScreen();
-  UpdateOpacityOfItem(suggestions_container_,
-                      suggestions_container_bounds.CenterPoint().y());
+  // The opacity of all apps indicator is a function of the fractional
+  // displacement of the app list from peeking(0) to fullscreen(1) state. When
+  // the fraction changes from |kAllAppsIndicatorOpacityStartFraction| to
+  // |kAllAppsIndicatorOpacityEndFraction|, the opacity of all apps indicator
+  // changes from 0.f to 1.0f.
+  float peeking_to_fullscreen_height =
+      contents_view_->GetDisplayHeight() - kPeekingAppListHeight;
+  float drag_amount = (work_area_bottom + kShelfSize) -
+                      app_list_y_position_in_screen - kPeekingAppListHeight;
+  fraction = std::max(drag_amount / peeking_to_fullscreen_height, 0.f);
+  opacity = std::min(std::max((fraction + kAllAppsIndicatorOpacityEndFraction -
+                               kAllAppsIndicatorOpacityStartFraction - 1.0f) /
+                                  (kAllAppsIndicatorOpacityEndFraction -
+                                   kAllAppsIndicatorOpacityStartFraction),
+                              0.f),
+                     1.0f);
+  all_apps_indicator_->layer()->SetOpacity(is_in_drag ? opacity : 1.0f);
 
-  // Updates the opacity of all apps indicator.
-  gfx::Rect all_apps_indicator_bounds =
-      all_apps_indicator_->GetLabelBoundsInScreen();
-  UpdateOpacityOfItem(all_apps_indicator_,
-                      all_apps_indicator_bounds.CenterPoint().y());
-
-  // Updates the opacity of all apps.
+  // Updates the opacity of all apps. The opacity of the app starting at 0.f
+  // when the ceterline of the app is |kAllAppsOpacityStartPx| above the bottom
+  // of work area and transitioning to 1.0f by the time the centerline reaches
+  // |kAllAppsOpacityEndPx| above the work area bottom.
+  float centerline_above_work_area = 0.f;
   for (int i = 0; i < view_model_.view_size(); ++i) {
     AppListItemView* item_view = GetItemViewAt(i);
     if (item_view != drag_view_) {
       gfx::Rect view_bounds = view_model_.ideal_bounds(i);
       views::View::ConvertRectToScreen(this, &view_bounds);
-      UpdateOpacityOfItem(item_view, view_bounds.CenterPoint().y());
+      centerline_above_work_area = std::max<float>(
+          work_area_bottom + kShelfSize - view_bounds.CenterPoint().y(), 0.f);
+      opacity = std::min(
+          std::max((centerline_above_work_area - kAllAppsOpacityStartPx) /
+                       (kAllAppsOpacityEndPx - kAllAppsOpacityStartPx),
+                   0.f),
+          1.0f);
+      item_view->layer()->SetOpacity(is_in_drag ? opacity : 1.0f);
     }
   }
 
-  // Updates the opacity of page switcher buttons.
+  // Updates the opacity of page switcher buttons. The same rule as all apps.
   if (page_switcher_view_) {
     gfx::Rect switcher_bounds = page_switcher_view_->GetBoundsInScreen();
-    UpdateOpacityOfItem(page_switcher_view_, switcher_bounds.CenterPoint().y());
+    centerline_above_work_area = std::max<float>(
+        work_area_bottom + kShelfSize - switcher_bounds.CenterPoint().y(), 0.f);
+    opacity = std::min(
+        std::max((centerline_above_work_area - kAllAppsOpacityStartPx) /
+                     (kAllAppsOpacityEndPx - kAllAppsOpacityStartPx),
+                 0.f),
+        1.0f);
+    page_switcher_view_->layer()->SetOpacity(is_in_drag ? opacity : 1.0f);
   }
 }
 
@@ -2249,13 +2292,13 @@
     return 0;
 
   if (page == 0) {
-    return kSearchBoxBottomPadding +
+    return kSearchBoxFullscreenBottomPadding +
            suggestions_container_->GetPreferredSize().height() +
            kSuggestionsAllAppsIndicatorPadding +
            all_apps_indicator_->GetPreferredSize().height() +
            kAllAppsIndicatorExtraPadding;
   }
-  return kSearchBoxBottomPadding - kTileVerticalPadding;
+  return kSearchBoxFullscreenBottomPadding - kTileVerticalPadding;
 }
 
 gfx::Rect AppsGridView::GetExpectedTileBounds(int slot) const {
diff --git a/ui/app_list/views/apps_grid_view.h b/ui/app_list/views/apps_grid_view.h
index 37cb41f4..68c04ca 100644
--- a/ui/app_list/views/apps_grid_view.h
+++ b/ui/app_list/views/apps_grid_view.h
@@ -193,11 +193,8 @@
   // The grid view must be inside a folder view.
   void OnFolderItemRemoved();
 
-  // Updates the opacity of all the items in the grid during dragging. The
-  // opacity of each item is based on how much the item's |centroid_y| is above
-  // |work_area_bottom|. If |is_end_gesture| is true, set all the items opacity
-  // to 1.0f.
-  void UpdateOpacity(float work_area_bottom, bool is_end_gesture);
+  // Updates the opacity of all the items in the grid during dragging.
+  void UpdateOpacity(int app_list_y_position_in_screen);
 
   // Return the view model for test purposes.
   const views::ViewModelT<AppListItemView>* view_model_for_test() const {
@@ -488,9 +485,6 @@
   // Returns true if the grid view is under an OEM folder.
   bool IsUnderOEMFolder();
 
-  // Updates opacity of |view_item| in the app list based on |centroid_y|.
-  void UpdateOpacityOfItem(views::View* view_item, float centroid_y);
-
   AppListModel* model_ = nullptr;         // Owned by AppListView.
   AppListItemList* item_list_ = nullptr;  // Not owned.
 
@@ -599,9 +593,6 @@
   // True if the fullscreen app list feature is enabled.
   const bool is_fullscreen_app_list_enabled_;
 
-  // The bottom of work area.
-  float work_area_bottom_ = 0.f;
-
   // True if it is the end gesture from shelf dragging.
   bool is_end_gesture_ = false;
 
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index c6c9f75..99b4ce6 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -487,7 +487,10 @@
     return;
 
   for (AppListPage* page : app_list_pages_) {
-    page->SetBoundsRect(page->GetPageBoundsForState(GetActiveState()));
+    if (app_list_view_ && app_list_view_->is_in_drag())
+      page->SetBoundsRect(page->GetPageBoundsDuringDragging(GetActiveState()));
+    else
+      page->SetBoundsRect(page->GetPageBoundsForState(GetActiveState()));
   }
 
   // The search box is contained in a widget so set the bounds of the widget
diff --git a/ui/app_list/views/contents_view.h b/ui/app_list/views/contents_view.h
index f8ff40d..56a55c04 100644
--- a/ui/app_list/views/contents_view.h
+++ b/ui/app_list/views/contents_view.h
@@ -105,6 +105,8 @@
 
   AppListMainView* app_list_main_view() const { return app_list_main_view_; }
 
+  AppListView* app_list_view() const { return app_list_view_; }
+
   // Returns the pagination model for the ContentsView.
   const PaginationModel& pagination_model() { return pagination_model_; }
 
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index c8a5c68..2b62cfc 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -22,6 +22,7 @@
 #include "ui/app_list/views/app_list_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view_delegate.h"
+#include "ui/app_list/views/search_result_page_view.h"
 #include "ui/base/ime/text_input_flags.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -54,7 +55,6 @@
 constexpr int kInnerPadding = 24;
 constexpr int kPreferredWidth = 360;
 constexpr int kPreferredWidthFullscreen = 544;
-constexpr int kSearchBoxPreferredHeight = 48;
 constexpr int kSearchBoxBorderWidth = 4;
 
 constexpr SkColor kHintTextColor = SkColorSetARGBMacro(0xFF, 0xA0, 0xA0, 0xA0);
@@ -69,6 +69,11 @@
 
 constexpr int kLightVibrantBlendAlpha = 0xE6;
 
+// Range of the fraction of app list from collapsed to peeking that search box
+// should change opacity.
+constexpr float kOpacityStartFraction = 0.1f;
+constexpr float kOpacityEndFraction = 0.6f;
+
 // Color of placeholder text in zero query state.
 constexpr SkColor kZeroQuerySearchboxColor =
     SkColorSetARGBMacro(0x8A, 0x00, 0x00, 0x00);
@@ -644,21 +649,28 @@
   return background_color_;
 }
 
-void SearchBoxView::UpdateOpacity(float work_area_bottom, bool is_end_gesture) {
-  float opacity = 1.0f;
-  if (!is_end_gesture) {
-    gfx::Rect search_box_bounds = this->GetBoundsInScreen();
-    float delta_y = std::max(
-        (work_area_bottom - search_box_bounds.CenterPoint().y()), 0.0f);
-    opacity = std::min(
-        delta_y / (AppListView::kNumOfShelfSize * AppListView::kShelfSize),
-        1.0f);
-  }
+void SearchBoxView::UpdateOpacity(int app_list_y_position_in_screen) {
+  // The opacity of searchbox is a function of the fractional displacement of
+  // the app list from collapsed(0) to peeking(1) state. When the fraction
+  // changes from |kOpacityStartFraction| to |kOpaticyEndFraction|, the opacity
+  // of searchbox changes from 0.f to 1.0f.
+  ContentsView* contents_view = static_cast<ContentsView*>(contents_view_);
+  float fraction =
+      std::max<float>(0, contents_view->app_list_view()->work_area_bottom() -
+                             app_list_y_position_in_screen) /
+      (kPeekingAppListHeight - kShelfSize);
 
-  // Restores the opacity of searchbox to 1.0f if it is the end of the gesture
-  // dragging.
+  float opacity = 1.0f;
+  if (contents_view->app_list_view()->is_in_drag())
+    opacity =
+        std::min(std::max((fraction - kOpacityStartFraction) /
+                              (kOpacityEndFraction - kOpacityStartFraction),
+                          0.f),
+                 1.0f);
+
+  // Restores the opacity of searchbox if the gesture dragging ends.
   this->layer()->SetOpacity(opacity);
-  contents_view_->layer()->SetOpacity(opacity);
+  contents_view->search_results_page_view()->layer()->SetOpacity(opacity);
 }
 
 bool SearchBoxView::IsArrowKey(const ui::KeyEvent& event) {
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index 25af2b31..d0de203 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -135,7 +135,7 @@
   SkColor GetBackgroundColorForState(AppListModel::State state) const;
 
   // Updates the opacity of the searchbox.
-  void UpdateOpacity(float work_area_bottom, bool is_end_gesture);
+  void UpdateOpacity(int app_list_y_position_in_screen);
 
   // Used only in the tests to get the current search icon.
   views::ImageView* get_search_icon_for_test() { return search_icon_; }
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index 0f4d9ad..4e4146b 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -144,6 +144,9 @@
     : selected_index_(0),
       is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()),
       contents_view_(new views::View) {
+  SetPaintToLayer();
+  layer()->SetFillsBoundsOpaquely(false);
+
   if (is_fullscreen_app_list_enabled_) {
     contents_view_->SetLayoutManager(
         new views::BoxLayout(views::BoxLayout::kVertical, gfx::Insets(), 0));
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index f982bb6..bd95de0 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -47,7 +47,6 @@
 
 // Layout constants.
 constexpr int kInstantContainerSpacing = 24;
-constexpr int kSearchBoxBottomPadding = 12;
 constexpr int kSearchBoxAndTilesSpacing = 35;
 constexpr int kStartPageSearchBoxWidth = 480;
 constexpr int kStartPageSearchBoxWidthFullscreen = 544;
@@ -147,8 +146,8 @@
   views::BoxLayout* instant_layout_manager = new views::BoxLayout(
       views::BoxLayout::kVertical, gfx::Insets(), kInstantContainerSpacing);
   if (is_fullscreen_app_list_enabled_) {
-    instant_layout_manager->set_inside_border_insets(
-        gfx::Insets(kSearchBoxTopPadding, 0, kSearchBoxBottomPadding, 0));
+    instant_layout_manager->set_inside_border_insets(gfx::Insets(
+        kSearchBoxTopPadding, 0, kSearchBoxPeekingBottomPadding, 0));
   } else {
     instant_layout_manager->set_inside_border_insets(
         gfx::Insets(0, 0, kSearchBoxAndTilesSpacing, 0));
@@ -446,8 +445,8 @@
 void StartPageView::UpdateOpacityOfItem(views::View* view_item,
                                         float centroid_y) {
   float delta_y = std::max(work_area_bottom_ - centroid_y, 0.f);
-  float opacity = std::min(
-      delta_y / (AppListView::kNumOfShelfSize * AppListView::kShelfSize), 1.0f);
+  float opacity =
+      std::min(delta_y / (AppListView::kNumOfShelfSize * kShelfSize), 1.0f);
   view_item->layer()->SetOpacity(is_end_gesture_ ? 1.0f : opacity);
 }
 
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index f1e6c73b..002a68d0 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -377,8 +377,6 @@
 
   if (is_fuchsia) {
     sources += [
-      "clipboard/clipboard_fuchsia.cc",
-      "clipboard/clipboard_fuchsia.h",
       "idle/idle_fuchsia.cc",
       "resource/resource_bundle_fuchsia.cc",
     ]
@@ -649,10 +647,6 @@
   if (is_android || is_ios) {
     sources -= [ "device_form_factor_desktop.cc" ]
   }
-
-  if (is_fuchsia) {
-    sources += [ "cursor/cursor_loader_fuchsia.cc" ]
-  }
 }
 
 static_library("test_support") {
diff --git a/ui/base/clipboard/clipboard_fuchsia.cc b/ui/base/clipboard/clipboard_fuchsia.cc
deleted file mode 100644
index 72de3f4..0000000
--- a/ui/base/clipboard/clipboard_fuchsia.cc
+++ /dev/null
@@ -1,281 +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 "ui/base/clipboard/clipboard_fuchsia.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/callback.h"
-#include "base/lazy_instance.h"
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/lock.h"
-#include "base/time/time.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace ui {
-
-const char kMimeTypeBitmap[] = "image/bmp";
-
-// Clipboard::FormatType implementation.
-Clipboard::FormatType::FormatType() {}
-
-Clipboard::FormatType::FormatType(const std::string& native_format)
-    : data_(native_format) {}
-
-Clipboard::FormatType::~FormatType() {}
-
-std::string Clipboard::FormatType::Serialize() const {
-  return data_;
-}
-
-// static
-Clipboard::FormatType Clipboard::FormatType::Deserialize(
-    const std::string& serialization) {
-  return FormatType(serialization);
-}
-
-bool Clipboard::FormatType::operator<(const FormatType& other) const {
-  return data_ < other.data_;
-}
-
-bool Clipboard::FormatType::Equals(const FormatType& other) const {
-  return data_ == other.data_;
-}
-
-// Various predefined FormatTypes.
-// static
-Clipboard::FormatType Clipboard::GetFormatType(
-    const std::string& format_string) {
-  return FormatType::Deserialize(format_string);
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetUrlFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList));
-  return type;
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetUrlWFormatType() {
-  return GetUrlFormatType();
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText));
-  return type;
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() {
-  return GetPlainTextFormatType();
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste));
-  return type;
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetHtmlFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML));
-  return type;
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetRtfFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF));
-  return type;
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetBitmapFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap));
-  return type;
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData));
-  return type;
-}
-
-// static
-const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() {
-  CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData));
-  return type;
-}
-
-// Clipboard factory method.
-// static
-Clipboard* Clipboard::Create() {
-  return new ClipboardFuchsia;
-}
-
-// ClipboardFuchsia implementation.
-
-ClipboardFuchsia::ClipboardFuchsia() {
-  DCHECK(CalledOnValidThread());
-}
-
-ClipboardFuchsia::~ClipboardFuchsia() {
-  DCHECK(CalledOnValidThread());
-}
-
-void ClipboardFuchsia::OnPreShutdown() {}
-
-uint64_t ClipboardFuchsia::GetSequenceNumber(ClipboardType type) const {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-  return 0;
-}
-
-bool ClipboardFuchsia::IsFormatAvailable(const Clipboard::FormatType& format,
-                                         ClipboardType type) const {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTIMPLEMENTED();
-  return false;
-}
-
-void ClipboardFuchsia::Clear(ClipboardType type) {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::ReadAvailableTypes(ClipboardType type,
-                                          std::vector<base::string16>* types,
-                                          bool* contains_filenames) const {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTIMPLEMENTED();
-  types->clear();
-  *contains_filenames = false;
-}
-
-void ClipboardFuchsia::ReadText(ClipboardType type,
-                                base::string16* result) const {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTREACHED();
-}
-
-void ClipboardFuchsia::ReadAsciiText(ClipboardType type,
-                                     std::string* result) const {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTREACHED();
-}
-
-// Note: |src_url| isn't really used. It is only implemented in Windows
-void ClipboardFuchsia::ReadHTML(ClipboardType type,
-                                base::string16* markup,
-                                std::string* src_url,
-                                uint32_t* fragment_start,
-                                uint32_t* fragment_end) const {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTREACHED();
-}
-
-void ClipboardFuchsia::ReadRTF(ClipboardType type, std::string* result) const {
-  DCHECK(CalledOnValidThread());
-  NOTREACHED();
-}
-
-SkBitmap ClipboardFuchsia::ReadImage(ClipboardType type) const {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTREACHED();
-  return SkBitmap();
-}
-
-void ClipboardFuchsia::ReadCustomData(ClipboardType clipboard_type,
-                                      const base::string16& type,
-                                      base::string16* result) const {
-  DCHECK(CalledOnValidThread());
-  NOTREACHED();
-}
-
-void ClipboardFuchsia::ReadBookmark(base::string16* title,
-                                    std::string* url) const {
-  DCHECK(CalledOnValidThread());
-  NOTREACHED();
-}
-
-void ClipboardFuchsia::ReadData(const Clipboard::FormatType& format,
-                                std::string* result) const {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-  result->clear();
-}
-
-base::Time ClipboardFuchsia::GetLastModifiedTime() const {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-  return base::Time();
-}
-
-void ClipboardFuchsia::ClearLastModifiedTime() {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-// Main entry point used to write several values in the clipboard.
-void ClipboardFuchsia::WriteObjects(ClipboardType type,
-                                    const ObjectMap& objects) {
-  DCHECK(CalledOnValidThread());
-  DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::WriteText(const char* text_data, size_t text_len) {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::WriteHTML(const char* markup_data,
-                                 size_t markup_len,
-                                 const char* url_data,
-                                 size_t url_len) {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::WriteRTF(const char* rtf_data, size_t data_len) {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::WriteBookmark(const char* title_data,
-                                     size_t title_len,
-                                     const char* url_data,
-                                     size_t url_len) {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::WriteWebSmartPaste() {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::WriteBitmap(const SkBitmap& bitmap) {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-void ClipboardFuchsia::WriteData(const Clipboard::FormatType& format,
-                                 const char* data_data,
-                                 size_t data_len) {
-  DCHECK(CalledOnValidThread());
-  NOTIMPLEMENTED();
-}
-
-}  // namespace ui
diff --git a/ui/base/clipboard/clipboard_fuchsia.h b/ui/base/clipboard/clipboard_fuchsia.h
deleted file mode 100644
index ca68893a..0000000
--- a/ui/base/clipboard/clipboard_fuchsia.h
+++ /dev/null
@@ -1,71 +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_BASE_CLIPBOARD_CLIPBOARD_FUCHSIA_H_
-#define UI_BASE_CLIPBOARD_CLIPBOARD_FUCHSIA_H_
-
-#include "ui/base/clipboard/clipboard.h"
-
-#include "base/macros.h"
-#include "base/time/time.h"
-
-namespace ui {
-
-// This is a stub implementation of Clipboard for Fuchsia.
-// TODO(fuchsia): Implement this class.
-class ClipboardFuchsia : public Clipboard {
- private:
-  friend class Clipboard;
-
-  ClipboardFuchsia();
-  ~ClipboardFuchsia() override;
-
-  // Clipboard overrides:
-  void OnPreShutdown() override;
-  uint64_t GetSequenceNumber(ClipboardType type) const override;
-  bool IsFormatAvailable(const FormatType& format,
-                         ClipboardType type) const override;
-  void Clear(ClipboardType type) override;
-  void ReadAvailableTypes(ClipboardType type,
-                          std::vector<base::string16>* types,
-                          bool* contains_filenames) const override;
-  void ReadText(ClipboardType type, base::string16* result) const override;
-  void ReadAsciiText(ClipboardType type, std::string* result) const override;
-  void ReadHTML(ClipboardType type,
-                base::string16* markup,
-                std::string* src_url,
-                uint32_t* fragment_start,
-                uint32_t* fragment_end) const override;
-  void ReadRTF(ClipboardType type, std::string* result) const override;
-  SkBitmap ReadImage(ClipboardType type) const override;
-  void ReadCustomData(ClipboardType clipboard_type,
-                      const base::string16& type,
-                      base::string16* result) const override;
-  void ReadBookmark(base::string16* title, std::string* url) const override;
-  void ReadData(const FormatType& format, std::string* result) const override;
-  base::Time GetLastModifiedTime() const override;
-  void ClearLastModifiedTime() override;
-  void WriteObjects(ClipboardType type, const ObjectMap& objects) override;
-  void WriteText(const char* text_data, size_t text_len) override;
-  void WriteHTML(const char* markup_data,
-                 size_t markup_len,
-                 const char* url_data,
-                 size_t url_len) override;
-  void WriteRTF(const char* rtf_data, size_t data_len) override;
-  void WriteBookmark(const char* title_data,
-                     size_t title_len,
-                     const char* url_data,
-                     size_t url_len) override;
-  void WriteWebSmartPaste() override;
-  void WriteBitmap(const SkBitmap& bitmap) override;
-  void WriteData(const FormatType& format,
-                 const char* data_data,
-                 size_t data_len) override;
-
-  DISALLOW_COPY_AND_ASSIGN(ClipboardFuchsia);
-};
-
-}  // namespace ui
-
-#endif  // UI_BASE_CLIPBOARD_CLIPBOARD_FUCHSIA_H_
diff --git a/ui/base/cursor/cursor_loader_fuchsia.cc b/ui/base/cursor/cursor_loader_fuchsia.cc
deleted file mode 100644
index a7e4280..0000000
--- a/ui/base/cursor/cursor_loader_fuchsia.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/base/cursor/cursor_loader.h"
-
-#include "base/logging.h"
-
-namespace ui {
-
-CursorLoader* CursorLoader::Create() {
-  NOTIMPLEMENTED();
-  return nullptr;
-}
-
-}  // namespace ui
diff --git a/ui/base/ime/BUILD.gn b/ui/base/ime/BUILD.gn
index cc69675..6d44738 100644
--- a/ui/base/ime/BUILD.gn
+++ b/ui/base/ime/BUILD.gn
@@ -52,11 +52,11 @@
     "composition_text.h",
     "composition_text_util_pango.cc",
     "composition_text_util_pango.h",
-    "composition_underline.h",
     "ime_bridge.cc",
     "ime_bridge.h",
     "ime_engine_handler_interface.h",
     "ime_input_context_handler_interface.h",
+    "ime_text_span.h",
     "infolist_entry.cc",
     "infolist_entry.h",
     "input_method.h",
@@ -199,4 +199,11 @@
       "//ui/ozone",
     ]
   }
+
+  if (is_fuchsia) {
+    sources -= [
+      "input_method_auralinux.cc",
+      "input_method_auralinux.h",
+    ]
+  }
 }
diff --git a/ui/base/ime/composition_text.cc b/ui/base/ime/composition_text.cc
index a2e583f6..3ee2ae3 100644
--- a/ui/base/ime/composition_text.cc
+++ b/ui/base/ime/composition_text.cc
@@ -16,15 +16,15 @@
 
 void CompositionText::Clear() {
   text.clear();
-  underlines.clear();
+  ime_text_spans.clear();
   selection = gfx::Range();
 }
 
 void CompositionText::CopyFrom(const CompositionText& obj) {
   Clear();
   text = obj.text;
-  for (size_t i = 0; i < obj.underlines.size(); i++) {
-    underlines.push_back(obj.underlines[i]);
+  for (size_t i = 0; i < obj.ime_text_spans.size(); i++) {
+    ime_text_spans.push_back(obj.ime_text_spans[i]);
   }
   selection = obj.selection;
 }
diff --git a/ui/base/ime/composition_text.h b/ui/base/ime/composition_text.h
index ba56c62..f46dc3c 100644
--- a/ui/base/ime/composition_text.h
+++ b/ui/base/ime/composition_text.h
@@ -8,7 +8,7 @@
 #include <stddef.h>
 
 #include "base/strings/string16.h"
-#include "ui/base/ime/composition_underline.h"
+#include "ui/base/ime/ime_text_span.h"
 #include "ui/base/ime/ui_base_ime_export.h"
 #include "ui/gfx/range/range.h"
 
@@ -21,12 +21,11 @@
   ~CompositionText();
 
   bool operator==(const CompositionText& rhs) const {
-    if ((this->text != rhs.text) ||
-        (this->selection != rhs.selection) ||
-        (this->underlines.size() != rhs.underlines.size()))
+    if ((this->text != rhs.text) || (this->selection != rhs.selection) ||
+        (this->ime_text_spans.size() != rhs.ime_text_spans.size()))
       return false;
-    for (size_t i = 0; i < this->underlines.size(); ++i) {
-      if (this->underlines[i] != rhs.underlines[i])
+    for (size_t i = 0; i < this->ime_text_spans.size(); ++i) {
+      if (this->ime_text_spans[i] != rhs.ime_text_spans[i])
         return false;
     }
     return true;
@@ -43,10 +42,8 @@
   // Content of the composition text.
   base::string16 text;
 
-  // Underline information of the composition text.
-  // They must be sorted in ascending order by their start_offset and cannot be
-  // overlapped with each other.
-  CompositionUnderlines underlines;
+  // ImeTextSpan information for the composition text.
+  ImeTextSpans ime_text_spans;
 
   // Selection range in the composition text. It represents the caret position
   // if the range length is zero. Usually it's used for representing the target
diff --git a/ui/base/ime/composition_text_unittest.cc b/ui/base/ime/composition_text_unittest.cc
index c03090a5..c1623af6 100644
--- a/ui/base/ime/composition_text_unittest.cc
+++ b/ui/base/ime/composition_text_unittest.cc
@@ -13,21 +13,21 @@
 
 TEST(CompositionTextTest, CopyTest) {
   const base::string16 kSampleText = base::UTF8ToUTF16("Sample Text");
-  const CompositionUnderline kSampleUnderline1(10, 20, SK_ColorBLACK, false,
-                                               SK_ColorTRANSPARENT);
+  const ImeTextSpan kSampleUnderline1(10, 20, SK_ColorBLACK, false,
+                                      SK_ColorTRANSPARENT);
 
-  const CompositionUnderline kSampleUnderline2(11, 21, SK_ColorBLACK, true,
-                                               SK_ColorTRANSPARENT);
+  const ImeTextSpan kSampleUnderline2(11, 21, SK_ColorBLACK, true,
+                                      SK_ColorTRANSPARENT);
 
-  const CompositionUnderline kSampleUnderline3(12, 22, SK_ColorRED, false,
-                                               SK_ColorTRANSPARENT);
+  const ImeTextSpan kSampleUnderline3(12, 22, SK_ColorRED, false,
+                                      SK_ColorTRANSPARENT);
 
   // Make CompositionText
   CompositionText text;
   text.text = kSampleText;
-  text.underlines.push_back(kSampleUnderline1);
-  text.underlines.push_back(kSampleUnderline2);
-  text.underlines.push_back(kSampleUnderline3);
+  text.ime_text_spans.push_back(kSampleUnderline1);
+  text.ime_text_spans.push_back(kSampleUnderline2);
+  text.ime_text_spans.push_back(kSampleUnderline3);
   text.selection.set_start(30);
   text.selection.set_end(40);
 
@@ -35,15 +35,16 @@
   text2.CopyFrom(text);
 
   EXPECT_EQ(text.text, text2.text);
-  EXPECT_EQ(text.underlines.size(), text2.underlines.size());
-  for (size_t i = 0; i < text.underlines.size(); ++i) {
-    EXPECT_EQ(text.underlines[i].start_offset,
-              text2.underlines[i].start_offset);
-    EXPECT_EQ(text.underlines[i].end_offset, text2.underlines[i].end_offset);
-    EXPECT_EQ(text.underlines[i].color, text2.underlines[i].color);
-    EXPECT_EQ(text.underlines[i].thick, text2.underlines[i].thick);
-    EXPECT_EQ(text.underlines[i].background_color,
-              text2.underlines[i].background_color);
+  EXPECT_EQ(text.ime_text_spans.size(), text2.ime_text_spans.size());
+  for (size_t i = 0; i < text.ime_text_spans.size(); ++i) {
+    EXPECT_EQ(text.ime_text_spans[i].start_offset,
+              text2.ime_text_spans[i].start_offset);
+    EXPECT_EQ(text.ime_text_spans[i].end_offset,
+              text2.ime_text_spans[i].end_offset);
+    EXPECT_EQ(text.ime_text_spans[i].color, text2.ime_text_spans[i].color);
+    EXPECT_EQ(text.ime_text_spans[i].thick, text2.ime_text_spans[i].thick);
+    EXPECT_EQ(text.ime_text_spans[i].background_color,
+              text2.ime_text_spans[i].background_color);
   }
 
   EXPECT_EQ(text.selection.start(), text2.selection.start());
diff --git a/ui/base/ime/composition_text_util_pango.cc b/ui/base/ime/composition_text_util_pango.cc
index b8a9297..8b30ff6 100644
--- a/ui/base/ime/composition_text_util_pango.cc
+++ b/ui/base/ime/composition_text_util_pango.cc
@@ -75,44 +75,41 @@
 
       if (background_attr || underline_attr) {
         // Use a black thin underline by default.
-        CompositionUnderline underline(char16_offsets[start],
-                                       char16_offsets[end],
-                                       SK_ColorBLACK,
-                                       false,
-                                       SK_ColorTRANSPARENT);
+        ImeTextSpan ime_text_span(char16_offsets[start], char16_offsets[end],
+                                  SK_ColorBLACK, false, SK_ColorTRANSPARENT);
 
         // Always use thick underline for a range with background color, which
         // is usually the selection range.
         if (background_attr) {
-          underline.thick = true;
+          ime_text_span.thick = true;
           // If the cursor is at start or end of this underline, then we treat
           // it as the selection range as well, but make sure to set the cursor
           // position to the selection end.
-          if (underline.start_offset == cursor_offset) {
-            composition->selection.set_start(underline.end_offset);
+          if (ime_text_span.start_offset == cursor_offset) {
+            composition->selection.set_start(ime_text_span.end_offset);
             composition->selection.set_end(cursor_offset);
-          } else if (underline.end_offset == cursor_offset) {
-            composition->selection.set_start(underline.start_offset);
+          } else if (ime_text_span.end_offset == cursor_offset) {
+            composition->selection.set_start(ime_text_span.start_offset);
             composition->selection.set_end(cursor_offset);
           }
         }
         if (underline_attr) {
           int type = reinterpret_cast<PangoAttrInt*>(underline_attr)->value;
           if (type == PANGO_UNDERLINE_DOUBLE)
-            underline.thick = true;
+            ime_text_span.thick = true;
           else if (type == PANGO_UNDERLINE_ERROR)
-            underline.color = SK_ColorRED;
+            ime_text_span.color = SK_ColorRED;
         }
-        composition->underlines.push_back(underline);
+        composition->ime_text_spans.push_back(ime_text_span);
       }
     } while (pango_attr_iterator_next(iter));
     pango_attr_iterator_destroy(iter);
   }
 
   // Use a black thin underline by default.
-  if (composition->underlines.empty()) {
-    composition->underlines.push_back(CompositionUnderline(
-        0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
+  if (composition->ime_text_spans.empty()) {
+    composition->ime_text_spans.push_back(
+        ImeTextSpan(0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT));
   }
 }
 
diff --git a/ui/base/ime/composition_text_util_pango_unittest.cc b/ui/base/ime/composition_text_util_pango_unittest.cc
index c645e30..eb2f340 100644
--- a/ui/base/ime/composition_text_util_pango_unittest.cc
+++ b/ui/base/ime/composition_text_util_pango_unittest.cc
@@ -25,7 +25,7 @@
   int end_offset;
 };
 
-struct Underline {
+struct ImeTextSpan {
   unsigned start_offset;
   unsigned end_offset;
   uint32_t color;
@@ -36,7 +36,7 @@
 struct TestData {
   const char* text;
   const AttributeInfo attrs[10];
-  const Underline underlines[10];
+  const ImeTextSpan ime_text_spans[10];
 };
 
 const TestData kTestData[] = {
@@ -95,8 +95,7 @@
       {0, 0, 0, false, SK_ColorTRANSPARENT}}},
 };
 
-void CompareUnderline(const Underline& a,
-                      const ui::CompositionUnderline& b) {
+void CompareImeTextSpan(const ImeTextSpan& a, const ui::ImeTextSpan& b) {
   EXPECT_EQ(a.start_offset, b.start_offset);
   EXPECT_EQ(a.end_offset, b.end_offset);
   EXPECT_EQ(a.color, b.color);
@@ -135,11 +134,11 @@
     ui::CompositionText result;
     ui::ExtractCompositionTextFromGtkPreedit(text, pango_attrs, 0, &result);
 
-    const Underline* underlines = kTestData[i].underlines;
-    for (size_t u = 0; underlines[u].color &&
-         u < result.underlines.size(); ++u) {
-      SCOPED_TRACE(testing::Message() << "Underline:" << u);
-      CompareUnderline(underlines[u], result.underlines[u]);
+    const ImeTextSpan* ime_text_spans = kTestData[i].ime_text_spans;
+    for (size_t u = 0;
+         ime_text_spans[u].color && u < result.ime_text_spans.size(); ++u) {
+      SCOPED_TRACE(testing::Message() << "ImeTextSpan:" << u);
+      CompareImeTextSpan(ime_text_spans[u], result.ime_text_spans[u]);
     }
 
     pango_attr_list_unref(pango_attrs);
diff --git a/ui/base/ime/composition_underline.h b/ui/base/ime/ime_text_span.h
similarity index 61%
rename from ui/base/ime/composition_underline.h
rename to ui/base/ime/ime_text_span.h
index 9eb7763..ddfbd43 100644
--- a/ui/base/ime/composition_underline.h
+++ b/ui/base/ime/ime_text_span.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 UI_BASE_IME_COMPOSITION_UNDERLINE_H_
-#define UI_BASE_IME_COMPOSITION_UNDERLINE_H_
+#ifndef UI_BASE_IME_IME_TEXT_SPAN_H_
+#define UI_BASE_IME_IME_TEXT_SPAN_H_
 
 #include <stdint.h>
 
@@ -13,10 +13,10 @@
 
 namespace ui {
 
-// Intentionally keep sync with blink::WebCompositionUnderline defined in:
-// third_party/WebKit/public/web/WebCompositionUnderline.h
-struct CompositionUnderline {
-  CompositionUnderline()
+// Intentionally keep sync with blink::WebImeTextSpan defined in:
+// third_party/WebKit/public/web/WebImeTextSpan.h
+struct ImeTextSpan {
+  ImeTextSpan()
       : start_offset(0),
         end_offset(0),
         color(SK_ColorTRANSPARENT),
@@ -24,35 +24,33 @@
         background_color(SK_ColorTRANSPARENT) {}
 
   // TODO(huangs): remove this constructor.
-  CompositionUnderline(uint32_t s, uint32_t e, SkColor c, bool t)
+  ImeTextSpan(uint32_t s, uint32_t e, SkColor c, bool t)
       : start_offset(s),
         end_offset(e),
         color(c),
         thick(t),
         background_color(SK_ColorTRANSPARENT) {}
 
-  CompositionUnderline(uint32_t s, uint32_t e, SkColor c, bool t, SkColor bc)
+  ImeTextSpan(uint32_t s, uint32_t e, SkColor c, bool t, SkColor bc)
       : start_offset(s),
         end_offset(e),
         color(c),
         thick(t),
         background_color(bc) {}
 
-  bool operator<(const CompositionUnderline& rhs) const {
+  bool operator<(const ImeTextSpan& rhs) const {
     return std::tie(start_offset, end_offset) <
            std::tie(rhs.start_offset, rhs.end_offset);
   }
 
-  bool operator==(const CompositionUnderline& rhs) const {
+  bool operator==(const ImeTextSpan& rhs) const {
     return (this->start_offset == rhs.start_offset) &&
            (this->end_offset == rhs.end_offset) && (this->color == rhs.color) &&
            (this->thick == rhs.thick) &&
            (this->background_color == rhs.background_color);
   }
 
-  bool operator!=(const CompositionUnderline& rhs) const {
-    return !(*this == rhs);
-  }
+  bool operator!=(const ImeTextSpan& rhs) const { return !(*this == rhs); }
 
   uint32_t start_offset;
   uint32_t end_offset;
@@ -61,8 +59,8 @@
   SkColor background_color;
 };
 
-typedef std::vector<CompositionUnderline> CompositionUnderlines;
+typedef std::vector<ImeTextSpan> ImeTextSpans;
 
 }  // namespace ui
 
-#endif  // UI_BASE_IME_COMPOSITION_UNDERLINE_H_
+#endif  // UI_BASE_IME_IME_TEXT_SPAN_H_
diff --git a/ui/base/ime/input_method_chromeos.cc b/ui/base/ime/input_method_chromeos.cc
index 1312ba4..a5dd1d6a 100644
--- a/ui/base/ime/input_method_chromeos.cc
+++ b/ui/base/ime/input_method_chromeos.cc
@@ -613,17 +613,18 @@
 
   out_composition->selection = gfx::Range(cursor_offset);
 
-  const CompositionUnderlines text_underlines = text.underlines;
-  if (!text_underlines.empty()) {
-    for (size_t i = 0; i < text_underlines.size(); ++i) {
-      const uint32_t start = text_underlines[i].start_offset;
-      const uint32_t end = text_underlines[i].end_offset;
+  const ImeTextSpans text_ime_text_spans = text.ime_text_spans;
+  if (!text_ime_text_spans.empty()) {
+    for (size_t i = 0; i < text_ime_text_spans.size(); ++i) {
+      const uint32_t start = text_ime_text_spans[i].start_offset;
+      const uint32_t end = text_ime_text_spans[i].end_offset;
       if (start >= end)
         continue;
-      CompositionUnderline underline(
-          char16_offsets[start], char16_offsets[end], text_underlines[i].color,
-          text_underlines[i].thick, text_underlines[i].background_color);
-      out_composition->underlines.push_back(underline);
+      ImeTextSpan ime_text_span(char16_offsets[start], char16_offsets[end],
+                                text_ime_text_spans[i].color,
+                                text_ime_text_spans[i].thick,
+                                text_ime_text_spans[i].background_color);
+      out_composition->ime_text_spans.push_back(ime_text_span);
     }
   }
 
@@ -631,28 +632,26 @@
   if (text.selection.start() < text.selection.end()) {
     const uint32_t start = text.selection.start();
     const uint32_t end = text.selection.end();
-    CompositionUnderline underline(char16_offsets[start],
-                                   char16_offsets[end],
-                                   SK_ColorBLACK,
-                                   true /* thick */,
-                                   SK_ColorTRANSPARENT);
-    out_composition->underlines.push_back(underline);
+    ImeTextSpan ime_text_span(char16_offsets[start], char16_offsets[end],
+                              SK_ColorBLACK, true /* thick */,
+                              SK_ColorTRANSPARENT);
+    out_composition->ime_text_spans.push_back(ime_text_span);
 
-    // If the cursor is at start or end of this underline, then we treat
+    // If the cursor is at start or end of this ime_text_span, then we treat
     // it as the selection range as well, but make sure to set the cursor
     // position to the selection end.
-    if (underline.start_offset == cursor_offset) {
-      out_composition->selection.set_start(underline.end_offset);
+    if (ime_text_span.start_offset == cursor_offset) {
+      out_composition->selection.set_start(ime_text_span.end_offset);
       out_composition->selection.set_end(cursor_offset);
-    } else if (underline.end_offset == cursor_offset) {
-      out_composition->selection.set_start(underline.start_offset);
+    } else if (ime_text_span.end_offset == cursor_offset) {
+      out_composition->selection.set_start(ime_text_span.start_offset);
       out_composition->selection.set_end(cursor_offset);
     }
   }
 
   // Use a black thin underline by default.
-  if (out_composition->underlines.empty()) {
-    out_composition->underlines.push_back(CompositionUnderline(
+  if (out_composition->ime_text_spans.empty()) {
+    out_composition->ime_text_spans.push_back(ImeTextSpan(
         0, length, SK_ColorBLACK, false /* thick */, SK_ColorTRANSPARENT));
   }
 }
diff --git a/ui/base/ime/win/imm32_manager.cc b/ui/base/ime/win/imm32_manager.cc
index 8b868ce5..bc444b7 100644
--- a/ui/base/ime/win/imm32_manager.cc
+++ b/ui/base/ime/win/imm32_manager.cc
@@ -62,10 +62,10 @@
 
 // Helper function for IMM32Manager::GetCompositionInfo() method, to get
 // underlines information of the current composition string.
-void GetCompositionUnderlines(HIMC imm_context,
-                              int target_start,
-                              int target_end,
-                              ui::CompositionUnderlines* underlines) {
+void GetImeTextSpans(HIMC imm_context,
+                     int target_start,
+                     int target_end,
+                     ui::ImeTextSpans* ime_text_spans) {
   int clause_size = ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE,
                                               NULL, 0);
   int clause_length = clause_size / sizeof(uint32_t);
@@ -75,19 +75,19 @@
       ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE,
                                 clause_data.get(), clause_size);
       for (int i = 0; i < clause_length - 1; ++i) {
-        ui::CompositionUnderline underline;
-        underline.start_offset = clause_data[i];
-        underline.end_offset = clause_data[i+1];
-        underline.color = SK_ColorBLACK;
-        underline.thick = false;
-        underline.background_color = SK_ColorTRANSPARENT;
+        ui::ImeTextSpan ime_text_span;
+        ime_text_span.start_offset = clause_data[i];
+        ime_text_span.end_offset = clause_data[i + 1];
+        ime_text_span.color = SK_ColorBLACK;
+        ime_text_span.thick = false;
+        ime_text_span.background_color = SK_ColorTRANSPARENT;
 
         // Use thick underline for the target clause.
-        if (underline.start_offset >= static_cast<uint32_t>(target_start) &&
-            underline.end_offset <= static_cast<uint32_t>(target_end)) {
-          underline.thick = true;
+        if (ime_text_span.start_offset >= static_cast<uint32_t>(target_start) &&
+            ime_text_span.end_offset <= static_cast<uint32_t>(target_end)) {
+          ime_text_span.thick = true;
         }
-        underlines->push_back(underline);
+        ime_text_spans->push_back(ime_text_span);
       }
     }
   }
@@ -294,8 +294,8 @@
                                       LPARAM lparam,
                                       CompositionText* composition) {
   // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and
-  // convert them into underlines and selection range respectively.
-  composition->underlines.clear();
+  // convert them into composition underlines and selection range respectively.
+  composition->ime_text_spans.clear();
 
   int length = static_cast<int>(composition->text.length());
 
@@ -320,36 +320,36 @@
     composition->selection = gfx::Range(0);
   }
 
-  // Retrieve the clause segmentations and convert them to underlines.
+  // Retrieve the clause segmentations and convert them to ime_text_spans.
   if (lparam & GCS_COMPCLAUSE) {
-    GetCompositionUnderlines(imm_context, target_start, target_end,
-                             &composition->underlines);
+    GetImeTextSpans(imm_context, target_start, target_end,
+                    &composition->ime_text_spans);
   }
 
-  // Set default underlines in case there is no clause information.
-  if (!composition->underlines.empty())
+  // Set default composition underlines in case there is no clause information.
+  if (!composition->ime_text_spans.empty())
     return;
 
-  CompositionUnderline underline;
-  underline.color = SK_ColorBLACK;
-  underline.background_color = SK_ColorTRANSPARENT;
+  ImeTextSpan ime_text_span;
+  ime_text_span.color = SK_ColorBLACK;
+  ime_text_span.background_color = SK_ColorTRANSPARENT;
   if (target_start > 0) {
-    underline.start_offset = 0U;
-    underline.end_offset = static_cast<uint32_t>(target_start);
-    underline.thick = false;
-    composition->underlines.push_back(underline);
+    ime_text_span.start_offset = 0U;
+    ime_text_span.end_offset = static_cast<uint32_t>(target_start);
+    ime_text_span.thick = false;
+    composition->ime_text_spans.push_back(ime_text_span);
   }
   if (target_end > target_start) {
-    underline.start_offset = static_cast<uint32_t>(target_start);
-    underline.end_offset = static_cast<uint32_t>(target_end);
-    underline.thick = true;
-    composition->underlines.push_back(underline);
+    ime_text_span.start_offset = static_cast<uint32_t>(target_start);
+    ime_text_span.end_offset = static_cast<uint32_t>(target_end);
+    ime_text_span.thick = true;
+    composition->ime_text_spans.push_back(ime_text_span);
   }
   if (target_end < length) {
-    underline.start_offset = static_cast<uint32_t>(target_end);
-    underline.end_offset = static_cast<uint32_t>(length);
-    underline.thick = false;
-    composition->underlines.push_back(underline);
+    ime_text_span.start_offset = static_cast<uint32_t>(target_end);
+    ime_text_span.end_offset = static_cast<uint32_t>(length);
+    ime_text_span.thick = false;
+    composition->ime_text_spans.push_back(ime_text_span);
   }
 }
 
@@ -403,7 +403,7 @@
         composition->text[0] = 0xFF3F;
       }
 
-      // Retrieve the composition underlines and selection range information.
+      // Retrieve the IME text spans and selection range information.
       GetCompositionInfo(imm_context, lparam, composition);
 
       // Mark that there is an ongoing composition.
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_avocado.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_avocado.png
new file mode 100644
index 0000000..d36e36b
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_avocado.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_cappucino.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_cappucino.png
new file mode 100644
index 0000000..b2aef62
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_cappucino.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_icecream.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_icecream.png
new file mode 100644
index 0000000..247f4bd98
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_icecream.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_icewater.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_icewater.png
new file mode 100644
index 0000000..b5427c3
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_icewater.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_melon.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_melon.png
new file mode 100644
index 0000000..d3a8248
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_melon.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_onigiri.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_onigiri.png
new file mode 100644
index 0000000..f2881146
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_onigiri.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_pizza.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_pizza.png
new file mode 100644
index 0000000..d533653
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_pizza.png
Binary files differ
diff --git a/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_sandwich.png b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_sandwich.png
new file mode 100644
index 0000000..bbce58db
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/default_user_images/abstract/avatar_sandwich.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_avocado.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_avocado.png
new file mode 100644
index 0000000..036fc9b0
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_avocado.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_cappucino.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_cappucino.png
new file mode 100644
index 0000000..8027ad4
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_cappucino.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_icecream.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_icecream.png
new file mode 100644
index 0000000..2aef7b30
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_icecream.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_icewater.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_icewater.png
new file mode 100644
index 0000000..9a38a16
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_icewater.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_melon.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_melon.png
new file mode 100644
index 0000000..180155f
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_melon.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_onigiri.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_onigiri.png
new file mode 100644
index 0000000..51b3b75
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_onigiri.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_pizza.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_pizza.png
new file mode 100644
index 0000000..c5c8dd07
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_pizza.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_sandwich.png b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_sandwich.png
new file mode 100644
index 0000000..dfbfd17
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/default_user_images/abstract/avatar_sandwich.png
Binary files differ
diff --git a/ui/chromeos/resources/ui_chromeos_resources.grd b/ui/chromeos/resources/ui_chromeos_resources.grd
index d458dea..569f59d1 100644
--- a/ui/chromeos/resources/ui_chromeos_resources.grd
+++ b/ui/chromeos/resources/ui_chromeos_resources.grd
@@ -78,6 +78,14 @@
       <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_55" file="default_user_images/illustration/avatar_sushi.png" />
       <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_56" file="default_user_images/illustration/avatar_bike.png" />
       <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_57" file="default_user_images/illustration/avatar_sunglasses.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_58" file="default_user_images/abstract/avatar_pizza.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_59" file="default_user_images/abstract/avatar_sandwich.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_60" file="default_user_images/abstract/avatar_cappucino.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_61" file="default_user_images/abstract/avatar_icewater.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_62" file="default_user_images/abstract/avatar_icecream.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_63" file="default_user_images/abstract/avatar_onigiri.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_64" file="default_user_images/abstract/avatar_melon.png" />
+      <structure type="chrome_scaled_image" name="IDR_LOGIN_DEFAULT_USER_65" file="default_user_images/abstract/avatar_avocado.png" />
 
       <!-- Print Job Notification images. -->
       <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_CANCEL" file="print_notification/cancel.png" />
diff --git a/ui/chromeos/ui_chromeos_strings.grd b/ui/chromeos/ui_chromeos_strings.grd
index 1ff7211..b1a2072 100644
--- a/ui/chromeos/ui_chromeos_strings.grd
+++ b/ui/chromeos/ui_chromeos_strings.grd
@@ -239,6 +239,30 @@
       <message name="IDS_LOGIN_DEFAULT_USER_DESC_57" desc="Description of the default user icon with a picture of a pair of sunglasses">
         Sunglasses
       </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_58" desc="Description of the default user icon with a picture of a pizza">
+        Pizza
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_59" desc="Description of the default user icon with a picture of a sandwich">
+        Sandwich
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_60" desc="Description of the default user icon with a picture of a cappocino">
+        Cappocino
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_61" desc="Description of the default user icon with a picture of an ice water">
+        Ice water
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_62" desc="Description of the default user icon with a picture of an ice cream">
+        Ice cream
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_63" desc="Description of the default user icon with a picture of onigiri">
+        Onigiri
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_64" desc="Description of the default user icon with a picture of a melon">
+        Melon
+      </message>
+      <message name="IDS_LOGIN_DEFAULT_USER_DESC_65" desc="Description of the default user icon with a picture of an avocado">
+        Avocado
+      </message>
 
       <!-- Attribution strings for stock user images, not translateable. -->
       <message translateable="false" name="IDS_LOGIN_DEFAULT_USER_AUTHOR">
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
index 8ec761f..dcede94 100644
--- a/ui/display/BUILD.gn
+++ b/ui/display/BUILD.gn
@@ -76,10 +76,6 @@
       "CoreGraphics.framework",
     ]
   }
-
-  if (is_fuchsia) {
-    sources += [ "screen_fuchsia.cc" ]
-  }
 }
 
 component("display_manager_test_api") {
diff --git a/ui/display/screen_fuchsia.cc b/ui/display/screen_fuchsia.cc
deleted file mode 100644
index e8f2cb8..0000000
--- a/ui/display/screen_fuchsia.cc
+++ /dev/null
@@ -1,18 +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 "ui/display/screen.h"
-
-#include "base/logging.h"
-
-namespace display {
-
-// static
-gfx::NativeWindow Screen::GetWindowForView(gfx::NativeView view) {
-  // TODO(fuchsia): Stubbed during headless bringup https://crbug.com/743296.
-  NOTREACHED();
-  return nullptr;
-}
-
-}  // namespace display
diff --git a/ui/login/account_picker/md_user_pod_row.js b/ui/login/account_picker/md_user_pod_row.js
index dae3ddd1c..b2606f7f 100644
--- a/ui/login/account_picker/md_user_pod_row.js
+++ b/ui/login/account_picker/md_user_pod_row.js
@@ -2915,6 +2915,8 @@
     // If testing mode is enabled.
     testingModeEnabled_: false,
 
+    // The color used by the scroll list when the user count exceeds
+    // LANDSCAPE_MODE_LIMIT or PORTRAIT_MODE_LIMIT.
     overlayColors_: {maskColor: undefined, scrollColor: undefined},
 
     /** @override */
@@ -3577,7 +3579,15 @@
      * screen orientation and showing the virtual keyboard.
      */
     onWindowResize: function() {
-      this.placePods_();
+      var isAccountPicker =
+          $('login-header-bar').signinUIState == SIGNIN_UI_STATE.ACCOUNT_PICKER;
+      if (isAccountPicker) {
+        // Redo pod placement if account picker is the current screen.
+        this.placePods_();
+      } else {
+        // Postpone pod placement. |handleBeforeShow| will check this flag.
+        this.podPlacementPostponed_ = true;
+      }
     },
 
     /**
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index 1dcda53..87e40cc 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -105,10 +105,6 @@
   // A lock screen enabled app is running in foreground - an app window is
   // shown over the lock screen user pods (header bar should still be visible).
   FOREGROUND: 'LOCK_SCREEN_APPS_STATE.FOREGROUND',
-  // State used to request launch of a lock screen note-taking app - this state
-  // is not expected to be received from platform as a current state (instead,
-  // on successful launch, state will be changed to FOREGROUND).
-  LAUNCH_REQUESTED: 'LOCK_SCREEN_APPS_STATE.LAUNCH_REQUESTED'
 };
 
 /** @const */ var USER_ACTION_ROLLBACK_TOGGLED = 'rollback-toggled';
@@ -809,6 +805,9 @@
       var currentScreen = $(currentScreenId);
       if (currentScreen)
         currentScreen.onWindowResize();
+      // The account picker always needs to be notified of window size changes.
+      if (currentScreenId != SCREEN_ACCOUNT_PICKER && $(SCREEN_ACCOUNT_PICKER))
+        $(SCREEN_ACCOUNT_PICKER).onWindowResize();
     },
 
     /*
diff --git a/ui/views/animation/OWNERS b/ui/views/animation/OWNERS
index 9710b4e0..be29560 100644
--- a/ui/views/animation/OWNERS
+++ b/ui/views/animation/OWNERS
@@ -1 +1 @@
-per-file *ink*=bruthig@chromium.org
+per-file *ink*=mohsen@chromium.org
diff --git a/ui/views/animation/test/OWNERS b/ui/views/animation/test/OWNERS
index 9710b4e0..be29560 100644
--- a/ui/views/animation/test/OWNERS
+++ b/ui/views/animation/test/OWNERS
@@ -1 +1 @@
-per-file *ink*=bruthig@chromium.org
+per-file *ink*=mohsen@chromium.org
diff --git a/ui/views/bubble/bubble_dialog_delegate_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_unittest.cc
index 0887c89..a6c2b175 100644
--- a/ui/views/bubble/bubble_dialog_delegate_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_unittest.cc
@@ -13,6 +13,7 @@
 #include "ui/events/event_utils.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/styled_label.h"
 #include "ui/views/test/test_views.h"
 #include "ui/views/test/test_widget_observer.h"
 #include "ui/views/test/views_test_base.h"
@@ -58,6 +59,7 @@
 
   using BubbleDialogDelegateView::SetAnchorRect;
   using BubbleDialogDelegateView::GetBubbleFrameView;
+  using BubbleDialogDelegateView::SizeToContents;
 
  private:
   View* view_;
@@ -384,4 +386,37 @@
             title_view->bounds().right());
 }
 
+// Ensure the BubbleFrameView correctly resizes when the title is provided by a
+// StyledLabel.
+TEST_F(BubbleDialogDelegateTest, StyledLabelTitle) {
+  std::unique_ptr<Widget> anchor_widget(CreateTestWidget());
+  TestBubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
+  StyledLabel* title_view = new StyledLabel(base::ASCIIToUTF16("123"), nullptr);
+  bubble_delegate->set_title_view(title_view);
+
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+  bubble_widget->Show();
+
+  const gfx::Size size_before_new_title =
+      bubble_widget->GetWindowBoundsInScreen().size();
+  title_view->SetText(base::ASCIIToUTF16("12"));
+  bubble_delegate->SizeToContents();
+
+  // A shorter title should change nothing, since both will be within the
+  // minimum dialog width.
+  EXPECT_EQ(size_before_new_title,
+            bubble_widget->GetWindowBoundsInScreen().size());
+
+  title_view->SetText(base::UTF8ToUTF16(std::string(200, '0')));
+  bubble_delegate->SizeToContents();
+
+  // A (much) longer title should increase the height, but not the width.
+  EXPECT_EQ(size_before_new_title.width(),
+            bubble_widget->GetWindowBoundsInScreen().width());
+  EXPECT_LT(size_before_new_title.height(),
+            bubble_widget->GetWindowBoundsInScreen().height());
+}
+
 }  // namespace views
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 33849cd..a7b5468 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -159,8 +159,10 @@
 }
 
 gfx::Rect BubbleFrameView::GetBoundsForClientView() const {
+  // When NonClientView asks for this, the size of the frame view has been set
+  // (i.e. |this|), but not the client view bounds.
   gfx::Rect client_bounds = GetContentsBounds();
-  client_bounds.Inset(GetInsets());
+  client_bounds.Inset(GetClientInsetsForFrameWidth(client_bounds.width()));
   // Only account for footnote_container_'s height if it's visible, because
   // content_margins_ adds extra padding even if all child views are invisible.
   if (footnote_container_ && footnote_container_->visible()) {
@@ -172,12 +174,17 @@
 
 gfx::Rect BubbleFrameView::GetWindowBoundsForClientBounds(
     const gfx::Rect& client_bounds) const {
-  gfx::Size size(GetSizeForClientSize(client_bounds.size()));
+  gfx::Size size(GetFrameSizeForClientSize(client_bounds.size()));
   return bubble_border_->GetBounds(gfx::Rect(), size);
 }
 
 bool BubbleFrameView::GetClientMask(const gfx::Size& size,
                                     gfx::Path* path) const {
+  // NonClientView calls this after setting the client view size from the return
+  // of GetBoundsForClientView(); feeding it back in |size|.
+  DCHECK(GetBoundsForClientView().size() == size);
+  DCHECK(GetWidget()->client_view()->size() == size);
+
   const int radius = bubble_border_->GetBorderCornerRadius();
   gfx::Insets content_insets = GetInsets();
   // If the client bounds don't touch the edges, no need to mask.
@@ -288,20 +295,7 @@
 }
 
 gfx::Insets BubbleFrameView::GetInsets() const {
-  gfx::Insets insets = content_margins_;
-
-  const int icon_height = title_icon_->GetPreferredSize().height();
-  const int label_height = title()->GetPreferredSize().height();
-  const bool has_title = icon_height > 0 || label_height > 0;
-  const int title_padding = has_title ? title_margins_.height() : 0;
-  const int title_height = std::max(icon_height, label_height) + title_padding;
-  const int close_height =
-      GetWidget()->widget_delegate()->ShouldShowCloseButton()
-          ? close_->height() + LayoutProvider::Get()->GetDistanceMetric(
-                                   DISTANCE_CLOSE_BUTTON_MARGIN)
-          : 0;
-  insets += gfx::Insets(std::max(title_height, close_height), 0, 0, 0);
-  return insets;
+  return GetClientInsetsForFrameWidth(GetContentsBounds().width());
 }
 
 gfx::Size BubbleFrameView::CalculatePreferredSize() const {
@@ -369,6 +363,17 @@
       title_icon_pref_size.width() > 0 ? title_margins_.left() : 0;
   const int title_label_x =
       bounds.x() + title_icon_pref_size.width() + title_icon_padding;
+
+  // TODO(tapted): Layout() should skip more surrounding code when !HasTitle().
+  // Currently DCHECKs fail since title_insets is 0 when there is no title.
+  if (DCHECK_IS_ON() && HasTitle()) {
+    gfx::Insets title_insets = GetTitleLabelInsetsFromFrame();
+    if (border())
+      title_insets += border()->GetInsets();
+    DCHECK_EQ(title_insets.left(), title_label_x);
+    DCHECK_EQ(title_insets.right(), width() - title_label_right);
+  }
+
   const int title_available_width =
       std::max(1, title_label_right - title_label_x);
   const int title_preferred_height =
@@ -470,7 +475,7 @@
 gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
                                                   const gfx::Size& client_size,
                                                   bool adjust_if_offscreen) {
-  gfx::Size size(GetSizeForClientSize(client_size));
+  gfx::Size size(GetFrameSizeForClientSize(client_size));
 
   const BubbleBorder::Arrow arrow = bubble_border_->arrow();
   if (adjust_if_offscreen && BubbleBorder::has_arrow(arrow)) {
@@ -572,34 +577,79 @@
     SchedulePaint();
 }
 
-gfx::Size BubbleFrameView::GetSizeForClientSize(
-    const gfx::Size& client_size) const {
-  // Accommodate the width of the title bar elements.
-  int title_bar_width = title_margins_.width() + border()->GetInsets().width();
-  gfx::Size title_icon_size = title_icon_->GetPreferredSize();
-  gfx::Size title_label_size = title()->GetPreferredSize();
-  if (title_icon_size.width() > 0 && title_label_size.width() > 0)
-    title_bar_width += title_margins_.left();
-  title_bar_width += title_icon_size.width();
-  if (close_->visible())
-    title_bar_width += close_->width() + 1;
+int BubbleFrameView::GetFrameWidthForClientWidth(int client_width) const {
+  // Note that GetMinimumSize() for multiline Labels is typically 0.
+  const int title_bar_width = title()->GetMinimumSize().width() +
+                              GetTitleLabelInsetsFromFrame().width();
+  const int client_area_width = client_width + content_margins_.width();
+  const int frame_width = std::max(title_bar_width, client_area_width);
+  DialogDelegate* dialog_delegate =
+      GetWidget()->widget_delegate()->AsDialogDelegate();
+  return dialog_delegate && dialog_delegate->ShouldSnapFrameWidth()
+             ? LayoutProvider::Get()->GetSnappedDialogWidth(frame_width)
+             : frame_width;
+}
 
-  gfx::Size size(client_size);
-  gfx::Insets client_insets = GetInsets();
-  size.Enlarge(client_insets.width(), client_insets.height());
-  size.SetToMax(gfx::Size(title_bar_width, 0));
+gfx::Size BubbleFrameView::GetFrameSizeForClientSize(
+    const gfx::Size& client_size) const {
+  const int frame_width = GetFrameWidthForClientWidth(client_size.width());
+  const gfx::Insets client_insets = GetClientInsetsForFrameWidth(frame_width);
+  DCHECK_GE(frame_width, client_size.width());
+  gfx::Size size(frame_width, client_size.height() + client_insets.height());
 
   // Only account for footnote_container_'s height if it's visible, because
   // content_margins_ adds extra padding even if all child views are invisible.
   if (footnote_container_ && footnote_container_->visible())
     size.Enlarge(0, footnote_container_->GetHeightForWidth(size.width()));
 
-  DialogDelegate* dialog_delegate =
-      GetWidget()->widget_delegate()->AsDialogDelegate();
-  if (dialog_delegate && dialog_delegate->ShouldSnapFrameWidth())
-    size.set_width(LayoutProvider::Get()->GetSnappedDialogWidth(size.width()));
-
   return size;
 }
 
+bool BubbleFrameView::HasTitle() const {
+  return custom_title_ != nullptr ||
+         default_title_->GetPreferredSize().height() > 0 ||
+         title_icon_->GetPreferredSize().height() > 0;
+}
+
+gfx::Insets BubbleFrameView::GetTitleLabelInsetsFromFrame() const {
+  int insets_right = 0;
+  if (GetWidget()->widget_delegate()->ShouldShowCloseButton()) {
+    const int close_margin =
+        LayoutProvider::Get()->GetDistanceMetric(DISTANCE_CLOSE_BUTTON_MARGIN);
+    insets_right = 2 * close_margin + close_->width();
+  }
+  if (!HasTitle())
+    return gfx::Insets(0, 0, 0, insets_right);
+
+  insets_right = std::max(insets_right, title_margins_.right());
+  const gfx::Size title_icon_pref_size = title_icon_->GetPreferredSize();
+  const int title_icon_padding =
+      title_icon_pref_size.width() > 0 ? title_margins_.left() : 0;
+  const int insets_left =
+      title_margins_.left() + title_icon_pref_size.width() + title_icon_padding;
+  return gfx::Insets(title_margins_.top(), insets_left, title_margins_.bottom(),
+                     insets_right);
+}
+
+gfx::Insets BubbleFrameView::GetClientInsetsForFrameWidth(
+    int frame_width) const {
+  int close_height = 0;
+  if (GetWidget()->widget_delegate()->ShouldShowCloseButton()) {
+    const int close_margin =
+        LayoutProvider::Get()->GetDistanceMetric(DISTANCE_CLOSE_BUTTON_MARGIN);
+    // Note: |close_margin| is not applied on the bottom of the icon.
+    close_height = close_margin + close_->height();
+  }
+  if (!HasTitle())
+    return content_margins_ + gfx::Insets(close_height, 0, 0, 0);
+
+  const int icon_height = title_icon_->GetPreferredSize().height();
+  const int label_height = title()->GetHeightForWidth(
+      frame_width - GetTitleLabelInsetsFromFrame().width());
+  const int title_height =
+      std::max(icon_height, label_height) + title_margins_.height();
+  return content_margins_ +
+         gfx::Insets(std::max(title_height, close_height), 0, 0, 0);
+}
+
 }  // namespace views
diff --git a/ui/views/bubble/bubble_frame_view.h b/ui/views/bubble/bubble_frame_view.h
index 37d2fddf..b456f3c 100644
--- a/ui/views/bubble/bubble_frame_view.h
+++ b/ui/views/bubble/bubble_frame_view.h
@@ -123,8 +123,26 @@
   void OffsetArrowIfOffScreen(const gfx::Rect& anchor_rect,
                               const gfx::Size& client_size);
 
+  // The width of the frame for the given |client_width|. The result accounts
+  // for the minimum title bar width and includes all insets and possible
+  // snapping. It does not include the border.
+  int GetFrameWidthForClientWidth(int client_width) const;
+
   // Calculates the size needed to accommodate the given client area.
-  gfx::Size GetSizeForClientSize(const gfx::Size& client_size) const;
+  gfx::Size GetFrameSizeForClientSize(const gfx::Size& client_size) const;
+
+  // True if the frame has a title area. This is the area affected by
+  // |title_margins_|, including the icon and title text, but not the close
+  // button.
+  bool HasTitle() const;
+
+  // The insets of the text portion of the title, based on |title_margins_| and
+  // whether there is an icon and/or close button. Note there may be no title,
+  // in which case only insets required for the close button are returned.
+  gfx::Insets GetTitleLabelInsetsFromFrame() const;
+
+  // The client_view insets (from the frame view) for the given |frame_width|.
+  gfx::Insets GetClientInsetsForFrameWidth(int frame_width) const;
 
   // The bubble border.
   BubbleBorder* bubble_border_;
diff --git a/ui/views/bubble/bubble_frame_view_unittest.cc b/ui/views/bubble/bubble_frame_view_unittest.cc
index 61066f0..92072dfe 100644
--- a/ui/views/bubble/bubble_frame_view_unittest.cc
+++ b/ui/views/bubble/bubble_frame_view_unittest.cc
@@ -552,11 +552,23 @@
   }
   ~TestBubbleDialogDelegateView() override {}
 
-  using BubbleDialogDelegateView::SetAnchorView;
+  void ChangeTitle(const base::string16& title) {
+    title_ = title;
+    // Note UpdateWindowTitle() always does a layout, which will be invalid if
+    // the Widget needs to change size. But also SizeToContents() _only_ does a
+    // layout if the size is actually changing.
+    GetWidget()->UpdateWindowTitle();
+    SizeToContents();
+  }
 
   void set_override_snap(bool value) { override_snap_ = value; }
 
   // BubbleDialogDelegateView:
+  using BubbleDialogDelegateView::SetAnchorView;
+  using BubbleDialogDelegateView::SizeToContents;
+  base::string16 GetWindowTitle() const override { return title_; }
+  bool ShouldShowWindowTitle() const override { return !title_.empty(); }
+
   void DeleteDelegate() override {
     // This delegate is owned by the test case itself, so it should not delete
     // itself here.
@@ -566,16 +578,34 @@
     return override_snap_.value_or(
         BubbleDialogDelegateView::ShouldSnapFrameWidth());
   }
+  gfx::Size GetMinimumSize() const override { return gfx::Size(); }
   gfx::Size CalculatePreferredSize() const override {
     return gfx::Size(200, 200);
   }
 
  private:
+  base::string16 title_;
   base::Optional<bool> override_snap_;
 
   DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView);
 };
 
+class TestAnchor {
+ public:
+  explicit TestAnchor(Widget::InitParams params) {
+    params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    widget_.Init(params);
+    widget_.Show();
+  }
+
+  Widget& widget() { return widget_; }
+
+ private:
+  Widget widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAnchor);
+};
+
 }  // namespace
 
 // This test ensures that if the installed LayoutProvider snaps dialog widths,
@@ -583,14 +613,9 @@
 TEST_F(BubbleFrameViewTest, WidthSnaps) {
   test::TestLayoutProvider provider;
   TestBubbleDialogDelegateView delegate;
+  TestAnchor anchor(CreateParams(Widget::InitParams::TYPE_WINDOW));
 
-  Widget anchor;
-  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
-  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  anchor.Init(params);
-  anchor.Show();
-
-  delegate.SetAnchorView(anchor.GetContentsView());
+  delegate.SetAnchorView(anchor.widget().GetContentsView());
   delegate.set_margins(gfx::Insets());
 
   Widget* w0 = BubbleDialogDelegateView::CreateBubble(&delegate);
@@ -618,4 +643,83 @@
   w2->CloseNow();
 }
 
+// Tests edge cases when the frame's title view starts to wrap text. This is to
+// ensure that the calculations BubbleFrameView does to determine the Widget
+// size for a given client view are consistent with the eventual size that the
+// client view takes after Layout().
+TEST_F(BubbleFrameViewTest, LayoutEdgeCases) {
+  test::TestLayoutProvider provider;
+  TestBubbleDialogDelegateView delegate;
+  TestAnchor anchor(CreateParams(Widget::InitParams::TYPE_WINDOW));
+  delegate.SetAnchorView(anchor.widget().GetContentsView());
+
+  Widget* bubble = BubbleDialogDelegateView::CreateBubble(&delegate);
+  bubble->Show();
+
+  // Even though the bubble has default margins, the dialog view should have
+  // been given its preferred size.
+  EXPECT_FALSE(delegate.margins().IsEmpty());
+  EXPECT_EQ(delegate.size(), delegate.GetPreferredSize());
+
+  // Starting with a short title.
+  base::string16 title(1, 'i');
+  delegate.ChangeTitle(title);
+  const int min_bubble_height = bubble->GetWindowBoundsInScreen().height();
+  EXPECT_LT(delegate.GetPreferredSize().height(), min_bubble_height);
+
+  // Grow the title incrementally until word wrap is required. There should
+  // never be a point where the BubbleFrameView over- or under-estimates the
+  // size required for the title. If it did, it would cause SizeToContents() to
+  // Widget size requiring the subsequent Layout() to fill the remaining client
+  // area with something other than |delegate|'s preferred size.
+  while (bubble->GetWindowBoundsInScreen().height() == min_bubble_height) {
+    title += ' ';
+    title += 'i';
+    delegate.ChangeTitle(title);
+    EXPECT_EQ(delegate.GetPreferredSize(), delegate.size()) << title;
+  }
+
+  // Sanity check that something interesting happened. The bubble should have
+  // grown by "a line" for the wrapped title, and the title should have reached
+  // a length that would have likely caused word wrap. A typical result would be
+  // a +17-20 change in height and title length of 53 characters.
+  const int two_line_height = bubble->GetWindowBoundsInScreen().height();
+  EXPECT_LT(12, two_line_height - min_bubble_height);
+  EXPECT_GT(25, two_line_height - min_bubble_height);
+  EXPECT_LT(30u, title.size());
+  EXPECT_GT(80u, title.size());
+
+  // Now add dialog snapping.
+  provider.SetSnappedDialogWidth(300);
+  delegate.SizeToContents();
+
+  // Height should go back to |min_bubble_height| since the window is wider:
+  // word wrapping should no longer happen.
+  EXPECT_EQ(min_bubble_height, bubble->GetWindowBoundsInScreen().height());
+  EXPECT_EQ(300, bubble->GetWindowBoundsInScreen().width());
+
+  // Now we are allowed to diverge from the client view width, but not height.
+  EXPECT_EQ(delegate.GetPreferredSize().height(), delegate.height());
+  EXPECT_LT(delegate.GetPreferredSize().width(), delegate.width());
+  EXPECT_GT(300, delegate.width());  // Greater, since there are margins.
+
+  const gfx::Size snapped_size = delegate.size();
+  const size_t old_title_size = title.size();
+
+  // Grow the title again with width snapping until word wrapping occurs.
+  while (bubble->GetWindowBoundsInScreen().height() == min_bubble_height) {
+    title += ' ';
+    title += 'i';
+    delegate.ChangeTitle(title);
+    EXPECT_EQ(snapped_size, delegate.size()) << title;
+  }
+  // Change to the height should have been the same as before. Title should
+  // have grown about 50%.
+  EXPECT_EQ(two_line_height, bubble->GetWindowBoundsInScreen().height());
+  EXPECT_LT(15u, title.size() - old_title_size);
+  EXPECT_GT(40u, title.size() - old_title_size);
+
+  // When |anchor| goes out of scope it should take |bubble| with it.
+}
+
 }  // namespace views
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm
index e300b7e..6d05846 100644
--- a/ui/views/cocoa/bridged_content_view.mm
+++ b/ui/views/cocoa/bridged_content_view.mm
@@ -1449,7 +1449,7 @@
   // the Chrome renderer. Add code to extract underlines from |text| once our
   // render text implementation supports thick underlines and discontinous
   // underlines for consecutive characters. See http://crbug.com/612675.
-  composition.underlines.push_back(ui::CompositionUnderline(
+  composition.ime_text_spans.push_back(ui::ImeTextSpan(
       0, [text length], SK_ColorBLACK, false, SK_ColorTRANSPARENT));
   textInputClient_->SetCompositionText(composition);
   hasUnhandledKeyDownEvent_ = NO;
diff --git a/ui/views/controls/textfield/textfield_model.cc b/ui/views/controls/textfield/textfield_model.cc
index f4d2282..58d466b 100644
--- a/ui/views/controls/textfield/textfield_model.cc
+++ b/ui/views/controls/textfield/textfield_model.cc
@@ -256,8 +256,8 @@
 // representing the target clause (on Windows). Returns an invalid range if
 // there is no such a range.
 gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) {
-  for (size_t i = 0; i < composition.underlines.size(); ++i) {
-    const ui::CompositionUnderline& underline = composition.underlines[i];
+  for (size_t i = 0; i < composition.ime_text_spans.size(); ++i) {
+    const ui::ImeTextSpan& underline = composition.ime_text_spans[i];
     if (underline.thick)
       return gfx::Range(underline.start_offset, underline.end_offset);
   }
@@ -659,8 +659,9 @@
   base::string16 new_text = text();
   render_text_->SetText(new_text.insert(cursor, composition.text));
   composition_range_ = gfx::Range(cursor, cursor + composition.text.length());
-  // Don't render transparent composition underlines.
-  if (composition.underlines.size() > 0 && composition.underlines[0].color != 0)
+  // Don't render transparent IME spans.
+  if (composition.ime_text_spans.size() > 0 &&
+      composition.ime_text_spans[0].color != 0)
     render_text_->SetCompositionRange(composition_range_);
   else
     render_text_->SetCompositionRange(gfx::Range::InvalidRange());
diff --git a/ui/views/controls/textfield/textfield_model_unittest.cc b/ui/views/controls/textfield/textfield_model_unittest.cc
index d3722bb..a28fb1e 100644
--- a/ui/views/controls/textfield/textfield_model_unittest.cc
+++ b/ui/views/controls/textfield/textfield_model_unittest.cc
@@ -867,7 +867,7 @@
 
   ui::CompositionText composition;
   composition.text = base::ASCIIToUTF16("678");
-  composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false));
+  composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 3, 0, false));
 
   // Cursor should be at the end of composition when characters are just typed.
   composition.selection = gfx::Range(3, 3);
@@ -881,15 +881,15 @@
 
   // Restart composition with targeting "67" in "678".
   composition.selection = gfx::Range(1, 3);
-  composition.underlines.clear();
-  composition.underlines.push_back(ui::CompositionUnderline(0, 2, 0, true));
-  composition.underlines.push_back(ui::CompositionUnderline(2, 3, 0, false));
+  composition.ime_text_spans.clear();
+  composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 2, 0, true));
+  composition.ime_text_spans.push_back(ui::ImeTextSpan(2, 3, 0, false));
   model.SetCompositionText(composition);
   EXPECT_TRUE(model.HasCompositionText());
   EXPECT_TRUE(model.HasSelection());
 #if !defined(OS_CHROMEOS)
   // |composition.selection| is ignored because SetCompositionText checks
-  // if a bold underline exists first.
+  // if a thick underline exists first.
   EXPECT_EQ(gfx::Range(5, 7), model.render_text()->selection());
   EXPECT_EQ(7U, model.render_text()->cursor_position());
 #else
@@ -922,9 +922,9 @@
   composition_text_confirmed_or_cleared_ = false;
   model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE);
 
-  // Also test the case where a selection exists but a bold underline doesn't.
+  // Also test the case where a selection exists but a thick underline doesn't.
   composition.selection = gfx::Range(0, 1);
-  composition.underlines.clear();
+  composition.ime_text_spans.clear();
   model.SetCompositionText(composition);
   EXPECT_STR_EQ("1234567890678", model.text());
   EXPECT_TRUE(model.HasSelection());
@@ -1475,7 +1475,7 @@
 
   ui::CompositionText composition;
   composition.text = base::ASCIIToUTF16("abc");
-  composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false));
+  composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 3, 0, false));
   composition.selection = gfx::Range(2, 3);
 
   model.SetText(base::ASCIIToUTF16("ABCDE"));
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 72cdf76..44d60e4 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -908,10 +908,10 @@
       // Transform |clip_path_| from local space to parent recording space.
       gfx::Transform to_parent_recording_space;
 
+      to_parent_recording_space.Translate(paint_info.offset_from_parent());
       to_parent_recording_space.Scale(
           SkFloatToScalar(paint_info.paint_recording_scale_x()),
           SkFloatToScalar(paint_info.paint_recording_scale_y()));
-      to_parent_recording_space.Translate(paint_info.offset_from_parent());
 
       clip_path_in_parent.transform(to_parent_recording_space.matrix());
       clip_recorder.ClipPathWithAntiAliasing(clip_path_in_parent);
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
index 0d06a9e..40fe593 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
@@ -13,7 +13,7 @@
  * Dimensions for camera capture.
  * @const
  */
-var CAPTURE_SIZE = {height: 480, width: 480};
+var CAPTURE_SIZE = {height: 384, width: 384};
 
 Polymer({
   is: 'cr-camera',
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
index 131d207..6765125 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
@@ -13,7 +13,17 @@
       }
 
       cr-camera {
-        --cr-camera-image-size: var(--cr-picture-image-size, 228px);
+        --cr-camera-image-size: 288px;
+      }
+
+      #preview {
+        bottom: 0;
+        height: var(--cr-picture-image-size, 228px);
+        left: 0;
+        margin: auto;
+        position: absolute;
+        right: 0;
+        top: 0;
       }
 
       img {
@@ -43,18 +53,21 @@
 
       #discard button {
         --iron-icon-fill-color: white;
+        -webkit-margin-start: 0;
         border-radius: 50%;
       }
     </style>
     <template is="dom-if"
         if="[[showImagePreview_(cameraActive_, imageSrc)]]">
-      <img alt="[[previewAltText]]" src="[[getImgSrc_(imageSrc)]]"
-          data-show-discard$="[[showDiscard_(imageType)]]">
-      <div id="discard" hidden="[[!showDiscard_(imageType)]]">
-        <button is="paper-icon-button-light" id="discardImage"
-            class="icon-delete-white" title="[[discardImageLabel]]"
-            on-tap="onTapDiscardImage_">
-        </button>
+      <div id="preview">
+        <img alt="[[previewAltText]]" src="[[getImgSrc_(imageSrc)]]"
+            data-show-discard$="[[showDiscard_(imageType)]]">
+        <div id="discard" hidden="[[!showDiscard_(imageType)]]">
+          <button is="paper-icon-button-light" id="discardImage"
+              class="icon-delete-white" title="[[discardImageLabel]]"
+              on-tap="onTapDiscardImage_">
+          </button>
+        </div>
       </div>
     </template>
     <template is="dom-if" if="[[cameraActive_]]">