diff --git a/DEPS b/DEPS
index 4f9f2f48..21f82be 100644
--- a/DEPS
+++ b/DEPS
@@ -106,7 +106,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': '32942201a456ace47c052536cba594be03e9aa6c',
+  'pdfium_revision': '14f8897509d9db8739951a90488fb1634a497db5',
   # 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.
@@ -142,7 +142,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': '5d3d40fb8894e8b4b63f89b0edee83375339ed8f',
+  'catapult_revision': '883d59ef70276b408240bc48e9b615787a224188',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -910,7 +910,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@78ba74d755847eea235f8dd57849b7b1e9ee8d10',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ecf6e8e622df1050b4e74695930ee99958c839b5',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/glue/glue.gni b/android_webview/glue/glue.gni
index 1bcaa68..0581c9bb 100644
--- a/android_webview/glue/glue.gni
+++ b/android_webview/glue/glue.gni
@@ -9,6 +9,8 @@
   "//android_webview:android_webview_commandline_java",
   "//android_webview:android_webview_platform_services_java",
   "//android_webview:system_webview_manifest",
+  "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
+  "//android_webview/support_library/callback:callback_java",
   "//base:base_java",
   "//components/autofill/android:autofill_java",
   "//components/autofill/android:provider_java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
index 2e965e0..4dc4ef0c 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
@@ -60,6 +60,8 @@
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.TraceEvent;
+import org.chromium.support_lib_boundary.util.Features;
+import org.chromium.support_lib_callback_glue.SupportLibWebViewContentsClientAdapter;
 
 import java.lang.ref.WeakReference;
 import java.security.Principal;
@@ -101,6 +103,8 @@
     private final Context mContext;
     // The WebViewClient instance that was passed to WebView.setWebViewClient().
     protected WebViewClient mWebViewClient = sNullWebViewClient;
+    // Some callbacks will be forwarded to this client for apps using the support library.
+    private SupportLibWebViewContentsClientAdapter mSupportLibClient;
     // The WebChromeClient instance that was passed to WebView.setContentViewClient().
     private WebChromeClient mWebChromeClient;
     // The listener receiving find-in-page API results.
@@ -176,6 +180,9 @@
         } else {
             mWebViewClient = sNullWebViewClient;
         }
+        // Always reset mSupportLibClient, since the WebViewClient may no longer be a
+        // WebViewClientCompat, or may support a different set of Features.
+        mSupportLibClient = new SupportLibWebViewContentsClientAdapter(mWebViewClient);
     }
 
     WebViewClient getWebViewClient() {
@@ -319,7 +326,10 @@
             TraceEvent.begin("WebViewContentsClientAdapter.shouldOverrideUrlLoading");
             if (TRACE) Log.i(TAG, "shouldOverrideUrlLoading=" + request.url);
             boolean result;
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            if (mSupportLibClient.isFeatureAvailable(Features.SHOULD_OVERRIDE_WITH_REDIRECTS)) {
+                result = mSupportLibClient.shouldOverrideUrlLoading(
+                        mWebView, new WebResourceRequestAdapter(request));
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                 result = mWebViewClient.shouldOverrideUrlLoading(
                         mWebView, new WebResourceRequestAdapter(request));
             } else {
@@ -547,11 +557,15 @@
      */
     @Override
     public void onPageCommitVisible(String url) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
         try {
             TraceEvent.begin("WebViewContentsClientAdapter.onPageCommitVisible");
             if (TRACE) Log.i(TAG, "onPageCommitVisible=" + url);
-            mWebViewClient.onPageCommitVisible(mWebView, url);
+            if (mSupportLibClient.isFeatureAvailable(Features.VISUAL_STATE_CALLBACK)) {
+                mSupportLibClient.onPageCommitVisible(mWebView, url);
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                mWebViewClient.onPageCommitVisible(mWebView, url);
+            }
+            // Otherwise, the API does not exist, so do nothing.
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onPageCommitVisible");
         }
@@ -563,6 +577,10 @@
     @Override
     public void onReceivedError(int errorCode, String description, String failingUrl) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) return;
+
+        // This event is handled by the support lib in {@link #onReceivedError2}.
+        if (mSupportLibClient.isFeatureAvailable(Features.WEB_RESOURCE_ERROR)) return;
+
         try {
             TraceEvent.begin("WebViewContentsClientAdapter.onReceivedError");
             if (description == null || description.isEmpty()) {
@@ -584,7 +602,6 @@
      */
     @Override
     public void onReceivedError2(AwWebResourceRequest request, AwWebResourceError error) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
         try {
             TraceEvent.begin("WebViewContentsClientAdapter.onReceivedError");
             if (error.description == null || error.description.isEmpty()) {
@@ -594,8 +611,15 @@
                 error.description = mWebViewDelegate.getErrorString(mContext, error.errorCode);
             }
             if (TRACE) Log.i(TAG, "onReceivedError=" + request.url);
-            mWebViewClient.onReceivedError(mWebView, new WebResourceRequestAdapter(request),
-                    new WebResourceErrorImpl(error));
+            if (mSupportLibClient.isFeatureAvailable(Features.WEB_RESOURCE_ERROR)) {
+                // Note: we must pass AwWebResourceError, since this class was introduced after L.
+                mSupportLibClient.onReceivedError(
+                        mWebView, new WebResourceRequestAdapter(request), error);
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                mWebViewClient.onReceivedError(mWebView, new WebResourceRequestAdapter(request),
+                        new WebResourceErrorImpl(error));
+            }
+            // Otherwise, this is handled by {@link #onReceivedError}.
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onReceivedError");
         }
@@ -608,34 +632,36 @@
     @TargetApi(Build.VERSION_CODES.O_MR1)
     public void onSafeBrowsingHit(AwWebResourceRequest request, int threatType,
             final Callback<AwSafeBrowsingResponse> callback) {
-        // WebViewClient.onSafeBrowsingHit was added in O_MR1.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
-            callback.onResult(new AwSafeBrowsingResponse(SafeBrowsingAction.SHOW_INTERSTITIAL,
-                    /* reporting */ true));
-            return;
-        }
         try {
             TraceEvent.begin("WebViewContentsClientAdapter.onSafeBrowsingHit");
-            mWebViewClient.onSafeBrowsingHit(mWebView, new WebResourceRequestAdapter(request),
-                    threatType, new SafeBrowsingResponse() {
-                        @Override
-                        public void showInterstitial(boolean allowReporting) {
-                            callback.onResult(new AwSafeBrowsingResponse(
-                                    SafeBrowsingAction.SHOW_INTERSTITIAL, allowReporting));
-                        }
+            if (mSupportLibClient.isFeatureAvailable(Features.SAFE_BROWSING_HIT)) {
+                mSupportLibClient.onSafeBrowsingHit(
+                        mWebView, new WebResourceRequestAdapter(request), threatType, callback);
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+                mWebViewClient.onSafeBrowsingHit(mWebView, new WebResourceRequestAdapter(request),
+                        threatType, new SafeBrowsingResponse() {
+                            @Override
+                            public void showInterstitial(boolean allowReporting) {
+                                callback.onResult(new AwSafeBrowsingResponse(
+                                        SafeBrowsingAction.SHOW_INTERSTITIAL, allowReporting));
+                            }
 
-                        @Override
-                        public void proceed(boolean report) {
-                            callback.onResult(
-                                    new AwSafeBrowsingResponse(SafeBrowsingAction.PROCEED, report));
-                        }
+                            @Override
+                            public void proceed(boolean report) {
+                                callback.onResult(new AwSafeBrowsingResponse(
+                                        SafeBrowsingAction.PROCEED, report));
+                            }
 
-                        @Override
-                        public void backToSafety(boolean report) {
-                            callback.onResult(new AwSafeBrowsingResponse(
-                                    SafeBrowsingAction.BACK_TO_SAFETY, report));
-                        }
-                    });
+                            @Override
+                            public void backToSafety(boolean report) {
+                                callback.onResult(new AwSafeBrowsingResponse(
+                                        SafeBrowsingAction.BACK_TO_SAFETY, report));
+                            }
+                        });
+            } else {
+                callback.onResult(new AwSafeBrowsingResponse(SafeBrowsingAction.SHOW_INTERSTITIAL,
+                        /* reporting */ true));
+            }
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onRenderProcessGone");
         }
@@ -643,14 +669,24 @@
 
     @Override
     public void onReceivedHttpError(AwWebResourceRequest request, AwWebResourceResponse response) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
         try {
             TraceEvent.begin("WebViewContentsClientAdapter.onReceivedHttpError");
             if (TRACE) Log.i(TAG, "onReceivedHttpError=" + request.url);
-            mWebViewClient.onReceivedHttpError(mWebView, new WebResourceRequestAdapter(request),
-                    new WebResourceResponse(true, response.getMimeType(), response.getCharset(),
-                            response.getStatusCode(), response.getReasonPhrase(),
-                            response.getResponseHeaders(), response.getData()));
+            if (mSupportLibClient.isFeatureAvailable(Features.RECEIVE_HTTP_ERROR)) {
+                // Note: we do not create an immutable instance here, because that constructor is
+                // not available on L.
+                mSupportLibClient.onReceivedHttpError(mWebView,
+                        new WebResourceRequestAdapter(request),
+                        new WebResourceResponse(response.getMimeType(), response.getCharset(),
+                                response.getStatusCode(), response.getReasonPhrase(),
+                                response.getResponseHeaders(), response.getData()));
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                mWebViewClient.onReceivedHttpError(mWebView, new WebResourceRequestAdapter(request),
+                        new WebResourceResponse(true, response.getMimeType(), response.getCharset(),
+                                response.getStatusCode(), response.getReasonPhrase(),
+                                response.getResponseHeaders(), response.getData()));
+            }
+            // Otherwise, the API does not exist, so do nothing.
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onReceivedHttpError");
         }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 5c34130..cce83619 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -864,6 +864,7 @@
             WindowAndroid windowAndroid) {
         mContentViewCore = ContentViewCore.create(mContext, PRODUCT_VERSION, webContents,
                 viewDelegate, internalDispatcher, windowAndroid);
+        mContentViewCore.setHideKeyboardOnBlur(false);
         SelectionPopupController controller = SelectionPopupController.fromWebContents(webContents);
         controller.setActionModeCallback(
                 new AwActionModeCallback(mContext, this, controller.getActionModeCallbackHelper()));
@@ -3480,7 +3481,7 @@
         public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
             if (isDestroyedOrNoOperation(NO_WARN)) return;
             mContainerViewFocused = focused;
-            mContentViewCore.onFocusChanged(focused, false /* hideKeyboardOnBlur */);
+            mContentViewCore.onViewFocusChanged(focused);
         }
 
         @Override
diff --git a/android_webview/support_library/boundary_interfaces/BUILD.gn b/android_webview/support_library/boundary_interfaces/BUILD.gn
index 6ce4cb1..00acb4e1 100644
--- a/android_webview/support_library/boundary_interfaces/BUILD.gn
+++ b/android_webview/support_library/boundary_interfaces/BUILD.gn
@@ -7,6 +7,7 @@
 
 android_library("boundary_interface_java") {
   java_files = [
+    "src/org/chromium/support_lib_boundary/FeatureFlagHolderBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/SafeBrowsingResponseBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/ServiceWorkerClientBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/ServiceWorkerControllerBoundaryInterface.java",
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/FeatureFlagHolderBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/FeatureFlagHolderBoundaryInterface.java
new file mode 100644
index 0000000..d2fa11e
--- /dev/null
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/FeatureFlagHolderBoundaryInterface.java
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.support_lib_boundary;
+
+/**
+ * Boundary interface to be implemented by any type which is constructed on the support library
+ * side (such as callback classes). This interface is a way for the instance to declare which
+ * {@link org.chromium.support_lib_boundary.util.Features} it supports (this may vary between
+ * instances if the app uses multiple versions of the support library).
+ *
+ * This need only be implemented by objects created on the support library side, since we know any
+ * objects created on the chromium side have the same feature list as the WebView APK itself (as
+ * returned by {@link WebViewProviderFactoryBoundaryInterface#getSupportedFeatures}).
+ */
+public interface FeatureFlagHolderBoundaryInterface {
+    /**
+     * Indicate the list of {@link org.chromium.support_lib_boundary.util.Features} supported by
+     * this object.
+     *
+     * @return The supported features.
+     */
+    String[] getSupportedFeatures();
+}
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
index d74ea55..e4d04b46 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
@@ -13,6 +13,19 @@
     // This class just contains constants representing features.
     private Features() {}
 
-    // WebViewCompat.postVisualStateCallback
+    // WebViewCompat#postVisualStateCallback
+    // WebViewClientCompat#onPageCommitVisible
     public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+
+    // WebViewClientCompat#onReceivedError(WebView, WebResourceRequest, WebResourceError)
+    public static final String WEB_RESOURCE_ERROR = "WEB_RESOURCE_ERROR";
+
+    // WebViewClientCompat#onReceivedHttpError
+    public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
+
+    // WebViewClientCompat#onSafeBrowsingHit
+    public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
+
+    // WebViewClientCompat#shouldOverrideUrlLoading
+    public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
 }
diff --git a/android_webview/support_library/callback/BUILD.gn b/android_webview/support_library/callback/BUILD.gn
new file mode 100644
index 0000000..e830f65
--- /dev/null
+++ b/android_webview/support_library/callback/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+
+android_library("callback_java") {
+  java_files = [ "java/src/org/chromium/support_lib_callback_glue/SupportLibWebViewContentsClientAdapter.java" ]
+
+  deps = [
+    "//android_webview:android_webview_commandline_java",
+    "//android_webview:android_webview_java",
+    "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
+    "//base:base_java",
+  ]
+}
diff --git a/android_webview/support_library/callback/java/src/org/chromium/support_lib_callback_glue/SupportLibWebViewContentsClientAdapter.java b/android_webview/support_library/callback/java/src/org/chromium/support_lib_callback_glue/SupportLibWebViewContentsClientAdapter.java
new file mode 100644
index 0000000..34a9628
--- /dev/null
+++ b/android_webview/support_library/callback/java/src/org/chromium/support_lib_callback_glue/SupportLibWebViewContentsClientAdapter.java
@@ -0,0 +1,155 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.support_lib_callback_glue;
+
+import android.support.annotation.Nullable;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import org.chromium.android_webview.AwContentsClient.AwWebResourceError;
+import org.chromium.android_webview.AwSafeBrowsingResponse;
+import org.chromium.android_webview.SafeBrowsingAction;
+import org.chromium.base.Callback;
+import org.chromium.support_lib_boundary.SafeBrowsingResponseBoundaryInterface;
+import org.chromium.support_lib_boundary.WebResourceErrorBoundaryInterface;
+import org.chromium.support_lib_boundary.WebViewClientBoundaryInterface;
+import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
+import org.chromium.support_lib_boundary.util.Features;
+
+import java.lang.reflect.InvocationHandler;
+
+/**
+ * Support library glue version of WebViewContentsClientAdapter.
+ */
+public class SupportLibWebViewContentsClientAdapter {
+    private static final String WEBVIEW_CLIENT_COMPAT_NAME = "androidx.webkit.WebViewClientCompat";
+
+    // If {@code null}, this indicates the WebViewClient is not a WebViewClientCompat. Otherwise,
+    // this is a Proxy for the WebViewClientCompat.
+    @Nullable
+    private WebViewClientBoundaryInterface mWebViewClient;
+
+    private static class SafeBrowsingResponseDelegate
+            implements SafeBrowsingResponseBoundaryInterface {
+        private Callback<AwSafeBrowsingResponse> mCallback;
+
+        SafeBrowsingResponseDelegate(Callback<AwSafeBrowsingResponse> callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void showInterstitial(boolean allowReporting) {
+            mCallback.onResult(new AwSafeBrowsingResponse(
+                    SafeBrowsingAction.SHOW_INTERSTITIAL, allowReporting));
+        }
+
+        @Override
+        public void proceed(boolean report) {
+            mCallback.onResult(new AwSafeBrowsingResponse(SafeBrowsingAction.PROCEED, report));
+        }
+
+        @Override
+        public void backToSafety(boolean report) {
+            mCallback.onResult(
+                    new AwSafeBrowsingResponse(SafeBrowsingAction.BACK_TO_SAFETY, report));
+        }
+    };
+
+    private static class WebResourceErrorDelegate implements WebResourceErrorBoundaryInterface {
+        private AwWebResourceError mError;
+
+        WebResourceErrorDelegate(AwWebResourceError error) {
+            mError = error;
+        }
+
+        @Override
+        public int getErrorCode() {
+            return mError.errorCode;
+        }
+
+        @Override
+        public CharSequence getDescription() {
+            return mError.description;
+        }
+    };
+
+    public SupportLibWebViewContentsClientAdapter(WebViewClient possiblyCompatClient) {
+        mWebViewClient = convertCompatClient(possiblyCompatClient);
+    }
+
+    @Nullable
+    private WebViewClientBoundaryInterface convertCompatClient(WebViewClient possiblyCompatClient) {
+        if (!clientIsCompat(possiblyCompatClient)) return null;
+
+        InvocationHandler handler =
+                BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(possiblyCompatClient);
+
+        return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+                WebViewClientBoundaryInterface.class, handler);
+    }
+
+    private boolean clientIsCompat(WebViewClient possiblyCompatClient) {
+        try {
+            Class compatClass = Class.forName(WEBVIEW_CLIENT_COMPAT_NAME, false,
+                    possiblyCompatClient.getClass().getClassLoader());
+            return compatClass.isInstance(possiblyCompatClient);
+        } catch (ClassNotFoundException e) {
+            // If WEBVIEW_CLIENT_COMPAT_NAME is not in the ClassLoader, then this cannot be an
+            // instance of WebViewClientCompat.
+            return false;
+        }
+    }
+
+    /**
+     * Indicates whether this client can handle the callback(s) assocated with {@param featureName}.
+     * This should be called with the correct feature name before invoking the corresponding
+     * callback, and the callback must not be called if this returns {@code false} for the feature.
+     *
+     * @param featureName the feature for the desired callback.
+     * @return {@code true} if this client can handle the feature.
+     */
+    public boolean isFeatureAvailable(String featureName) {
+        if (mWebViewClient == null) return false;
+        // TODO(ntfschr): provide a real implementation, which consults the WebViewClientCompat.
+        return true;
+    }
+
+    public void onPageCommitVisible(WebView webView, String url) {
+        assert isFeatureAvailable(Features.VISUAL_STATE_CALLBACK);
+        mWebViewClient.onPageCommitVisible(webView, url);
+    }
+
+    public void onReceivedError(
+            WebView webView, WebResourceRequest request, final AwWebResourceError error) {
+        assert isFeatureAvailable(Features.WEB_RESOURCE_ERROR);
+        WebResourceErrorBoundaryInterface errorDelegate = new WebResourceErrorDelegate(error);
+        InvocationHandler errorHandler =
+                BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(errorDelegate);
+        mWebViewClient.onReceivedError(webView, request, errorHandler);
+    }
+
+    public void onReceivedHttpError(
+            WebView webView, WebResourceRequest request, WebResourceResponse response) {
+        assert isFeatureAvailable(Features.RECEIVE_HTTP_ERROR);
+        mWebViewClient.onReceivedHttpError(webView, request, response);
+    }
+
+    public void onSafeBrowsingHit(WebView webView, WebResourceRequest request, int threatType,
+            Callback<AwSafeBrowsingResponse> callback) {
+        assert isFeatureAvailable(Features.SAFE_BROWSING_HIT);
+        SafeBrowsingResponseBoundaryInterface responseDelegate =
+                new SafeBrowsingResponseDelegate(callback);
+        InvocationHandler responseHandler =
+                BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(responseDelegate);
+        mWebViewClient.onSafeBrowsingHit(webView, request, threatType, responseHandler);
+    }
+
+    public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request) {
+        assert isFeatureAvailable(Features.SHOULD_OVERRIDE_WITH_REDIRECTS);
+        return mWebViewClient.shouldOverrideUrlLoading(webView, request);
+    }
+}
diff --git a/ash/app_list/model/search/search_result.h b/ash/app_list/model/search/search_result.h
index c89a0cc..b4a33cb 100644
--- a/ash/app_list/model/search/search_result.h
+++ b/ash/app_list/model/search/search_result.h
@@ -119,10 +119,6 @@
   void UpdateFromMatch(const TokenizedString& title,
                        const TokenizedStringMatch& match);
 
-  // TODO(mukai): Remove this method and really simplify the ownership of
-  // SearchResult. Ideally, SearchResult will be copyable.
-  virtual std::unique_ptr<SearchResult> Duplicate() const = 0;
-
   // Invokes a custom action on the result. It does nothing by default.
   virtual void InvokeAction(int action_index, int event_flags);
 
diff --git a/ash/message_center/message_center_controller.cc b/ash/message_center/message_center_controller.cc
index 3fc2fed..e387373 100644
--- a/ash/message_center/message_center_controller.cc
+++ b/ash/message_center/message_center_controller.cc
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/command_line.h"
+#include "base/unguessable_token.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -44,11 +45,14 @@
     : public message_center::NotificationDelegate {
  public:
   AshClientNotificationDelegate(const std::string& notification_id,
+                                const base::UnguessableToken& display_token,
                                 mojom::AshMessageCenterClient* client)
-      : notification_id_(notification_id), client_(client) {}
+      : notification_id_(notification_id),
+        display_token_(display_token),
+        client_(client) {}
 
   void Close(bool by_user) override {
-    client_->HandleNotificationClosed(notification_id_, by_user);
+    client_->HandleNotificationClosed(display_token_, by_user);
   }
 
   void Click(const base::Optional<int>& button_index,
@@ -72,7 +76,12 @@
  private:
   ~AshClientNotificationDelegate() override = default;
 
-  std::string notification_id_;
+  // The ID of the notification.
+  const std::string notification_id_;
+
+  // The token that was generated for the ShowClientNotification() call.
+  const base::UnguessableToken display_token_;
+
   mojom::AshMessageCenterClient* client_;
 
   DISALLOW_COPY_AND_ASSIGN(AshClientNotificationDelegate);
@@ -139,12 +148,14 @@
 }
 
 void MessageCenterController::ShowClientNotification(
-    const message_center::Notification& notification) {
+    const message_center::Notification& notification,
+    const base::UnguessableToken& display_token) {
   DCHECK(client_.is_bound());
   auto message_center_notification =
       std::make_unique<message_center::Notification>(notification);
-  message_center_notification->set_delegate(base::WrapRefCounted(
-      new AshClientNotificationDelegate(notification.id(), client_.get())));
+  message_center_notification->set_delegate(
+      base::WrapRefCounted(new AshClientNotificationDelegate(
+          notification.id(), display_token, client_.get())));
   MessageCenter::Get()->AddNotification(std::move(message_center_notification));
 }
 
diff --git a/ash/message_center/message_center_controller.h b/ash/message_center/message_center_controller.h
index cebdfdf..d331513e 100644
--- a/ash/message_center/message_center_controller.h
+++ b/ash/message_center/message_center_controller.h
@@ -38,7 +38,8 @@
   void SetClient(
       mojom::AshMessageCenterClientAssociatedPtrInfo client) override;
   void ShowClientNotification(
-      const message_center::Notification& notification) override;
+      const message_center::Notification& notification,
+      const base::UnguessableToken& display_token) override;
   void CloseClientNotification(const std::string& id) override;
   void UpdateNotifierIcon(const message_center::NotifierId& notifier_id,
                           const gfx::ImageSkia& icon) override;
diff --git a/ash/message_center/notifier_settings_view_unittest.cc b/ash/message_center/notifier_settings_view_unittest.cc
index 926efc63..062a656 100644
--- a/ash/message_center/notifier_settings_view_unittest.cc
+++ b/ash/message_center/notifier_settings_view_unittest.cc
@@ -38,7 +38,8 @@
   }
 
   // mojom::AshMessageCenterClient:
-  void HandleNotificationClosed(const std::string& id, bool by_user) override {}
+  void HandleNotificationClosed(const base::UnguessableToken& token,
+                                bool by_user) override {}
   void HandleNotificationClicked(const std::string& id) override {}
   void HandleNotificationButtonClicked(
       const std::string& id,
diff --git a/ash/public/interfaces/ash_message_center_controller.mojom b/ash/public/interfaces/ash_message_center_controller.mojom
index 639a764..199cceb 100644
--- a/ash/public/interfaces/ash_message_center_controller.mojom
+++ b/ash/public/interfaces/ash_message_center_controller.mojom
@@ -8,6 +8,7 @@
 import "ui/message_center/public/mojo/notification.mojom";
 import "ui/message_center/public/mojo/notifier_id.mojom";
 import "mojo/public/mojom/base/string16.mojom";
+import "mojo/public/mojom/base/unguessable_token.mojom";
 
 // A struct that contains information for presenting a notifier in a settings
 // panel.
@@ -33,8 +34,12 @@
 interface AshMessageCenterController {
   SetClient(associated AshMessageCenterClient client);
 
-  ShowClientNotification(message_center.mojom.Notification notification);
+  // Shows a notification. |display_token| will be used to reference this
+  // notification for AshMessageCenterClient::Close().
+  ShowClientNotification(message_center.mojom.Notification notification,
+                         mojo_base.mojom.UnguessableToken display_token);
 
+  // Closes a notification by the notification ID.
   CloseClientNotification(string id);
 
   UpdateNotifierIcon(message_center.mojom.NotifierId notifier_id,
@@ -54,7 +59,8 @@
 // the client.
 interface AshMessageCenterClient {
   // Called when a notification previously displayed by the client is closed.
-  HandleNotificationClosed(string id, bool by_user);
+  HandleNotificationClosed(mojo_base.mojom.UnguessableToken display_token,
+                           bool by_user);
 
   // Called when the body of a notification is clicked.
   HandleNotificationClicked(string id);
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc
index 4ebe7c3..130e548 100644
--- a/ash/shell/app_list.cc
+++ b/ash/shell/app_list.cc
@@ -184,11 +184,6 @@
 
   WindowTypeShelfItem::Type type() const { return type_; }
 
-  // app_list::SearchResult:
-  std::unique_ptr<SearchResult> Duplicate() const override {
-    return std::unique_ptr<SearchResult>();
-  }
-
  private:
   WindowTypeShelfItem::Type type_;
 
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index bb95233..77a3e99 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -2908,7 +2908,8 @@
 }
 
 // Verify that attempting to drag with a secondary finger works as expected.
-TEST_F(WindowSelectorTest, DraggingWithTwoFingers) {
+// Flaky, see https://crbug.com/827435.
+TEST_F(WindowSelectorTest, DISABLED_DraggingWithTwoFingers) {
   std::unique_ptr<aura::Window> window1 = CreateTestWindow();
   std::unique_ptr<aura::Window> window2 = CreateTestWindow();
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 78cc8a74..d235a66 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2270,7 +2270,6 @@
     "optional_unittest.cc",
     "os_compat_android_unittest.cc",
     "path_service_unittest.cc",
-    "pending_task_unittest.cc",
     "pickle_unittest.cc",
     "posix/file_descriptor_shuffle_unittest.cc",
     "posix/unix_domain_socket_unittest.cc",
diff --git a/base/base_switches.cc b/base/base_switches.cc
index 9f33d0bf..62e6d3dc 100644
--- a/base/base_switches.cc
+++ b/base/base_switches.cc
@@ -7,6 +7,9 @@
 
 namespace switches {
 
+// Delays execution of base::TaskPriority::BACKGROUND tasks until shutdown.
+const char kDisableBackgroundTasks[] = "disable-background-tasks";
+
 // Disables the crash reporting.
 const char kDisableBreakpad[]               = "disable-breakpad";
 
diff --git a/base/base_switches.h b/base/base_switches.h
index e5b9453..a444f09b 100644
--- a/base/base_switches.h
+++ b/base/base_switches.h
@@ -11,6 +11,7 @@
 
 namespace switches {
 
+extern const char kDisableBackgroundTasks[];
 extern const char kDisableBreakpad[];
 extern const char kDisableFeatures[];
 extern const char kDisableLowEndDeviceMode[];
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc
index d67412be..2197b8591 100644
--- a/base/debug/task_annotator.cc
+++ b/base/debug/task_annotator.cc
@@ -8,12 +8,29 @@
 
 #include "base/debug/activity_tracker.h"
 #include "base/debug/alias.h"
+#include "base/no_destructor.h"
 #include "base/pending_task.h"
+#include "base/threading/thread_local.h"
 #include "base/trace_event/trace_event.h"
 
 namespace base {
 namespace debug {
 
+namespace {
+
+TaskAnnotator::ObserverForTesting* g_task_annotator_observer = nullptr;
+
+// Returns the TLS slot that stores the PendingTask currently in progress on
+// each thread. Used to allow creating a breadcrumb of program counters on the
+// stack to help identify a task's origin in crashes.
+ThreadLocalPointer<const PendingTask>* GetTLSForCurrentPendingTask() {
+  static NoDestructor<ThreadLocalPointer<const PendingTask>>
+      tls_for_current_pending_task;
+  return tls_for_current_pending_task.get();
+}
+
+}  // namespace
+
 TaskAnnotator::TaskAnnotator() = default;
 
 TaskAnnotator::~TaskAnnotator() = default;
@@ -26,6 +43,21 @@
                            TRACE_ID_MANGLE(GetTaskTraceID(pending_task)),
                            TRACE_EVENT_FLAG_FLOW_OUT);
   }
+
+  // TODO(https://crbug.com/826902): Fix callers that invoke DidQueueTask()
+  // twice for the same PendingTask.
+  // DCHECK(!pending_task.task_backtrace[0])
+  //     << "Task backtrace was already set, task posted twice??";
+  if (!pending_task.task_backtrace[0]) {
+    const PendingTask* parent_task = GetTLSForCurrentPendingTask()->Get();
+    if (parent_task) {
+      pending_task.task_backtrace[0] =
+          parent_task->posted_from.program_counter();
+      std::copy(parent_task->task_backtrace.begin(),
+                parent_task->task_backtrace.end() - 1,
+                pending_task.task_backtrace.begin() + 1);
+    }
+  }
 }
 
 void TaskAnnotator::RunTask(const char* queue_function,
@@ -58,7 +90,17 @@
             pending_task->task_backtrace.end(), task_backtrace.begin() + 2);
   debug::Alias(&task_backtrace);
 
+  ThreadLocalPointer<const PendingTask>* tls_for_current_pending_task =
+      GetTLSForCurrentPendingTask();
+  const PendingTask* previous_pending_task =
+      tls_for_current_pending_task->Get();
+  tls_for_current_pending_task->Set(pending_task);
+
+  if (g_task_annotator_observer)
+    g_task_annotator_observer->BeforeRunTask(pending_task);
   std::move(pending_task->task).Run();
+
+  tls_for_current_pending_task->Set(previous_pending_task);
 }
 
 uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
@@ -67,5 +109,16 @@
           32);
 }
 
+// static
+void TaskAnnotator::RegisterObserverForTesting(ObserverForTesting* observer) {
+  DCHECK(!g_task_annotator_observer);
+  g_task_annotator_observer = observer;
+}
+
+// static
+void TaskAnnotator::ClearObserverForTesting() {
+  g_task_annotator_observer = nullptr;
+}
+
 }  // namespace debug
 }  // namespace base
diff --git a/base/debug/task_annotator.h b/base/debug/task_annotator.h
index de03e41..f53d02c 100644
--- a/base/debug/task_annotator.h
+++ b/base/debug/task_annotator.h
@@ -18,6 +18,13 @@
 // such as task origins, queueing durations and memory usage.
 class BASE_EXPORT TaskAnnotator {
  public:
+  class ObserverForTesting {
+   public:
+    // Invoked just before RunTask() in the scope in which the task is about to
+    // be executed.
+    virtual void BeforeRunTask(const PendingTask* pending_task) = 0;
+  };
+
   TaskAnnotator();
   ~TaskAnnotator();
 
@@ -40,6 +47,14 @@
   uint64_t GetTaskTraceID(const PendingTask& task) const;
 
  private:
+  friend class TaskAnnotatorBacktraceIntegrationTest;
+
+  // Registers an ObserverForTesting that will be invoked by all TaskAnnotators'
+  // RunTask(). This registration and the implementation of BeforeRunTask() are
+  // responsible to ensure thread-safety.
+  static void RegisterObserverForTesting(ObserverForTesting* observer);
+  static void ClearObserverForTesting();
+
   DISALLOW_COPY_AND_ASSIGN(TaskAnnotator);
 };
 
diff --git a/base/debug/task_annotator_unittest.cc b/base/debug/task_annotator_unittest.cc
index bfb0e7c..51a5d32 100644
--- a/base/debug/task_annotator_unittest.cc
+++ b/base/debug/task_annotator_unittest.cc
@@ -3,8 +3,24 @@
 // found in the LICENSE file.
 
 #include "base/debug/task_annotator.h"
+
+#include <algorithm>
+#include <vector>
+
 #include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
 #include "base/pending_task.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -28,5 +44,328 @@
   EXPECT_EQ(123, result);
 }
 
+// Test task annotator integration in base APIs and ensuing support for
+// backtraces. Tasks posted across multiple threads in this test fixture should
+// be synchronized as BeforeRunTask() and VerifyTraceAndPost() assume tasks are
+// observed in lock steps, one at a time.
+class TaskAnnotatorBacktraceIntegrationTest
+    : public ::testing::Test,
+      public TaskAnnotator::ObserverForTesting {
+ public:
+  using ExpectedTrace = std::vector<const void*>;
+
+  TaskAnnotatorBacktraceIntegrationTest() = default;
+
+  ~TaskAnnotatorBacktraceIntegrationTest() override = default;
+
+  // TaskAnnotator::ObserverForTesting:
+  void BeforeRunTask(const PendingTask* pending_task) override {
+    AutoLock auto_lock(on_before_run_task_lock_);
+    last_posted_from_ = pending_task->posted_from;
+    last_task_backtrace_ = pending_task->task_backtrace;
+  }
+
+  void SetUp() override { TaskAnnotator::RegisterObserverForTesting(this); }
+
+  void TearDown() override { TaskAnnotator::ClearObserverForTesting(); }
+
+  void VerifyTraceAndPost(const scoped_refptr<SequencedTaskRunner>& task_runner,
+                          const Location& posted_from,
+                          const Location& next_from_here,
+                          const ExpectedTrace& expected_trace,
+                          OnceClosure task) {
+    SCOPED_TRACE(StringPrintf("Callback Depth: %zu", expected_trace.size()));
+
+    EXPECT_EQ(posted_from, last_posted_from_);
+    for (size_t i = 0; i < last_task_backtrace_.size(); i++) {
+      SCOPED_TRACE(StringPrintf("Trace frame: %zu", i));
+      if (i < expected_trace.size())
+        EXPECT_EQ(expected_trace[i], last_task_backtrace_[i]);
+      else
+        EXPECT_EQ(nullptr, last_task_backtrace_[i]);
+    }
+
+    task_runner->PostTask(next_from_here, std::move(task));
+  }
+
+  // Same as VerifyTraceAndPost() with the exception that it also posts a task
+  // that will prevent |task| from running until |wait_before_next_task| is
+  // signaled.
+  void VerifyTraceAndPostWithBlocker(
+      const scoped_refptr<SequencedTaskRunner>& task_runner,
+      const Location& posted_from,
+      const Location& next_from_here,
+      const ExpectedTrace& expected_trace,
+      OnceClosure task,
+      WaitableEvent* wait_before_next_task) {
+    DCHECK(wait_before_next_task);
+
+    // Need to lock to ensure the upcoming VerifyTraceAndPost() runs before the
+    // BeforeRunTask() hook for the posted WaitableEvent::Wait(). Otherwise the
+    // upcoming VerifyTraceAndPost() will race to read the state saved in the
+    // BeforeRunTask() hook preceding the current task.
+    AutoLock auto_lock(on_before_run_task_lock_);
+    task_runner->PostTask(
+        FROM_HERE,
+        BindOnce(&WaitableEvent::Wait, Unretained(wait_before_next_task)));
+    VerifyTraceAndPost(task_runner, posted_from, next_from_here, expected_trace,
+                       std::move(task));
+  }
+
+ protected:
+  static void RunTwo(OnceClosure c1, OnceClosure c2) {
+    std::move(c1).Run();
+    std::move(c2).Run();
+  }
+
+ private:
+  // While calls to VerifyTraceAndPost() are strictly ordered in tests below
+  // (and hence non-racy), some helper methods (e.g. Wait/Signal) do racily call
+  // into BeforeRunTask(). This Lock ensures these unobserved writes are not
+  // racing. Locking isn't required on read per the VerifyTraceAndPost()
+  // themselves being ordered.
+  Lock on_before_run_task_lock_;
+
+  Location last_posted_from_ = {};
+  std::array<const void*, 4> last_task_backtrace_ = {};
+
+  DISALLOW_COPY_AND_ASSIGN(TaskAnnotatorBacktraceIntegrationTest);
+};
+
+// Ensure the task backtrace populates correctly.
+TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedSimple) {
+  MessageLoop loop;
+  const Location location0 = FROM_HERE;
+  const Location location1 = FROM_HERE;
+  const Location location2 = FROM_HERE;
+  const Location location3 = FROM_HERE;
+  const Location location4 = FROM_HERE;
+  const Location location5 = FROM_HERE;
+
+  RunLoop run_loop;
+
+  // Task 5 has tasks 4/3/2/1 as parents (task 0 isn't visible as only the
+  // last 4 parents are kept).
+  OnceClosure task5 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location5, FROM_HERE,
+      ExpectedTrace({location4.program_counter(), location3.program_counter(),
+                     location2.program_counter(), location1.program_counter()}),
+      run_loop.QuitClosure());
+
+  // Task i=4/3/2/1/0 have tasks [0,i) as parents.
+  OnceClosure task4 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location4, location5,
+      ExpectedTrace({location3.program_counter(), location2.program_counter(),
+                     location1.program_counter(), location0.program_counter()}),
+      std::move(task5));
+  OnceClosure task3 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location3, location4,
+      ExpectedTrace({location2.program_counter(), location1.program_counter(),
+                     location0.program_counter()}),
+      std::move(task4));
+  OnceClosure task2 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location2, location3,
+      ExpectedTrace({location1.program_counter(), location0.program_counter()}),
+      std::move(task3));
+  OnceClosure task1 =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+               Unretained(this), loop.task_runner(), location1, location2,
+               ExpectedTrace({location0.program_counter()}), std::move(task2));
+  OnceClosure task0 =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+               Unretained(this), loop.task_runner(), location0, location1,
+               ExpectedTrace({}), std::move(task1));
+
+  loop.task_runner()->PostTask(location0, std::move(task0));
+
+  run_loop.Run();
+}
+
+// Ensure it works when posting tasks across multiple threads managed by //base.
+TEST_F(TaskAnnotatorBacktraceIntegrationTest, MultipleThreads) {
+  test::ScopedTaskEnvironment scoped_task_environment;
+
+  // Use diverse task runners (a MessageLoop on the main thread, a TaskScheduler
+  // based SequencedTaskRunner, and a TaskScheduler based
+  // SingleThreadTaskRunner) to verify that TaskAnnotator can capture backtraces
+  // for PostTasks back-and-forth between these.
+  auto main_thread_a = ThreadTaskRunnerHandle::Get();
+  auto task_runner_b = CreateSingleThreadTaskRunnerWithTraits({});
+  auto task_runner_c = CreateSequencedTaskRunnerWithTraits(
+      {base::MayBlock(), base::WithBaseSyncPrimitives()});
+
+  const Location& location_a0 = FROM_HERE;
+  const Location& location_a1 = FROM_HERE;
+  const Location& location_a2 = FROM_HERE;
+  const Location& location_a3 = FROM_HERE;
+
+  const Location& location_b0 = FROM_HERE;
+  const Location& location_b1 = FROM_HERE;
+
+  const Location& location_c0 = FROM_HERE;
+
+  RunLoop run_loop;
+
+  // All tasks below happen in lock step by nature of being posted by the
+  // previous one (plus the synchronous nature of RunTwo()) with the exception
+  // of the follow-up local task to |task_b0_local|. This WaitableEvent ensures
+  // it completes before |task_c0| runs to avoid racy invocations of
+  // BeforeRunTask()+VerifyTraceAndPost().
+  WaitableEvent lock_step(WaitableEvent::ResetPolicy::AUTOMATIC,
+                          WaitableEvent::InitialState::NOT_SIGNALED);
+
+  // Here is the execution order generated below:
+  //  A: TA0 -> TA1 \                                    TA2
+  //  B:            TB0L \ + TB0F \  Signal \           /
+  //                      ---------\--/      \         /
+  //                                \         \       /
+  //  C:                            Wait........ TC0 /
+
+  // On task runner c, post a task back to main thread that verifies its trace
+  // and terminates after one more self-post.
+  OnceClosure task_a2 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), main_thread_a, location_a2, location_a3,
+      ExpectedTrace(
+          {location_c0.program_counter(), location_b0.program_counter(),
+           location_a1.program_counter(), location_a0.program_counter()}),
+      run_loop.QuitClosure());
+  OnceClosure task_c0 =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+               Unretained(this), main_thread_a, location_c0, location_a2,
+               ExpectedTrace({location_b0.program_counter(),
+                              location_a1.program_counter(),
+                              location_a0.program_counter()}),
+               std::move(task_a2));
+
+  // On task runner b run two tasks that conceptually come from the same
+  // location (managed via RunTwo().) One will post back to task runner b and
+  // another will post to task runner c to test spawning multiple tasks on
+  // different message loops. The task posted to task runner c will not get
+  // location b1 whereas the one posted back to task runner b will.
+  OnceClosure task_b0_fork = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPostWithBlocker,
+      Unretained(this), task_runner_c, location_b0, location_c0,
+      ExpectedTrace(
+          {location_a1.program_counter(), location_a0.program_counter()}),
+      std::move(task_c0), &lock_step);
+  OnceClosure task_b0_local =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+               Unretained(this), task_runner_b, location_b0, location_b1,
+               ExpectedTrace({location_a1.program_counter(),
+                              location_a0.program_counter()}),
+               BindOnce(&WaitableEvent::Signal, Unretained(&lock_step)));
+
+  OnceClosure task_a1 =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+               Unretained(this), task_runner_b, location_a1, location_b0,
+               ExpectedTrace({location_a0.program_counter()}),
+               BindOnce(&TaskAnnotatorBacktraceIntegrationTest::RunTwo,
+                        std::move(task_b0_local), std::move(task_b0_fork)));
+  OnceClosure task_a0 =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+               Unretained(this), main_thread_a, location_a0, location_a1,
+               ExpectedTrace({}), std::move(task_a1));
+
+  main_thread_a->PostTask(location_a0, std::move(task_a0));
+
+  run_loop.Run();
+}
+
+// Ensure nesting doesn't break the chain.
+TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedNested) {
+  MessageLoop loop;
+  const Location location0 = FROM_HERE;
+  const Location location1 = FROM_HERE;
+  const Location location2 = FROM_HERE;
+  const Location location3 = FROM_HERE;
+  const Location location4 = FROM_HERE;
+  const Location location5 = FROM_HERE;
+
+  RunLoop run_loop;
+
+  // Task execution below looks like this, w.r.t. to RunLoop depths:
+  // 1 : T0 \ + NRL1 \                                 ---------> T4 -> T5
+  // 2 :     ---------> T1 \ -> NRL2 \ ----> T2 -> T3 / + Quit /
+  // 3 :                    ---------> DN /
+
+  // NRL1 tests that tasks that occur at a different nesting depth than their
+  // parent have a sane backtrace nonetheless (both ways).
+
+  // NRL2 tests that posting T2 right after exiting the RunLoop (from the same
+  // task) results in NRL2 being its parent (and not the DoNothing() task that
+  // just ran -- which would have been the case if the "current task" wasn't
+  // restored properly when returning from a task within a task).
+
+  // In other words, this is regression test for a bug in the previous
+  // implementation. In the current implementation, replacing
+  //   tls_for_current_pending_task->Set(previous_pending_task);
+  // by
+  //   tls_for_current_pending_task->Set(nullptr);
+  // at the end of TaskAnnotator::RunTask() makes this test fail.
+
+  RunLoop nested_run_loop1(RunLoop::Type::kNestableTasksAllowed);
+
+  // Expectations are the same as in SingleThreadedSimple test despite the
+  // nested loop starting between tasks 0 and 1 and stopping between tasks 3 and
+  // 4.
+  OnceClosure task5 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location5, FROM_HERE,
+      ExpectedTrace({location4.program_counter(), location3.program_counter(),
+                     location2.program_counter(), location1.program_counter()}),
+      run_loop.QuitClosure());
+  OnceClosure task4 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location4, location5,
+      ExpectedTrace({location3.program_counter(), location2.program_counter(),
+                     location1.program_counter(), location0.program_counter()}),
+      std::move(task5));
+  OnceClosure task3 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location3, location4,
+      ExpectedTrace({location2.program_counter(), location1.program_counter(),
+                     location0.program_counter()}),
+      std::move(task4));
+
+  OnceClosure run_task_3_then_quit_nested_loop1 =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::RunTwo, std::move(task3),
+               nested_run_loop1.QuitClosure());
+
+  OnceClosure task2 = BindOnce(
+      &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+      Unretained(this), loop.task_runner(), location2, location3,
+      ExpectedTrace({location1.program_counter(), location0.program_counter()}),
+      std::move(run_task_3_then_quit_nested_loop1));
+
+  // Task 1 is custom. It enters another nested RunLoop, has it do work and exit
+  // before posting the next task. This confirms that |task1| is restored as the
+  // current task before posting |task2| after returning from the nested loop.
+  RunLoop nested_run_loop2(RunLoop::Type::kNestableTasksAllowed);
+  OnceClosure task1 = BindOnce(
+      [](RunLoop* nested_run_loop, const Location& location2,
+         OnceClosure task2) {
+        ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing());
+        nested_run_loop->RunUntilIdle();
+        ThreadTaskRunnerHandle::Get()->PostTask(location2, std::move(task2));
+      },
+      Unretained(&nested_run_loop2), location2, std::move(task2));
+
+  OnceClosure task0 =
+      BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost,
+               Unretained(this), loop.task_runner(), location0, location1,
+               ExpectedTrace({}), std::move(task1));
+
+  loop.task_runner()->PostTask(location0, std::move(task0));
+  loop.task_runner()->PostTask(
+      FROM_HERE, BindOnce(&RunLoop::Run, Unretained(&nested_run_loop1)));
+
+  run_loop.Run();
+}
+
 }  // namespace debug
 }  // namespace base
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index 92874be0..41cb4f4 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -343,7 +343,6 @@
 
 void MessageLoop::RunTask(PendingTask* pending_task) {
   DCHECK(task_execution_allowed_);
-  current_pending_task_ = pending_task;
 
   // Execute the task and assume the worst: It is probably not reentrant.
   task_execution_allowed_ = false;
@@ -357,8 +356,6 @@
     observer.DidProcessTask(*pending_task);
 
   task_execution_allowed_ = true;
-
-  current_pending_task_ = nullptr;
 }
 
 bool MessageLoop::DeferOrRunPendingTask(PendingTask pending_task) {
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 81d445f..f508e1a 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -288,9 +288,7 @@
   friend class internal::IncomingTaskQueue;
   friend class ScheduleWorkTest;
   friend class Thread;
-  friend struct PendingTask;
   FRIEND_TEST_ALL_PREFIXES(MessageLoopTest, DeleteUnboundLoop);
-  friend class PendingTaskTest;
 
   // Creates a MessageLoop without binding to a thread.
   // If |type| is TYPE_CUSTOM non-null |pump_factory| must be also given
@@ -365,13 +363,6 @@
 
   ObserverList<TaskObserver> task_observers_;
 
-  // Used to allow creating a breadcrumb of program counters in PostTask.
-  // This variable is only initialized while a task is being executed and is
-  // meant only to store context for creating a backtrace breadcrumb. Do not
-  // attach other semantics to it without thinking through the use caes
-  // thoroughly.
-  const PendingTask* current_pending_task_ = nullptr;
-
   scoped_refptr<internal::IncomingTaskQueue> incoming_task_queue_;
 
   // A task runner which we haven't bound to a thread yet.
diff --git a/base/message_loop/message_pump_for_io.h b/base/message_loop/message_pump_for_io.h
index 78a8096..6aac1e6 100644
--- a/base/message_loop/message_pump_for_io.h
+++ b/base/message_loop/message_pump_for_io.h
@@ -36,7 +36,7 @@
 #elif defined(OS_POSIX)
 using MessagePumpForIO = MessagePumpLibevent;
 #else
-#error Platform doesn't define MessagePumpForIO
+#error Platform does not define MessagePumpForIO
 #endif
 
 }  // namespace base
diff --git a/base/message_loop/message_pump_for_ui.h b/base/message_loop/message_pump_for_ui.h
index 09db352..2d3e175 100644
--- a/base/message_loop/message_pump_for_ui.h
+++ b/base/message_loop/message_pump_for_ui.h
@@ -47,7 +47,7 @@
 #elif defined(OS_FUCHSIA)
 using MessagePumpForUI = MessagePumpFuchsia;
 #else
-#error Platform doesn't define MessagePumpForUI
+#error Platform does not define MessagePumpForUI
 #endif
 
 }  // namespace base
diff --git a/base/pending_task.cc b/base/pending_task.cc
index 31f2d2d..7224a6b0 100644
--- a/base/pending_task.cc
+++ b/base/pending_task.cc
@@ -15,21 +15,7 @@
     : task(std::move(task)),
       posted_from(posted_from),
       delayed_run_time(delayed_run_time),
-      sequence_num(0),
-      nestable(nestable),
-      is_high_res(false) {
-  const PendingTask* parent_task =
-      MessageLoop::current() ? MessageLoop::current()->current_pending_task_
-                             : nullptr;
-  if (parent_task) {
-    task_backtrace[0] = parent_task->posted_from.program_counter();
-    std::copy(parent_task->task_backtrace.begin(),
-              parent_task->task_backtrace.end() - 1,
-              task_backtrace.begin() + 1);
-  } else {
-    task_backtrace.fill(nullptr);
-  }
-}
+      nestable(nestable) {}
 
 PendingTask::PendingTask(PendingTask&& other) = default;
 
diff --git a/base/pending_task.h b/base/pending_task.h
index 8c7854b..495015b 100644
--- a/base/pending_task.h
+++ b/base/pending_task.h
@@ -44,17 +44,18 @@
   // The time when the task should be run.
   base::TimeTicks delayed_run_time;
 
-  // Task backtrace.
-  std::array<const void*, 4> task_backtrace;
+  // Task backtrace. mutable so it can be set while annotating const PendingTask
+  // objects from TaskAnnotator::DidQueueTask().
+  mutable std::array<const void*, 4> task_backtrace = {};
 
   // Secondary sort key for run time.
-  int sequence_num;
+  int sequence_num = 0;
 
   // OK to dispatch from a nested loop.
   Nestable nestable;
 
   // Needs high resolution timers.
-  bool is_high_res;
+  bool is_high_res = false;
 };
 
 using TaskQueue = base::queue<PendingTask>;
diff --git a/base/pending_task_unittest.cc b/base/pending_task_unittest.cc
deleted file mode 100644
index 2a3e0c01..0000000
--- a/base/pending_task_unittest.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-// 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.
-
-#include "base/pending_task.h"
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-class PendingTaskTest : public ::testing::Test {
- public:
-  PendingTaskTest() = default;
-
-  ~PendingTaskTest() override = default;
-
- protected:
-  using ExpectedTrace = std::vector<const void*>;
-
-  static void VerifyTraceAndPost(const scoped_refptr<TaskRunner>& task_runner,
-                                 const Location& posted_from,
-                                 const Location& next_from_here,
-                                 const std::vector<const void*>& expected_trace,
-                                 Closure task) {
-    SCOPED_TRACE(StringPrintf("Callback Depth: %zu", expected_trace.size()));
-
-    // Beyond depth + 1, the trace is nonsensical because there haven't been
-    // enough nested tasks called.
-    const PendingTask* current_pending_task =
-        MessageLoop::current()->current_pending_task_;
-    size_t window = std::min(current_pending_task->task_backtrace.size(),
-                             expected_trace.size());
-
-    EXPECT_EQ(posted_from,
-              MessageLoop::current()->current_pending_task_->posted_from);
-    for (size_t i = 0; i < window; i++) {
-      SCOPED_TRACE(StringPrintf("Trace frame: %zu", i));
-      EXPECT_EQ(expected_trace[i], current_pending_task->task_backtrace[i]);
-    }
-    task_runner->PostTask(next_from_here, std::move(task));
-  }
-
-  static void RunTwo(Closure c1, Closure c2) {
-    c1.Run();
-    c2.Run();
-  }
-};
-
-// Ensure the task backtrace populates correctly.
-TEST_F(PendingTaskTest, SingleThreadedSimple) {
-  MessageLoop loop;
-  const Location& location0 = FROM_HERE;
-  const Location& location1 = FROM_HERE;
-  const Location& location2 = FROM_HERE;
-  const Location& location3 = FROM_HERE;
-  const Location& location4 = FROM_HERE;
-  const Location& location5 = FROM_HERE;
-
-  Closure task5 = Bind(
-      &PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location4,
-      location5,
-      ExpectedTrace({location3.program_counter(), location2.program_counter(),
-                     location1.program_counter(), location0.program_counter()}),
-      DoNothing());
-  Closure task4 = Bind(
-      &PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location3,
-      location4,
-      ExpectedTrace({location2.program_counter(), location1.program_counter(),
-                     location0.program_counter(), nullptr}),
-      task5);
-  Closure task3 = Bind(
-      &PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location2,
-      location3, ExpectedTrace({location1.program_counter(),
-                                location0.program_counter(), nullptr, nullptr}),
-      task4);
-  Closure task2 =
-      Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(), location1,
-           location2, ExpectedTrace({location0.program_counter()}), task3);
-  Closure task1 = Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(),
-                       location0, location1, ExpectedTrace({}), task2);
-
-  loop.task_runner()->PostTask(location0, task1);
-
-  RunLoop().RunUntilIdle();
-}
-
-// Post a task onto another thread. Ensure on the other thread, it has the
-// right stack trace.
-TEST_F(PendingTaskTest, MultipleThreads) {
-  MessageLoop loop;  // Implicitly "thread a."
-  Thread thread_b("pt_test_b");
-  Thread thread_c("pt_test_c");
-  thread_b.StartAndWaitForTesting();
-  thread_c.StartAndWaitForTesting();
-
-  const Location& location_a0 = FROM_HERE;
-  const Location& location_a1 = FROM_HERE;
-  const Location& location_a2 = FROM_HERE;
-  const Location& location_a3 = FROM_HERE;
-
-  const Location& location_b0 = FROM_HERE;
-  const Location& location_b1 = FROM_HERE;
-
-  const Location& location_c0 = FROM_HERE;
-
-  // On thread c, post a task back to thread a that verifies its trace
-  // and terminates after one more self-post.
-  Closure task_a2 =
-      Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(),
-           location_a2, location_a3,
-           ExpectedTrace(
-               {location_c0.program_counter(), location_b0.program_counter(),
-                location_a1.program_counter(), location_a0.program_counter()}),
-           DoNothing());
-  Closure task_c0 = Bind(&PendingTaskTest::VerifyTraceAndPost,
-                         loop.task_runner(), location_c0, location_a2,
-                         ExpectedTrace({location_b0.program_counter(),
-                                        location_a1.program_counter(),
-                                        location_a0.program_counter()}),
-                         task_a2);
-
-  // On thread b run two tasks that conceptually come from the same location
-  // (managed via RunTwo().) One will post back to thread b and another will
-  // post to thread c to test spawning multiple tasks on different message
-  // loops. The task posted to thread c will not get location b1 whereas the
-  // one posted back to thread b will.
-  Closure task_b0_fork =
-      Bind(&PendingTaskTest::VerifyTraceAndPost,
-           thread_c.message_loop()->task_runner(), location_b0, location_c0,
-           ExpectedTrace({location_a1.program_counter(),
-                          location_a0.program_counter(), nullptr}),
-           task_c0);
-  Closure task_b0_local =
-      Bind(&PendingTaskTest::VerifyTraceAndPost,
-           thread_b.message_loop()->task_runner(), location_b0, location_b1,
-           ExpectedTrace({location_a1.program_counter(),
-                          location_a0.program_counter(), nullptr}),
-           DoNothing());
-
-  // Push one frame onto the stack in thread a then pass to thread b.
-  Closure task_a1 =
-      Bind(&PendingTaskTest::VerifyTraceAndPost,
-           thread_b.message_loop()->task_runner(), location_a1, location_b0,
-           ExpectedTrace({location_a0.program_counter(), nullptr}),
-           Bind(&PendingTaskTest::RunTwo, task_b0_local, task_b0_fork));
-  Closure task_a0 =
-      Bind(&PendingTaskTest::VerifyTraceAndPost, loop.task_runner(),
-           location_a0, location_a1, ExpectedTrace({nullptr}), task_a1);
-
-  loop.task_runner()->PostTask(location_a0, task_a0);
-
-  RunLoop().RunUntilIdle();
-
-  thread_b.FlushForTesting();
-  thread_b.Stop();
-
-  thread_c.FlushForTesting();
-  thread_c.Stop();
-}
-
-}  // namespace base
diff --git a/base/sys_info.h b/base/sys_info.h
index a2f20c7..6e58715 100644
--- a/base/sys_info.h
+++ b/base/sys_info.h
@@ -64,16 +64,6 @@
   // on failure.
   static int64_t AmountOfTotalDiskSpace(const FilePath& path);
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-  // Returns the total number of inodes on the volume containing |path|, or -1
-  // on failure.
-  static int64_t AmountOfMaxDiskInode(const FilePath& path);
-
-  // Returns the number of inodes available to non-root on the volume containing
-  // |path|, or -1 on failure.
-  static int64_t AmountOfAvailableDiskInode(const FilePath& path);
-#endif
-
   // Returns system uptime.
   static TimeDelta Uptime();
 
diff --git a/base/sys_info_posix.cc b/base/sys_info_posix.cc
index 27de28a..f6fcd10 100644
--- a/base/sys_info_posix.cc
+++ b/base/sys_info_posix.cc
@@ -237,20 +237,4 @@
   return getpagesize();
 }
 
-#if !defined(OS_ANDROID)
-int64_t SysInfo::AmountOfMaxDiskInode(const FilePath& path) {
-  struct statvfs stats;
-  if (HANDLE_EINTR(statvfs(path.value().c_str(), &stats)) != 0)
-    return -1;
-  return static_cast<int64_t>(stats.f_files);
-}
-
-int64_t SysInfo::AmountOfAvailableDiskInode(const FilePath& path) {
-  struct statvfs stats;
-  if (HANDLE_EINTR(statvfs(path.value().c_str(), &stats)) != 0)
-    return -1;
-  return static_cast<int64_t>(stats.f_favail);
-}
-#endif  // !defined(OS_ANDROID)
-
 }  // namespace base
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index a78e40a..f72a302 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -8,7 +8,9 @@
 #include <string>
 #include <vector>
 
+#include "base/base_switches.h"
 #include "base/callback.h"
+#include "base/command_line.h"
 #include "base/json/json_writer.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
@@ -105,6 +107,19 @@
       kMaxBlockShutdownTasksPostedDuringShutdown, 50);
 }
 
+// Returns the maximum number of TaskPriority::BACKGROUND sequences that can be
+// scheduled concurrently based on command line flags.
+int GetMaxNumScheduledBackgroundSequences() {
+  // The CommandLine might not be initialized if TaskScheduler is initialized
+  // in a dynamic library which doesn't have access to argc/argv.
+  if (CommandLine::InitializedForCurrentProcess() &&
+      CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableBackgroundTasks)) {
+    return 0;
+  }
+  return std::numeric_limits<int>::max();
+}
+
 }  // namespace
 
 // Atomic internal state used by TaskTracker. Sequential consistency shouldn't
@@ -226,6 +241,9 @@
   DISALLOW_COPY_AND_ASSIGN(PreemptedBackgroundSequence);
 };
 
+TaskTracker::TaskTracker(StringPiece histogram_label)
+    : TaskTracker(histogram_label, GetMaxNumScheduledBackgroundSequences()) {}
+
 TaskTracker::TaskTracker(StringPiece histogram_label,
                          int max_num_scheduled_background_sequences)
     : state_(new State),
diff --git a/base/task_scheduler/task_tracker.h b/base/task_scheduler/task_tracker.h
index 2da33ff..37de128 100644
--- a/base/task_scheduler/task_tracker.h
+++ b/base/task_scheduler/task_tracker.h
@@ -87,12 +87,14 @@
 class BASE_EXPORT TaskTracker {
  public:
   // |histogram_label| is used as a suffix for histograms, it must not be empty.
-  // |max_num_scheduled_background_sequences| is the maximum number of
-  // background sequences that can be scheduled concurrently during normal
-  // execution (ignored during shutdown).
+  // The first constructor sets the maximum number of TaskPriority::BACKGROUND
+  // sequences that can be scheduled concurrently to 0 if the
+  // --disable-background-tasks flag is specified, max() otherwise. The second
+  // constructor sets it to |max_num_scheduled_background_sequences|.
+  TaskTracker(StringPiece histogram_label);
   TaskTracker(StringPiece histogram_label,
-              int max_num_scheduled_background_sequences =
-                  std::numeric_limits<int>::max());
+              int max_num_scheduled_background_sequences);
+
   virtual ~TaskTracker();
 
   // Synchronously shuts down the scheduler. Once this is called, only tasks
diff --git a/base/task_scheduler/task_tracker_posix.cc b/base/task_scheduler/task_tracker_posix.cc
index d929aac..8289d90 100644
--- a/base/task_scheduler/task_tracker_posix.cc
+++ b/base/task_scheduler/task_tracker_posix.cc
@@ -11,9 +11,7 @@
 namespace base {
 namespace internal {
 
-TaskTrackerPosix::TaskTrackerPosix(StringPiece name,
-                                   int max_num_scheduled_background_sequences)
-    : TaskTracker(name, max_num_scheduled_background_sequences) {}
+TaskTrackerPosix::TaskTrackerPosix(StringPiece name) : TaskTracker(name) {}
 TaskTrackerPosix::~TaskTrackerPosix() = default;
 
 void TaskTrackerPosix::RunOrSkipTask(Task task,
diff --git a/base/task_scheduler/task_tracker_posix.h b/base/task_scheduler/task_tracker_posix.h
index 60b7f370..4689f7a1 100644
--- a/base/task_scheduler/task_tracker_posix.h
+++ b/base/task_scheduler/task_tracker_posix.h
@@ -5,7 +5,6 @@
 #ifndef BASE_TASK_SCHEDULER_TASK_TRACKER_POSIX_H_
 #define BASE_TASK_SCHEDULER_TASK_TRACKER_POSIX_H_
 
-#include <limits>
 #include <memory>
 
 #include "base/base_export.h"
@@ -28,10 +27,7 @@
 // TaskTracker can run tasks.
 class BASE_EXPORT TaskTrackerPosix : public TaskTracker {
  public:
-  // This must match the signature of TaskTracker() to allow interchangeability.
-  TaskTrackerPosix(StringPiece name,
-                   int max_num_scheduled_background_sequences =
-                       std::numeric_limits<int>::max());
+  TaskTrackerPosix(StringPiece name);
   ~TaskTrackerPosix() override;
 
   // Sets the MessageLoopForIO with which to setup FileDescriptorWatcher in the
diff --git a/base/threading/scoped_blocking_call.h b/base/threading/scoped_blocking_call.h
index c8c4b36..e376c308 100644
--- a/base/threading/scoped_blocking_call.h
+++ b/base/threading/scoped_blocking_call.h
@@ -24,14 +24,54 @@
 class BlockingObserver;
 }
 
-// This class can be instantiated in a scope where a a blocking call (which
-// isn't using local computing resources -- e.g. a synchronous network request)
-// is made. Instantiation will hint the BlockingObserver for this thread about
-// the scope of the blocking operation.
+// This class must be instantiated in every scope where a blocking call is made.
+// CPU usage should be minimal within that scope. //base APIs that block
+// instantiate their own ScopedBlockingCall; it is not necessary to instantiate
+// another ScopedBlockingCall in the scope where these APIs are used.
 //
-// In particular, when instantiated from a TaskScheduler parallel or sequenced
-// task, this will allow the thread to be replaced in its pool (more or less
-// aggressively depending on BlockingType).
+// Good:
+//   Data data;
+//   {
+//     ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+//     data = GetDataFromNetwork();
+//   }
+//   CPUIntensiveProcessing(data);
+//
+// Bad:
+//   ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+//   Data data = GetDataFromNetwork();
+//   CPUIntensiveProcessing(data);  // CPU usage within a ScopedBlockingCall.
+//
+// Good:
+//   Data a;
+//   Data b;
+//   {
+//     ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
+//     a = GetDataFromMemoryCacheOrNetwork();
+//     b = GetDataFromMemoryCacheOrNetwork();
+//   }
+//   CPUIntensiveProcessing(a);
+//   CPUIntensiveProcessing(b);
+//
+// Bad:
+//   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
+//   Data a = GetDataFromMemoryCacheOrNetwork();
+//   Data b = GetDataFromMemoryCacheOrNetwork();
+//   CPUIntensiveProcessing(a);  // CPU usage within a ScopedBlockingCall.
+//   CPUIntensiveProcessing(b);  // CPU usage within a ScopedBlockingCall.
+//
+// Good:
+//   base::WaitableEvent waitable_event(...);
+//   waitable_event.Wait();
+//
+// Bad:
+//  base::WaitableEvent waitable_event(...);
+//  ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+//  waitable_event.Wait();  // Wait() instantiates its own ScopedBlockingCall.
+//
+// When a ScopedBlockingCall is instantiated from a TaskScheduler parallel or
+// sequenced task, the thread pool size is incremented to compensate for the
+// blocked thread (more or less aggressively depending on BlockingType).
 class BASE_EXPORT ScopedBlockingCall {
  public:
   ScopedBlockingCall(BlockingType blocking_type);
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index 142982a..f8eb453 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -94,15 +94,7 @@
       toolchain_uses_clang = is_clang
     }
 
-    if (toolchain_uses_clang && host_os != "win") {
-      # This toolchain definition uses response files for compilations.  GN uses
-      # the quoting rules of the host OS, while clang-cl always defaults to
-      # cmd.exe quoting rules for parsing response files.  Tell clang-cl to use
-      # POSIX quoting rules, so it can understand what GN generates.
-      cl = "${invoker.cl} --rsp-quoting=posix"
-    } else {
-      cl = invoker.cl
-    }
+    cl = invoker.cl
 
     if (toolchain_uses_clang && use_clang_static_analyzer) {
       analyzer_prefix =
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index ddfcd50..f830cb59 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1492,9 +1492,9 @@
     "//chrome/child",
     "//chrome/common",
     "//chrome/gpu",
-    "//chrome/profiling",
     "//chrome/renderer",
     "//chrome/utility",
+    "//components/services/heap_profiling",
     "//components/sync",
     "//content/public/child",
     "//pdf",
@@ -1820,10 +1820,10 @@
       "//chrome/child",
       "//chrome/common",
       "//chrome/gpu",
-      "//chrome/profiling",
       "//chrome/renderer",
       "//chrome/utility",
       "//components/safe_browsing/android:safe_browsing_mobile",
+      "//components/services/heap_profiling",
       "//content/public/app:both",
       "//content/public/common:service_names",
       "//services/service_manager/embedder",
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 9480185..4924aa2 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -645,10 +645,12 @@
       "javatests/src/org/chromium/chrome/browser/vr_shell/nfc_apk/SimNfcActivity.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/rules/ChromeTabbedActivityVrTestRule.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/rules/CustomTabActivityVrTestRule.java",
+      "javatests/src/org/chromium/chrome/browser/vr_shell/rules/HeadTrackingMode.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/rules/VrActivityRestriction.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/rules/VrActivityRestrictionRule.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/rules/VrTestRule.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/rules/WebappActivityVrTestRule.java",
+      "javatests/src/org/chromium/chrome/browser/vr_shell/util/HeadTrackingUtils.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/util/NfcSimUtils.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/util/TransitionUtils.java",
       "javatests/src/org/chromium/chrome/browser/vr_shell/util/VrInfoBarUtils.java",
diff --git a/chrome/android/java/res/drawable/ic_check_googblue_24dp_animated.xml b/chrome/android/java/res/drawable/ic_check_googblue_24dp_animated.xml
index d020b19..b775b23a 100644
--- a/chrome/android/java/res/drawable/ic_check_googblue_24dp_animated.xml
+++ b/chrome/android/java/res/drawable/ic_check_googblue_24dp_animated.xml
@@ -3,9 +3,10 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:aapt="http://schemas.android.com/aapt"
+    xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21">
 
     <aapt:attr name="android:drawable">
@@ -19,27 +20,19 @@
             <path
                 android:name="ic_check"
                 android:pathData="M4.12,12.705 l4.88,4.88 l11.295,-11.29"
-                android:strokeColor="#4285F4"
-                android:strokeWidth="2" />
+                android:strokeWidth="2"
+                android:strokeColor="#4285F4"/>
         </vector>
     </aapt:attr>
 
-    <target android:name="ic_check" >
+    <target android:name="ic_check">
         <aapt:attr name="android:animation">
-            <set>
-                <objectAnimator
-                    android:duration="200"
-                    android:propertyName="pathData"
-                    android:valueFrom="M4.12,12.705 l0,0 l0,0"
-                    android:valueTo="M4.12,12.705 l4.88,4.88 l0,0"
-                    android:valueType="pathType"/>
-                <objectAnimator
-                    android:duration="200"
-                    android:propertyName="pathData"
-                    android:valueFrom="M4.12,12.705 l4.88,4.88 l0,0"
-                    android:valueTo="M4.12,12.705 l4.88,4.88 l11.295,-11.29"
-                    android:valueType="pathType"/>
-            </set>
+            <objectAnimator
+                android:duration="400"
+                android:propertyName="trimPathEnd"
+                android:valueFrom="0"
+                android:valueTo="1"
+                tools:valueFrom="1"/>
         </aapt:attr>
     </target>
 </animated-vector>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/bottom_toolbar.xml b/chrome/android/java/res/layout/bottom_toolbar.xml
new file mode 100644
index 0000000..4e2fd4e
--- /dev/null
+++ b/chrome/android/java/res/layout/bottom_toolbar.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<org.chromium.chrome.browser.toolbar.ScrollingBottomViewResourceFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/bottom_toolbar_control_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/control_container_height" >
+
+    <ImageView
+        android:id="@+id/bottom_toolbar_top_shadow"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/toolbar_shadow_height"
+        android:src="@drawable/modern_toolbar_shadow"
+        android:scaleType="fitXY"
+        android:scaleY="-1"
+        android:contentDescription="@null" />
+
+    <org.chromium.chrome.browser.widget.bottomsheet.TouchRestrictingFrameLayout
+        android:id="@+id/bottom_toolbar_container"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/control_container_height"
+        android:layout_marginTop="@dimen/toolbar_shadow_height" >
+
+        <View
+            android:id="@+id/bottom_sheet_toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@color/modern_primary_color" >
+        </View>
+
+    </org.chromium.chrome.browser.widget.bottomsheet.TouchRestrictingFrameLayout>
+
+</org.chromium.chrome.browser.toolbar.ScrollingBottomViewResourceFrameLayout>
diff --git a/chrome/android/java/res/layout/main.xml b/chrome/android/java/res/layout/main.xml
index 5417220b..c30bacc2 100644
--- a/chrome/android/java/res/layout/main.xml
+++ b/chrome/android/java/res/layout/main.xml
@@ -86,6 +86,14 @@
             android:visibility="gone" />
 
         <ViewStub
+            android:id="@+id/bottom_toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|bottom"
+            android:inflatedId="@+id/bottom_toolbar"
+            android:layout="@layout/bottom_toolbar" />
+
+        <ViewStub
             android:id="@+id/control_container_stub"
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 4aaf9bb..6fa99d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -127,6 +127,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tabmodel.TabWindowManager;
+import org.chromium.chrome.browser.toolbar.BottomToolbarController;
 import org.chromium.chrome.browser.toolbar.Toolbar;
 import org.chromium.chrome.browser.toolbar.ToolbarControlContainer;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
@@ -258,6 +259,7 @@
     private ToolbarManager mToolbarManager;
     private FindToolbarManager mFindToolbarManager;
     private BottomSheetController mBottomSheetController;
+    private BottomToolbarController mBottomToolbarController;
     private BottomSheet mBottomSheet;
     private ContextualSuggestionsCoordinator mContextualSuggestionsCoordinator;
     private FadingBackgroundView mFadingBackgroundView;
@@ -1146,6 +1148,11 @@
             mBottomSheet = null;
         }
 
+        if (mBottomToolbarController != null) {
+            mBottomToolbarController.destroy();
+            mBottomToolbarController = null;
+        }
+
         if (mContextualSuggestionsCoordinator != null) {
             mContextualSuggestionsCoordinator.destroy();
             mContextualSuggestionsCoordinator = null;
@@ -1263,6 +1270,13 @@
         VrShellDelegate.onNativeLibraryAvailable();
         super.finishNativeInitialization();
 
+        if (FeatureUtilities.isChromeDuplexEnabled()) {
+            ViewGroup coordinator = findViewById(R.id.coordinator);
+            mBottomToolbarController = new BottomToolbarController(mFullscreenManager,
+                    mCompositorViewHolder.getResourceManager(),
+                    mCompositorViewHolder.getLayoutManager(), coordinator);
+        }
+
         if (FeatureUtilities.isContextualSuggestionsBottomSheetEnabled(isTablet())) {
             ViewGroup coordinator = (ViewGroup) findViewById(R.id.coordinator);
             getLayoutInflater().inflate(R.layout.bottom_sheet, coordinator);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
index e9b7402f..3dc8ba8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
@@ -142,6 +142,16 @@
     }
 
     /**
+     * Adds a {@link SceneOverlay} that can be shown in this layout to the first position in the
+     * scene overlay list, meaning it will be drawn behind all other overlays.
+     * @param overlay The {@link SceneOverlay} to be added.
+     */
+    void addSceneOverlayToBack(SceneOverlay overlay) {
+        assert !mSceneOverlays.contains(overlay);
+        mSceneOverlays.add(0, overlay);
+    }
+
+    /**
      * Adds a {@link SceneOverlay} that can potentially be shown on top of this {@link Layout}.  The
      * {@link SceneOverlay}s added to this {@link Layout} will be cascaded in the order they are
      * added.  The {@link SceneOverlay} added first will become the content of the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
index a4bb1d7..733e144 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -903,6 +903,15 @@
     }
 
     /**
+     * Add a {@link SceneOverlay} to the back of the list. This means the overlay will be drawn
+     * first and therefore behind all other overlays currently in the list.
+     * @param overlay The overlay to be added to the back of the list.
+     */
+    public void addSceneOverlayToBack(SceneOverlay overlay) {
+        mStaticLayout.addSceneOverlayToBack(overlay);
+    }
+
+    /**
      * Clears all content associated with {@code tabId} from the internal caches.
      * @param tabId The id of the tab to clear.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
index 070cb70..9684ac5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
@@ -35,6 +35,9 @@
     /** The current offset of the bottom view in px. */
     private int mCurrentOffsetPx;
 
+    /** The {@link ViewResourceFrameLayout} that this scene layer represents. */
+    private ViewResourceFrameLayout mBottomView;
+
     /**
      * Build a composited bottom view layer.
      * @param resourceManager A resource manager for dynamic resource creation.
@@ -43,10 +46,11 @@
      */
     public ScrollingBottomViewSceneLayer(ResourceManager resourceManager,
             ViewResourceFrameLayout bottomView, int topShadowHeightPx) {
-        mResourceId = bottomView.getId();
+        mBottomView = bottomView;
+        mResourceId = mBottomView.getId();
         mTopShadowHeightPx = topShadowHeightPx;
         resourceManager.getDynamicResourceLoader().registerResource(
-                mResourceId, bottomView.getResourceAdapter());
+                mResourceId, mBottomView.getResourceAdapter());
     }
 
     /**
@@ -82,7 +86,8 @@
 
     @Override
     public boolean isSceneOverlayTreeShowing() {
-        return true;
+        // If the offset is greater than the toolbar's height, don't draw the layer.
+        return mCurrentOffsetPx < mBottomView.getHeight() - mTopShadowHeightPx;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index ea2de9e2..8bfbe7c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -31,6 +31,9 @@
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
+import org.chromium.chrome.browser.vr_shell.OnExitVrRequestListener;
+import org.chromium.chrome.browser.vr_shell.VrIntentUtils;
+import org.chromium.chrome.browser.vr_shell.VrShellDelegate;
 import org.chromium.chrome.browser.widget.PromoDialog;
 import org.chromium.ui.base.PageTransition;
 
@@ -304,10 +307,31 @@
             return;
         }
 
-        showPromoDialog(dialogCreator);
+        if (VrIntentUtils.isVrIntent(activity.getIntent()) || VrShellDelegate.isInVr()) {
+            showPromoDialogForVr(dialogCreator, activity);
+        } else {
+            showPromoDialog(dialogCreator);
+        }
         mSearchEnginePromoShownThisSession = true;
     }
 
+    private void showPromoDialogForVr(Callable<PromoDialog> dialogCreator, Activity activity) {
+        VrShellDelegate.requestToExitVrForSearchEnginePromoDialog(new OnExitVrRequestListener() {
+            @Override
+            public void onSucceeded() {
+                showPromoDialog(dialogCreator);
+            }
+
+            @Override
+            public void onDenied() {
+                // We need to make sure that the dialog shows up even if user denied to
+                // leave VR.
+                VrShellDelegate.forceExitVrImmediately();
+                showPromoDialog(dialogCreator);
+            }
+        }, activity);
+    }
+
     private void showPromoDialog(Callable<PromoDialog> dialogCreator) {
         try {
             dialogCreator.call().show();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java
new file mode 100644
index 0000000..db63de0
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.modelutil;
+
+import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
+
+/**
+ * A model change processor for use with a {@link PropertyObservable} model. The
+ * {@link PropertyModelChangeProcessor} should be registered as a property observer of the model.
+ * Internally uses a view binder to bind model properties to the toolbar view.
+ * @param <M> The {@link PropertyObservable} model.
+ * @param <V> The view object that is changing.
+ * @param <P> The property of the view that changed.
+ */
+public class PropertyModelChangeProcessor<M extends PropertyObservable<P>, V, P>
+        implements PropertyObserver<P> {
+    /**
+     * A generic view binder that associates a view with a model.
+     * @param <M> The {@link PropertyObservable} model.
+     * @param <V> The view object that is changing.
+     * @param <P> The property of the view that changed.
+     */
+    public interface ViewBinder<M, V, P> { void bind(M model, V view, P propertyKey); }
+
+    private final V mView;
+    private final M mModel;
+    private final ViewBinder<M, V, P> mViewBinder;
+
+    /**
+     * Construct a new PropertyModelChangeProcessor.
+     * @param model The model containing the data to be bound.
+     * @param view The view to which data will be bound.
+     * @param viewBinder A class that binds the model to the view.
+     */
+    public PropertyModelChangeProcessor(M model, V view, ViewBinder<M, V, P> viewBinder) {
+        mModel = model;
+        mView = view;
+        mViewBinder = viewBinder;
+    }
+
+    @Override
+    public void onPropertyChanged(PropertyObservable<P> source, P propertyKey) {
+        // TODO(bauerb): Add support for batching and for full model updates.
+        mViewBinder.bind(mModel, mView, propertyKey);
+    }
+}
\ No newline at end of file
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 31c31f1..45a08d0e 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
@@ -102,6 +102,7 @@
     @Nullable
     private URI mCanDedupedApplicationId;
     private boolean mIsReadyToPayQueried;
+    private boolean mIsServiceConnected;
 
     /**
      * Builds the point of interaction with a locally installed 3rd party native Android payment
@@ -160,6 +161,7 @@
         mServiceConnection = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
+                mIsServiceConnected = true;
                 IsReadyToPayService isReadyToPayService =
                         IsReadyToPayService.Stub.asInterface(service);
                 if (isReadyToPayService == null) {
@@ -170,7 +172,9 @@
             }
 
             @Override
-            public void onServiceDisconnected(ComponentName name) {}
+            public void onServiceDisconnected(ComponentName name) {
+                mIsServiceConnected = false;
+            }
         };
 
         mIsReadyToPayIntent.putExtras(buildExtras(null /* id */, null /* merchantName */,
@@ -194,7 +198,10 @@
 
     private void respondToGetInstrumentsQuery(final PaymentInstrument instrument) {
         if (mServiceConnection != null) {
-            ContextUtils.getApplicationContext().unbindService(mServiceConnection);
+            if (mIsServiceConnected) {
+                ContextUtils.getApplicationContext().unbindService(mServiceConnection);
+                mIsServiceConnected = false;
+            }
             mServiceConnection = null;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarController.java
new file mode 100644
index 0000000..37713c2
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarController.java
@@ -0,0 +1,69 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
+import org.chromium.chrome.browser.compositor.scene_layer.ScrollingBottomViewSceneLayer;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.chrome.browser.toolbar.BottomToolbarModel.PropertyKey;
+import org.chromium.chrome.browser.toolbar.BottomToolbarViewBinder.ViewHolder;
+import org.chromium.ui.resources.ResourceManager;
+
+/**
+ * The controller for the bottom toolbar. This class handles all interactions that the bottom
+ * toolbar has with the outside world. This class has two primary components, an Android view that
+ * handles user actions and a composited texture that draws when the controls are being scrolled
+ * off-screen. The Android version does not draw unless the controls offset is 0.
+ */
+public class BottomToolbarController {
+    /** The mediator that handles events from outside the bottom toolbar. */
+    private BottomToolbarMediator mMediator;
+
+    /**
+     * Build the controller that manages the bottom toolbar.
+     * @param fullscreenManager A {@link ChromeFullscreenManager} to update the bottom controls
+     *                          height for the renderer.
+     * @param resourceManager A {@link ResourceManager} for loading textures into the compositor.
+     * @param layoutManager A {@link LayoutManager} to attach overlays to.
+     * @param root The root {@link ViewGroup} for locating the vies to inflate.
+     */
+    public BottomToolbarController(ChromeFullscreenManager fullscreenManager,
+            ResourceManager resourceManager, LayoutManager layoutManager, ViewGroup root) {
+        BottomToolbarModel model = new BottomToolbarModel();
+        mMediator = new BottomToolbarMediator(model, fullscreenManager, root.getResources());
+
+        int shadowHeight =
+                root.getResources().getDimensionPixelOffset(R.dimen.toolbar_shadow_height);
+
+        // This is the Android view component of the views that constitute the bottom toolbar.
+        View inflatedView = ((ViewStub) root.findViewById(R.id.bottom_toolbar)).inflate();
+        final ScrollingBottomViewResourceFrameLayout toolbarRoot =
+                (ScrollingBottomViewResourceFrameLayout) inflatedView;
+        toolbarRoot.setTopShadowHeight(shadowHeight);
+
+        // This is the compositor component of the bottom toolbar views.
+        final ScrollingBottomViewSceneLayer sceneLayer =
+                new ScrollingBottomViewSceneLayer(resourceManager, toolbarRoot, shadowHeight);
+        layoutManager.addSceneOverlayToBack(sceneLayer);
+
+        PropertyModelChangeProcessor<BottomToolbarModel, ViewHolder, PropertyKey> processor =
+                new PropertyModelChangeProcessor<>(model, new ViewHolder(sceneLayer, toolbarRoot),
+                        new BottomToolbarViewBinder());
+        model.addObserver(processor);
+    }
+
+    /**
+     * Clean up any state when the bottom toolbar is destroyed.
+     */
+    public void destroy() {
+        mMediator.destroy();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
new file mode 100644
index 0000000..64648a55
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar;
+
+import android.content.res.Resources;
+import android.view.View;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
+
+/**
+ * This class is responsible for reacting to events from the outside world, interacting with other
+ * coordinators, running most of the business logic associated with the bottom toolbar, and updating
+ * the model accordingly.
+ */
+class BottomToolbarMediator implements FullscreenListener {
+    /** The model for the bottom toolbar that holds all of its state. */
+    private BottomToolbarModel mModel;
+
+    /** The fullscreen manager to observe browser controls events. */
+    private ChromeFullscreenManager mFullscreenManager;
+
+    /**
+     * Build a new mediator that handles events from outside the bottom toolbar.
+     * @param model The {@link BottomToolbarModel} that holds all the state for the bottom toolbar.
+     * @param fullscreenManager A {@link ChromeFullscreenManager} for events related to the browser
+     *                          controls.
+     * @param resources Android {@link Resources} to pull dimensions from.
+     */
+    public BottomToolbarMediator(BottomToolbarModel model,
+            ChromeFullscreenManager fullscreenManager, Resources resources) {
+        mModel = model;
+        mFullscreenManager = fullscreenManager;
+        mFullscreenManager.addListener(this);
+
+        // Notify the fullscreen manager that the bottom controls now have a height.
+        fullscreenManager.setBottomControlsHeight(
+                resources.getDimensionPixelOffset(R.dimen.control_container_height));
+        fullscreenManager.updateViewportSize();
+    }
+
+    /**
+     * Clean up anything that needs to be when the bottom toolbar is destroyed.
+     */
+    public void destroy() {
+        mFullscreenManager.removeListener(this);
+    }
+
+    @Override
+    public void onContentOffsetChanged(float offset) {}
+
+    @Override
+    public void onControlsOffsetChanged(float topOffset, float bottomOffset, boolean needsAnimate) {
+        mModel.setYOffset((int) bottomOffset);
+        if (bottomOffset > 0) {
+            mModel.setAndroidViewVisibility(View.INVISIBLE);
+        } else {
+            mModel.setAndroidViewVisibility(View.VISIBLE);
+        }
+    }
+
+    @Override
+    public void onToggleOverlayVideoMode(boolean enabled) {}
+
+    @Override
+    public void onBottomControlsHeightChanged(int bottomControlsHeight) {}
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
new file mode 100644
index 0000000..6d4fe75
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar;
+
+import org.chromium.chrome.browser.modelutil.PropertyObservable;
+
+/**
+ * All of the state for the bottom toolbar, updated by the {@link BottomToolbarController}.
+ */
+public class BottomToolbarModel extends PropertyObservable<BottomToolbarModel.PropertyKey> {
+    /** The different properties that can change on the bottom toolbar. */
+    public static class PropertyKey {
+        public static final PropertyKey Y_OFFSET = new PropertyKey();
+        public static final PropertyKey ANDROID_VIEW_VISIBILITY = new PropertyKey();
+
+        private PropertyKey() {}
+    }
+
+    /** The Y offset of the view in px. */
+    private int mYOffsetPx;
+
+    /** The visibility of the Android view version of the toolbar. */
+    private int mAndroidViewVisibility;
+
+    /** Default constructor. */
+    public BottomToolbarModel() {}
+
+    /**
+     * @param offsetPx The current Y offset in px.
+     */
+    public void setYOffset(int offsetPx) {
+        mYOffsetPx = offsetPx;
+        notifyPropertyChanged(PropertyKey.Y_OFFSET);
+    }
+
+    /**
+     * @return The current Y offset in px.
+     */
+    public int getYOffset() {
+        return mYOffsetPx;
+    }
+
+    /**
+     * @param visibility The visibility of the Android view version of the toolbar.
+     */
+    public void setAndroidViewVisibility(int visibility) {
+        mAndroidViewVisibility = visibility;
+        notifyPropertyChanged(PropertyKey.ANDROID_VIEW_VISIBILITY);
+    }
+
+    /**
+     * @return The visibility of the Android view version of the toolbar.
+     */
+    public int getAndroidViewVisibility() {
+        return mAndroidViewVisibility;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
new file mode 100644
index 0000000..3005f00
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar;
+
+import android.view.ViewGroup;
+
+import org.chromium.chrome.browser.compositor.scene_layer.ScrollingBottomViewSceneLayer;
+import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
+import org.chromium.chrome.browser.toolbar.BottomToolbarModel.PropertyKey;
+
+/**
+ * This class is responsible for pushing updates to both the Android view and the compositor
+ * component of the bottom toolbar. These updates are pulled from the {@link BottomToolbarModel}
+ * when a notification of an update is received.
+ */
+public class BottomToolbarViewBinder
+        implements PropertyModelChangeProcessor.ViewBinder<BottomToolbarModel,
+                BottomToolbarViewBinder.ViewHolder, BottomToolbarModel.PropertyKey> {
+    /**
+     * A wrapper class that holds a {@link ViewGroup} (the toolbar view) and a composited layer to
+     * be used with the {@link BottomToolbarViewBinder}.
+     */
+    public static class ViewHolder {
+        /** A handle to the composited bottom toolbar layer. */
+        public final ScrollingBottomViewSceneLayer sceneLayer;
+
+        /** A handle to the Android {@link ViewGroup} version of the toolbar. */
+        public final ViewGroup toolbarRoot;
+
+        /**
+         * @param compositedSceneLayer The composited bottom toolbar view.
+         * @param toolbarRootView The Android {@link ViewGroup} toolbar.
+         */
+        public ViewHolder(
+                ScrollingBottomViewSceneLayer compositedSceneLayer, ViewGroup toolbarRootView) {
+            sceneLayer = compositedSceneLayer;
+            toolbarRoot = toolbarRootView;
+        }
+    }
+
+    /**
+     * Build a binder that handles interaction between the model and the views that make up the
+     * bottom toolbar.
+     */
+    public BottomToolbarViewBinder() {}
+
+    @Override
+    public final void bind(BottomToolbarModel model, ViewHolder view, PropertyKey propertyKey) {
+        if (PropertyKey.Y_OFFSET == propertyKey) {
+            view.sceneLayer.setYOffset(model.getYOffset());
+        } else if (PropertyKey.ANDROID_VIEW_VISIBILITY == propertyKey) {
+            view.toolbarRoot.setVisibility(model.getAndroidViewVisibility());
+        } else {
+            assert false : "Unhandled property detected in BottomToolbarViewBinder!";
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ScrollingBottomViewResourceFrameLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ScrollingBottomViewResourceFrameLayout.java
new file mode 100644
index 0000000..3eef70e0
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ScrollingBottomViewResourceFrameLayout.java
@@ -0,0 +1,61 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.browser.widget.ViewResourceFrameLayout;
+import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
+
+/**
+ * A {@link ViewResourceFrameLayout} that specifically handles redraw of the top shadow of the view
+ * it represents.
+ */
+public class ScrollingBottomViewResourceFrameLayout extends ViewResourceFrameLayout {
+    /** A cached rect to avoid extra allocations. */
+    private final Rect mCachedRect = new Rect();
+
+    /** The height of the shadow sitting above the bottom view in px. */
+    private int mTopShadowHeightPx;
+
+    public ScrollingBottomViewResourceFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected ViewResourceAdapter createResourceAdapter() {
+        return new ViewResourceAdapter(this) {
+            @Override
+            public void onCaptureStart(Canvas canvas, Rect dirtyRect) {
+                mCachedRect.set(dirtyRect);
+                if (mCachedRect.intersect(0, 0, getWidth(), mTopShadowHeightPx)) {
+                    canvas.save();
+
+                    // Clip the canvas to only the section of the dirty rect that contains the top
+                    // shadow of the view.
+                    canvas.clipRect(mCachedRect);
+
+                    // Clear the shadow so redrawing does not make it progressively darker.
+                    canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+                    canvas.restore();
+                }
+
+                super.onCaptureStart(canvas, dirtyRect);
+            }
+        };
+    }
+
+    /**
+     * @param height The height of the view's top shadow in px.
+     */
+    public void setTopShadowHeight(int height) {
+        mTopShadowHeightPx = height;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrInputConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrInputConnection.java
index a8c14e65..a84bac1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrInputConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrInputConnection.java
@@ -43,7 +43,7 @@
     public void requestTextState() {
         if (DEBUG_LOGS) Log.i(TAG, "requestTextState");
         InputConnection ic = mImeAdapter.getActiveInputConnection();
-        assert ic != null;
+        if (ic == null) return;
         if (mImeThreadResponseHandler == null) {
             mImeThreadResponseHandler = new Handler();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
index 166550f..784b11dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShell.java
@@ -6,8 +6,6 @@
 
 import android.widget.FrameLayout;
 
-import org.chromium.chrome.browser.tab.Tab;
-
 /**
  * Abstracts away the VrShell class, which may or may not be present at runtime depending on
  * compile flags.
@@ -16,8 +14,7 @@
     /**
      * Performs native VrShell initialization.
      */
-    void initializeNative(
-            Tab currentTab, boolean forWebVr, boolean webVrAutopresentationExpected, boolean inCct);
+    void initializeNative(boolean forWebVr, boolean webVrAutopresentationExpected, boolean inCct);
 
     /**
      * Pauses VrShell.
@@ -67,7 +64,7 @@
     /**
      * Requests to exit VR.
      */
-    void requestToExitVr(@UiUnsupportedMode int reason);
+    void requestToExitVr(@UiUnsupportedMode int reason, boolean showExitPromptBeforeDoff);
 
     /**
      *  Triggers VrShell to navigate forward.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index bbf4e260..b2f3f84 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -184,6 +184,7 @@
     // Listener to be called once we exited VR due to to an unsupported mode, e.g. the user clicked
     // the URL bar security icon.
     private OnExitVrRequestListener mOnExitVrRequestListener;
+    private Runnable mPendingExitVrRequest;
     private boolean mExitedDueToUnsupportedMode;
     private boolean mExitingCct;
     private boolean mPaused;
@@ -365,6 +366,11 @@
         sVrModeObservers.remove(observer);
     }
 
+    public static void forceExitVrImmediately() {
+        if (sInstance == null) return;
+        sInstance.shutdownVr(true, true);
+    }
+
     /**
      * See {@link Activity#onActivityResult}.
      */
@@ -521,6 +527,35 @@
         }
     }
 
+    public static void requestToExitVrForSearchEnginePromoDialog(
+            OnExitVrRequestListener listener, Activity activity) {
+        // When call site requests to exit VR, depend on the timing, Chrome may not in VR yet
+        // (Chrome only enter VR after onNewIntentWithNative is called in the cold start case).
+        // While not in VR, calling requestToExitVr would immediately notify listener that exit VR
+        // succeed (without showing DOFF screen). If call site decide to show 2D UI when exit VR
+        // succeeded, it leads to case that 2D UI is showing on top of VR when Chrome eventually
+        // enters VR. To prevent this from happening, we set mPendingExitVrRequest which should be
+        // executed at runPendingExitVrTask. runPendingExitVrTask is called after it is safe to
+        // request exit VR.
+        if (isInVr()) {
+            sInstance.requestToExitVrInternal(
+                    listener, UiUnsupportedMode.SEARCH_ENGINE_PROMO, false);
+        } else {
+            // Making sure that we response to this request as it is very important that search
+            // engine promo dialog isn't ignored due to VR.
+            assert VrIntentUtils.isVrIntent(activity.getIntent());
+            VrShellDelegate instance = getInstance();
+            if (instance == null) {
+                listener.onDenied();
+                return;
+            }
+            sInstance.mPendingExitVrRequest = () -> {
+                sInstance.requestToExitVrInternal(
+                        listener, UiUnsupportedMode.SEARCH_ENGINE_PROMO, false);
+            };
+        }
+    }
+
     public static void requestToExitVr(OnExitVrRequestListener listener) {
         requestToExitVr(listener, UiUnsupportedMode.GENERIC_UNSUPPORTED_FEATURE);
     }
@@ -544,7 +579,7 @@
             listener.onSucceeded();
             return;
         }
-        sInstance.requestToExitVrInternal(listener, reason);
+        sInstance.requestToExitVrInternal(listener, reason, true);
     }
 
     /**
@@ -617,10 +652,13 @@
     public static void maybeHandleVrIntentPreNative(ChromeActivity activity, Intent intent) {
         if (!VrIntentUtils.isVrIntent(intent)) return;
 
-        if (sInstance != null) sInstance.swapHostActivity(activity);
+        if (sInstance != null && !sInstance.mInternalIntentUsedToStartVr) {
+            sInstance.swapHostActivity(activity, false /* disableVrMode */);
+            // If the user has launched Chrome from the launcher, rather than resuming from the
+            // dashboard, we don't want to launch into presentation.
+            sInstance.exitWebVRAndClearState();
+        }
 
-        // If we're already in VR, nothing to do here.
-        if (sInstance != null && sInstance.mInVr) return;
         if (DEBUG_LOGS) Log.i(TAG, "maybeHandleVrIntentPreNative: preparing for transition");
 
         // We add a black overlay view so that we can show black while the VR UI is loading.
@@ -1016,16 +1054,9 @@
                 if (activity == mActivity) onStart();
                 break;
             case ActivityState.RESUMED:
-                if (mInVr && activity != mActivity) {
-                    if (mShowingDaydreamDoff) {
-                        onExitVrResult(true);
-                    } else {
-                        shutdownVr(true /* disableVrMode */, false /* stayingInChrome */);
-                    }
-                }
                 if (!activitySupportsPresentation(activity)) return;
                 if (!(activity instanceof ChromeActivity)) return;
-                swapHostActivity((ChromeActivity) activity);
+                swapHostActivity((ChromeActivity) activity, true /* disableVrMode */);
                 onResume();
                 break;
             default:
@@ -1035,9 +1066,10 @@
 
     // Called when an activity that supports VR is resumed, and attaches VrShellDelegate to that
     // activity.
-    private void swapHostActivity(ChromeActivity activity) {
+    private void swapHostActivity(ChromeActivity activity, boolean disableVrMode) {
         assert mActivity != null;
         if (mActivity == activity) return;
+        if (mInVr) shutdownVr(disableVrMode, false /* stayingInChrome */);
         mActivity = activity;
         mListeningForWebVrActivateBeforePause = false;
         if (mVrDaydreamApi != null) mVrDaydreamApi.close();
@@ -1196,8 +1228,8 @@
 
         addVrViews();
         boolean webVrMode = mRequestedWebVr || tentativeWebVrMode || mAutopresentWebVr;
-        mVrShell.initializeNative(mActivity.getActivityTab(), webVrMode, mAutopresentWebVr,
-                mActivity instanceof CustomTabActivity);
+        mVrShell.initializeNative(
+                webVrMode, mAutopresentWebVr, mActivity instanceof CustomTabActivity);
         mVrShell.setWebVrModeEnabled(webVrMode);
 
         // We're entering VR, but not in WebVr mode.
@@ -1237,19 +1269,19 @@
     }
 
     private void onVrIntent() {
-        if (mInVr) return;
-
         if (USE_HIDE_ANIMATION) mNeedsAnimationCancel = true;
-        mEnterVrOnStartup = true;
+        mInVrAtChromeLaunch = true;
+
+        assert !mInternalIntentUsedToStartVr;
+        nativeRecordVrStartAction(mNativeVrShellDelegate, VrStartAction.INTENT_LAUNCH);
+
+        if (mInVr) return;
 
         // TODO(mthiesse): Assuming we've gone through DON flow saves ~2 seconds on VR entry. See
         // the comments in enterVr(). This may not always be the case in the future, but for now
         // it's a reasonable assumption.
         mDonSucceeded = true;
-        mInVrAtChromeLaunch = true;
-
-        nativeRecordVrStartAction(mNativeVrShellDelegate, VrStartAction.INTENT_LAUNCH);
-
+        mEnterVrOnStartup = true;
         if (!mPaused) enterVrAfterDon();
     }
 
@@ -1353,6 +1385,20 @@
                 }
             }
         }
+        // If canceling entry animation started, the activity is paused first and then expect a
+        // onResume is called next. We don't want to disrupt this process by calling
+        // runPendingExitVrTask which will show DOFF sceeen. DOFF might trigger onStop before
+        // onResume which will crash Chrome. So if we know that we are canceling animation, we don't
+        // call runPendingExitVrTask.
+        if (!mCancellingEntryAnimation) {
+            runPendingExitVrTask();
+        }
+    }
+
+    private void runPendingExitVrTask() {
+        if (mPendingExitVrRequest == null) return;
+        mPendingExitVrRequest.run();
+        mPendingExitVrRequest = null;
     }
 
     @Override
@@ -1493,8 +1539,8 @@
         return ENTER_VR_REQUESTED;
     }
 
-    private void requestToExitVrInternal(
-            OnExitVrRequestListener listener, @UiUnsupportedMode int reason) {
+    private void requestToExitVrInternal(OnExitVrRequestListener listener,
+            @UiUnsupportedMode int reason, boolean showExitPromptBeforeDoff) {
         assert listener != null;
         // If we are currently processing another request, deny the request.
         if (mOnExitVrRequestListener != null) {
@@ -1502,8 +1548,15 @@
             return;
         }
         mOnExitVrRequestListener = listener;
-        mShowingExitVrPrompt = true;
-        mVrShell.requestToExitVr(reason);
+        mShowingExitVrPrompt = showExitPromptBeforeDoff;
+        mVrShell.requestToExitVr(reason, showExitPromptBeforeDoff);
+    }
+
+    private void exitWebVRAndClearState() {
+        exitWebVRPresent();
+        mAutopresentWebVr = false;
+        mRequestedWebVr = false;
+        mListeningForWebVrActivateBeforePause = false;
     }
 
     @CalledByNative
@@ -1602,12 +1655,15 @@
             });
         }
 
-        mCancellingEntryAnimation = false;
+        if (mCancellingEntryAnimation) {
+            // If we know this onResume is called after cancel animation finished, it is safe to
+            // request exit VR and show DOFF.
+            runPendingExitVrTask();
+            mCancellingEntryAnimation = false;
+        }
 
         if (mEnterVrOnStartup) {
             // This means that Chrome was started with a VR intent, so we should enter VR.
-            // TODO(crbug.com/776235): The launcher should ensure that the DON flow has been run
-            // prior to starting Chrome.
             assert !mProbablyInDon;
             if (DEBUG_LOGS) Log.i(TAG, "onResume: entering VR mode for VR intent");
             enterVrAfterDon();
@@ -1662,10 +1718,6 @@
         // are safe.
         if (mInVr) mVrShell.pause();
         if (mShowingDaydreamDoff || mProbablyInDon) return;
-
-        // TODO(mthiesse): When the user resumes Chrome in a 2D context, we don't want to tear down
-        // VR UI, so for now, exit VR.
-        shutdownVr(true /* disableVrMode */, false /* stayingInChrome */);
     }
 
     protected void onPause() {
@@ -1676,10 +1728,6 @@
         unregisterDaydreamIntent(mVrDaydreamApi);
         if (mVrSupportLevel == VrSupportLevel.VR_NOT_AVAILABLE) return;
 
-        // TODO(ymalik): We should be able to remove this if we handle it for multi-window in
-        // {@link onMultiWindowModeChanged} since we're calling it in onStop.
-        if (!mInVr && !mProbablyInDon) cancelPendingVrEntry();
-
         // When the active web page has a vrdisplayactivate event handler,
         // mListeningForWebVrActivate should be set to true, which means a vrdisplayactive event
         // should be fired once DON flow finished. However, DON flow will pause our activity,
@@ -1699,12 +1747,12 @@
         if (maybeCloseVrCct()) return;
         mStopped = false;
         if (mDonSucceeded) setWindowModeForVr();
+        if (mInVr && !mVrDaydreamApi.isInVrSession()) shutdownVr(true, false);
     }
 
     private void onStop() {
         if (DEBUG_LOGS) Log.i(TAG, "onStop");
         mStopped = true;
-        if (!mProbablyInDon) cancelPendingVrEntry();
         assert !mCancellingEntryAnimation;
     }
 
@@ -1890,10 +1938,10 @@
      */
     /* package */ Runnable getVrCloseButtonListener() {
         if (mCloseButtonListener != null) return mCloseButtonListener;
-        final boolean startedForAutopresentation = mAutopresentWebVr;
         mCloseButtonListener = new Runnable() {
             @Override
             public void run() {
+                boolean startedForAutopresentation = mAutopresentWebVr;
                 // Avoid launching DD home when we shutdown VR.
                 mAutopresentWebVr = false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index 75b9626b..7790f40 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -313,8 +313,8 @@
 
     @Override
     @TargetApi(Build.VERSION_CODES.N)
-    public void initializeNative(Tab currentTab, boolean forWebVr,
-            boolean webVrAutopresentationExpected, boolean inCct) {
+    public void initializeNative(
+            boolean forWebVr, boolean webVrAutopresentationExpected, boolean inCct) {
         Tab tab = mActivity.getActivityTab();
         if (mActivity.isInOverviewMode() || tab == null) {
             launchNTP();
@@ -356,7 +356,7 @@
                 getGvrApi().getNativeGvrContext(), mReprojectedRendering, displayWidthMeters,
                 displayHeightMeters, dm.widthPixels, dm.heightPixels, pauseContent);
 
-        swapToTab(currentTab);
+        swapToTab(tab);
         createTabList();
         mActivity.getTabModelSelector().addObserver(mTabModelSelectorObserver);
         createTabModelSelectorTabObserver();
@@ -908,8 +908,14 @@
     }
 
     @Override
-    public void requestToExitVr(@UiUnsupportedMode int reason) {
-        if (mNativeVrShell != 0) nativeRequestToExitVr(mNativeVrShell, reason);
+    public void requestToExitVr(@UiUnsupportedMode int reason, boolean showExitPromptBeforeDoff) {
+        if (mNativeVrShell == 0) return;
+        if (showExitPromptBeforeDoff) {
+            nativeRequestToExitVr(mNativeVrShell, reason);
+        } else {
+            nativeLogUnsupportedModeUserMetric(mNativeVrShell, reason);
+            mDelegate.onExitVrRequestResult(true);
+        }
     }
 
     @CalledByNative
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index a9e76b2..f36aa9d 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -673,6 +673,7 @@
   "java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java",
   "java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java",
   "java/src/org/chromium/chrome/browser/modelutil/ListObservable.java",
+  "java/src/org/chromium/chrome/browser/modelutil/PropertyModelChangeProcessor.java",
   "java/src/org/chromium/chrome/browser/modelutil/PropertyObservable.java",
   "java/src/org/chromium/chrome/browser/modelutil/RecyclerViewModelChangeProcessor.java",
   "java/src/org/chromium/chrome/browser/modelutil/RecyclerViewAdapter.java",
@@ -1271,10 +1272,15 @@
   "java/src/org/chromium/chrome/browser/tabmodel/document/StorageDelegate.java",
   "java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java",
   "java/src/org/chromium/chrome/browser/toolbar/ActionModeController.java",
+  "java/src/org/chromium/chrome/browser/toolbar/BottomToolbarController.java",
+  "java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java",
+  "java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java",
+  "java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java",
   "java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java",
   "java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbarAnimationDelegate.java",
   "java/src/org/chromium/chrome/browser/toolbar/HomePageButton.java",
   "java/src/org/chromium/chrome/browser/toolbar/KeyboardNavigationListener.java",
+  "java/src/org/chromium/chrome/browser/toolbar/ScrollingBottomViewResourceFrameLayout.java",
   "java/src/org/chromium/chrome/browser/toolbar/TabSwitcherCallout.java",
   "java/src/org/chromium/chrome/browser/toolbar/TabSwitcherDrawable.java",
   "java/src/org/chromium/chrome/browser/toolbar/Toolbar.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBlobUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBlobUrlTest.java
index 7398a9af..7ee1266f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBlobUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBlobUrlTest.java
@@ -33,7 +33,6 @@
         mPaymentRequestTestRule.openPageAndClickNode("buy");
         mPaymentRequestTestRule.assertWaitForPageScaleFactorMatch(2);
         mPaymentRequestTestRule.expectResultContains(
-                new String[] {"SecurityError: Failed to construct 'PaymentRequest': "
-                        + "Must be in a secure context"});
+                new String[] {"PaymentRequest is not defined"});
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDataUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDataUrlTest.java
index 1312e18e..f46c17f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDataUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestDataUrlTest.java
@@ -46,7 +46,6 @@
     public void test() throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.openPageAndClickNode("buy");
         mPaymentRequestTestRule.expectResultContains(
-                new String[] {"SecurityError: Failed to construct 'PaymentRequest': "
-                        + "Must be in a secure context"});
+                new String[] {"PaymentRequest is not defined"});
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellDialogTest.java
index 01d9564..c79ad76 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellDialogTest.java
@@ -27,6 +27,7 @@
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.vr_shell.rules.ChromeTabbedActivityVrTestRule;
+import org.chromium.chrome.browser.vr_shell.rules.HeadTrackingMode;
 import org.chromium.chrome.browser.vr_shell.util.TransitionUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 
@@ -95,6 +96,7 @@
     @Test
     @Manual
     @LargeTest
+    @HeadTrackingMode(HeadTrackingMode.SupportedMode.FROZEN)
     public void microphoneDialogTest() throws InterruptedException, TimeoutException {
         // Display audio permissions prompt.
         displayDialog(
@@ -110,6 +112,7 @@
     @Test
     @Manual
     @LargeTest
+    @HeadTrackingMode(HeadTrackingMode.SupportedMode.FROZEN)
     public void cameraDialogTest() throws InterruptedException, TimeoutException {
         // Display Camera permissions prompt.
         displayDialog(
@@ -125,6 +128,7 @@
     @Test
     @Manual
     @LargeTest
+    @HeadTrackingMode(HeadTrackingMode.SupportedMode.FROZEN)
     public void locationDialogTest() throws InterruptedException, TimeoutException {
         // Display Location permissions prompt.
         displayDialog("test_navigation_2d_page",
@@ -140,6 +144,7 @@
     @Test
     @Manual
     @LargeTest
+    @HeadTrackingMode(HeadTrackingMode.SupportedMode.FROZEN)
     public void notificationDialogTest() throws InterruptedException, TimeoutException {
         // Display Notification permissions prompt.
         displayDialog("test_navigation_2d_page", "Notification.requestPermission(()=>{})");
@@ -154,6 +159,7 @@
     @Test
     @Manual
     @LargeTest
+    @HeadTrackingMode(HeadTrackingMode.SupportedMode.FROZEN)
     public void midiDialogTest() throws InterruptedException, TimeoutException {
         // Display MIDI permissions prompt.
         displayDialog("test_navigation_2d_page", "navigator.requestMIDIAccess({sysex: true})");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/ChromeTabbedActivityVrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/ChromeTabbedActivityVrTestRule.java
index becc01ef..8a3fc20 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/ChromeTabbedActivityVrTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/ChromeTabbedActivityVrTestRule.java
@@ -9,6 +9,7 @@
 
 import org.chromium.chrome.browser.vr_shell.TestVrShellDelegate;
 import org.chromium.chrome.browser.vr_shell.rules.VrActivityRestriction.SupportedActivity;
+import org.chromium.chrome.browser.vr_shell.util.HeadTrackingUtils;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 
 /**
@@ -17,14 +18,22 @@
  */
 public class ChromeTabbedActivityVrTestRule
         extends ChromeTabbedActivityTestRule implements VrTestRule {
+    private boolean mTrackerDirty;
+
     @Override
-    public Statement apply(final Statement base, Description desc) {
+    public Statement apply(final Statement base, final Description desc) {
         return super.apply(new Statement() {
             @Override
             public void evaluate() throws Throwable {
+                HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation(
+                        ChromeTabbedActivityVrTestRule.this, desc);
                 startMainActivityOnBlankPage();
                 TestVrShellDelegate.createTestVrShellDelegate(getActivity());
-                base.evaluate();
+                try {
+                    base.evaluate();
+                } finally {
+                    if (isTrackerDirty()) HeadTrackingUtils.revertTracker();
+                }
             }
         }, desc);
     }
@@ -33,4 +42,14 @@
     public SupportedActivity getRestriction() {
         return SupportedActivity.CTA;
     }
+
+    @Override
+    public boolean isTrackerDirty() {
+        return mTrackerDirty;
+    }
+
+    @Override
+    public void setTrackerDirty() {
+        mTrackerDirty = true;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/CustomTabActivityVrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/CustomTabActivityVrTestRule.java
index d5caf40..801ba67 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/CustomTabActivityVrTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/CustomTabActivityVrTestRule.java
@@ -13,21 +13,30 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.vr_shell.TestVrShellDelegate;
 import org.chromium.chrome.browser.vr_shell.rules.VrActivityRestriction.SupportedActivity;
+import org.chromium.chrome.browser.vr_shell.util.HeadTrackingUtils;
 
 /**
  * VR extension of CustomTabActivityTestRule. Applies CustomTabActivityTestRule then
  * opens up a CustomTabActivity to a blank page.
  */
 public class CustomTabActivityVrTestRule extends CustomTabActivityTestRule implements VrTestRule {
+    private boolean mTrackerDirty;
+
     @Override
-    public Statement apply(final Statement base, Description desc) {
+    public Statement apply(final Statement base, final Description desc) {
         return super.apply(new Statement() {
             @Override
             public void evaluate() throws Throwable {
+                HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation(
+                        CustomTabActivityVrTestRule.this, desc);
                 startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent(
                         InstrumentationRegistry.getTargetContext(), "about:blank"));
                 TestVrShellDelegate.createTestVrShellDelegate(getActivity());
-                base.evaluate();
+                try {
+                    base.evaluate();
+                } finally {
+                    if (isTrackerDirty()) HeadTrackingUtils.revertTracker();
+                }
             }
         }, desc);
     }
@@ -36,4 +45,14 @@
     public SupportedActivity getRestriction() {
         return SupportedActivity.CCT;
     }
+
+    @Override
+    public boolean isTrackerDirty() {
+        return mTrackerDirty;
+    }
+
+    @Override
+    public void setTrackerDirty() {
+        mTrackerDirty = true;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/HeadTrackingMode.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/HeadTrackingMode.java
new file mode 100644
index 0000000..bcfb2de5
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/HeadTrackingMode.java
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation for setting the VrCore head tracking service's tracking mode during pre-test setup.
+ *
+ * The benefit of setting the mode this way instead of via HeadTrackingUtils during a test is that
+ * starting services is asynchronous with no good way of waiting until whatever the service does
+ * takes effect. When set during a test, the test must idle long enough to safely assume that the
+ * service has taken effect. When applied during test setup, the Chrome startup period acts as the
+ * wait, as Chrome startup is slow enough that it's safe to assume the service has started by the
+ * time Chrome is ready.
+ *
+ * For example, the following would cause a test to start with its head position locked looking
+ * straight forward:
+ *     <code>
+ *     @HeadTrackingMode(HeadTrackingMode.SupportedMode.FROZEN)
+ *     </code>
+ * If a test is not annotated with this, it will use whatever mode is currently set. This should
+ * usually be the normal, sensor-based tracker, but is not guaranteed.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HeadTrackingMode {
+    public enum SupportedMode {
+        FROZEN, // Locked looking straight forward.
+        SWEEP, // Rotates back and forth horizontally in a 180 degree arc.
+        ROTATE, // Rotates 360 degrees.
+        CIRCLE_STRAFE, // Rotates 360 degrees, and if 6DOF is supported, changes position.
+        MOTION_SICKNESS // Moves in a figure-eight-like pattern.
+    }
+
+    /**
+     * @return The supported mode.
+     */
+    public SupportedMode value();
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/VrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/VrTestRule.java
index 0bee04cb1..d3b8135 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/VrTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/VrTestRule.java
@@ -15,4 +15,14 @@
      * Get the VrActivityRestriction.SupportedActivity that this rule is restricted to running in.
      */
     public SupportedActivity getRestriction();
+
+    /**
+     * Whether the head tracking mode has been changed.
+     */
+    public boolean isTrackerDirty();
+
+    /**
+     * Tells the rule that the head tracking mode has been changed.
+     */
+    public void setTrackerDirty();
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/WebappActivityVrTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/WebappActivityVrTestRule.java
index ada20011..9dbb9525 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/WebappActivityVrTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/rules/WebappActivityVrTestRule.java
@@ -9,6 +9,7 @@
 
 import org.chromium.chrome.browser.vr_shell.TestVrShellDelegate;
 import org.chromium.chrome.browser.vr_shell.rules.VrActivityRestriction.SupportedActivity;
+import org.chromium.chrome.browser.vr_shell.util.HeadTrackingUtils;
 import org.chromium.chrome.browser.webapps.WebappActivityTestRule;
 
 /**
@@ -16,14 +17,22 @@
  * up a WebappActivity to a blank page.
  */
 public class WebappActivityVrTestRule extends WebappActivityTestRule implements VrTestRule {
+    private boolean mTrackerDirty;
+
     @Override
-    public Statement apply(final Statement base, Description desc) {
+    public Statement apply(final Statement base, final Description desc) {
         return super.apply(new Statement() {
             @Override
             public void evaluate() throws Throwable {
+                HeadTrackingUtils.checkForAndApplyHeadTrackingModeAnnotation(
+                        WebappActivityVrTestRule.this, desc);
                 startWebappActivity();
                 TestVrShellDelegate.createTestVrShellDelegate(getActivity());
-                base.evaluate();
+                try {
+                    base.evaluate();
+                } finally {
+                    if (isTrackerDirty()) HeadTrackingUtils.revertTracker();
+                }
             }
         }, desc);
     }
@@ -32,4 +41,14 @@
     public SupportedActivity getRestriction() {
         return SupportedActivity.WAA;
     }
+
+    @Override
+    public boolean isTrackerDirty() {
+        return mTrackerDirty;
+    }
+
+    @Override
+    public void setTrackerDirty() {
+        mTrackerDirty = true;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/HeadTrackingUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/HeadTrackingUtils.java
new file mode 100644
index 0000000..ac38f42d
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/HeadTrackingUtils.java
@@ -0,0 +1,209 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.vr_shell.util;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Assert;
+import org.junit.runner.Description;
+
+import org.chromium.chrome.browser.vr_shell.rules.HeadTrackingMode;
+import org.chromium.chrome.browser.vr_shell.rules.HeadTrackingMode.SupportedMode;
+import org.chromium.chrome.browser.vr_shell.rules.VrTestRule;
+
+import java.util.Arrays;
+
+/**
+ * Utility class for interacting with the VrCore head tracking service, which allows fake head
+ * poses to be submitted instead of using actual sensor data.
+ *
+ * Requires that either the O2 rendering path is enabled or EnableVrCoreHeadTracking is set to true
+ * in the shared prefs file in order to actually work.
+ */
+public class HeadTrackingUtils {
+    private static final ComponentName HEAD_TRACKING_COMPONENT = new ComponentName(
+            "com.google.vr.vrcore", "com.google.vr.vrcore.tracking.HeadTrackingService");
+
+    private static final String ACTION_SET_TRACKER_TYPE = "com.google.vr.vrcore.SET_TRACKER_TYPE";
+    private static final String ACTION_SET_FAKE_TRACKER_MODE =
+            "com.google.vr.vrcore.SET_FAKE_TRACKER_MODE";
+    private static final String ACTION_SET_FAKE_TRACKER_POSE =
+            "com.google.vr.vrcore.SET_FAKE_TRACKER_POSE";
+    private static final String EXTRA_FAKE_TRACKER_MODE = "com.google.vr.vrcore.FAKE_TRACKER_MODE";
+    private static final String EXTRA_FAKE_TRACKER_POSE = "com.google.vr.vrcore.FAKE_TRACKER_POSE";
+    private static final String EXTRA_TRACKER_TYPE = "com.google.vr.vrcore.TRACKER_TYPE";
+
+    private static final int HEAD_TRACKING_APPLICATION_DELAY_MS = 500;
+
+    /**
+     * Class for holding data necessary to set the head tracking service's head pose to an
+     * arbitarary, static value. Contains either a quaternion or set of rotation Euler angles
+     * describing the direction to look in and an optional position in room space if 6DOF is
+     * supported.
+     */
+    public static class FakePose {
+        private float[] mQuaternion;
+        private float[] mRotationEulerAngles;
+        private float[] mRoomSpacePosition;
+
+        public FakePose setQuaternion(float x, float y, float z, float w) {
+            mQuaternion = new float[] {x, y, z, w};
+            mRotationEulerAngles = null;
+            return this;
+        }
+
+        public FakePose setRotationEulerAngles(float rollDeg, float pitchDeg, float yawDeg) {
+            mRotationEulerAngles = new float[] {rollDeg, pitchDeg, yawDeg};
+            mQuaternion = null;
+            return this;
+        }
+
+        public FakePose setRoomSpacePosition(float x, float y, float z) {
+            mRoomSpacePosition = new float[] {x, y, z};
+            return this;
+        }
+
+        public FakePose clearRoomSpacePosition() {
+            mRoomSpacePosition = null;
+            return this;
+        }
+
+        public float[] getDataForExtra() {
+            if (mQuaternion == null && mRotationEulerAngles == null) {
+                throw new IllegalArgumentException(
+                        "Tried to get FakePose data without setting either quaternion/angle data");
+            }
+            float[] orientationArray =
+                    mRotationEulerAngles == null ? mQuaternion : mRotationEulerAngles;
+            if (mRoomSpacePosition == null) return orientationArray;
+            float[] combinedArray = Arrays.copyOf(
+                    orientationArray, orientationArray.length + mRoomSpacePosition.length);
+            for (int i = 0; i < mRoomSpacePosition.length; i++) {
+                combinedArray[i + orientationArray.length] = mRoomSpacePosition[i];
+            }
+            return combinedArray;
+        }
+    }
+
+    /**
+     * Checks for the presence of a HeadTrackingMode annotation, and if found, sets the tracking
+     * mode to the specified value. If no annotation is found, the tracking mode is left at whatever
+     * the existing value is.
+     *
+     * @param rule The VrTestRule used by the current test case.
+     * @param desc The JUnit4 Description for the current test case.
+     */
+    public static void checkForAndApplyHeadTrackingModeAnnotation(
+            VrTestRule rule, Description desc) {
+        // Check if the test has a HeadTrackingMode annotation
+        HeadTrackingMode annotation = desc.getAnnotation(HeadTrackingMode.class);
+        if (annotation == null) return;
+        applyHeadTrackingModeInternal(rule, annotation.value());
+    }
+
+    /**
+     * Sets the tracker type to the given mode and waits long enough to safely assume that the
+     * service has started.
+     *
+     * @param rule The VrTestRule used by the current test case.
+     * @param mode The HeadTrackingMode.SupportedMode value to set the fake head tracker mode to.
+     */
+    public static void applyHeadTrackingMode(VrTestRule rule, SupportedMode mode) {
+        applyHeadTrackingModeInternal(rule, mode);
+        // TODO(bsheedy): Remove this sleep if the head tracking service ever exposes a way to be
+        // notified when a setting has been applied.
+        SystemClock.sleep(HEAD_TRACKING_APPLICATION_DELAY_MS);
+    }
+
+    /**
+     * Sets the head pose to the pose described by the given FakePose and waits long enough to
+     * safely assume that the pose has taken effect.
+     *
+     * @param rule The VrTestRule used by the current test case.
+     * @param pose The FakePose instance containing the pose data that will be sent to the head
+     *     tracking service.
+     */
+    public static void setHeadPose(VrTestRule rule, FakePose pose) {
+        restartHeadTrackingServiceIfNecessary(rule);
+        // Set the head pose to the given value
+        Intent poseIntent = new Intent(ACTION_SET_FAKE_TRACKER_POSE);
+        poseIntent.putExtra(EXTRA_FAKE_TRACKER_POSE, pose.getDataForExtra());
+        poseIntent.setComponent(HEAD_TRACKING_COMPONENT);
+        Assert.assertTrue(InstrumentationRegistry.getContext().startService(poseIntent) != null);
+        rule.setTrackerDirty();
+        // TODO(bsheedy): Remove this sleep. Could either expose poses up to Java and wait until
+        // we receive a pose that's the same as the one we set or see if the head tracking service
+        // adds the requested functionality of sending a notification when it's done applying
+        // settings.
+        SystemClock.sleep(HEAD_TRACKING_APPLICATION_DELAY_MS);
+    }
+
+    /**
+     * Reverts the tracking type back to values that a regular user would have (using real sensor
+     * data).
+     *
+     * Only meant to be called by rules after a test has run. Reseting the tracker to use sensor
+     * data during a test technically works, but messes up orientation if done while still in VR.
+     * until VR is exited and re-entered.
+     */
+    public static void revertTracker() {
+        Intent typeIntent = new Intent(ACTION_SET_TRACKER_TYPE);
+        typeIntent.putExtra(EXTRA_TRACKER_TYPE, "sensor");
+        typeIntent.setComponent(HEAD_TRACKING_COMPONENT);
+        InstrumentationRegistry.getContext().startService(typeIntent);
+    }
+
+    public static String supportedModeToString(SupportedMode mode) {
+        switch (mode) {
+            case FROZEN:
+                return "frozen";
+            case SWEEP:
+                return "sweep";
+            case ROTATE:
+                return "rotate";
+            case CIRCLE_STRAFE:
+                return "circle_strafe";
+            case MOTION_SICKNESS:
+                return "motion_sickness";
+            default:
+                return "unknown_mode";
+        }
+    }
+
+    private static void applyHeadTrackingModeInternal(VrTestRule rule, SupportedMode mode) {
+        restartHeadTrackingServiceIfNecessary(rule);
+        // Set the fake tracker mode to the given value.
+        Intent modeIntent = new Intent(ACTION_SET_FAKE_TRACKER_MODE);
+        modeIntent.putExtra(EXTRA_FAKE_TRACKER_MODE, supportedModeToString(mode));
+        modeIntent.setComponent(HEAD_TRACKING_COMPONENT);
+        Assert.assertTrue(InstrumentationRegistry.getContext().startService(modeIntent) != null);
+        rule.setTrackerDirty();
+    }
+
+    private static void restartHeadTrackingServiceIfNecessary(VrTestRule rule) {
+        // If the tracker has already been dirtied, then we can assume that the tracker type
+        // has already been set to "fake".
+        if (rule.isTrackerDirty()) return;
+
+        // VR sessions from previous tests can somehow interfere with the setting of the tracker
+        // type, even if said previous sessions did not touch the head tracking service. Killing
+        // the service before attempting to set the tracker type appears to work around this
+        // issue.
+        // TODO(https://crbug.com/829127): Remove this once the root cause is fixed.
+        Intent stopIntent = new Intent();
+        stopIntent.setComponent(HEAD_TRACKING_COMPONENT);
+        InstrumentationRegistry.getContext().stopService(stopIntent);
+
+        // Set the tracker tracker type to "fake".
+        Intent typeIntent = new Intent(ACTION_SET_TRACKER_TYPE);
+        typeIntent.putExtra(EXTRA_TRACKER_TYPE, "fake");
+        typeIntent.setComponent(HEAD_TRACKING_COMPONENT);
+        Assert.assertTrue(InstrumentationRegistry.getContext().startService(typeIntent) != null);
+        rule.setTrackerDirty();
+    }
+}
\ No newline at end of file
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 6a4237b..d66d411 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -386,7 +386,7 @@
 
 chrome_packaged_services = [
   ":chrome_manifest",
-  "//chrome/profiling:manifest",
+  "//components/services/heap_profiling:manifest",
   "//components/services/patch:manifest",
   "//components/services/unzip:manifest",
   "//chrome/services/file_util:manifest",
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 8b4cb14..4c92453 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10619,18 +10619,6 @@
       <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup when it is from a Chrome extension.">
         "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to pair
       </message>
-      <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a website.">
-        <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect
-      </message>
-      <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a Chrome extension.">
-        "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect
-      </message>
-      <message name="IDS_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no USB devices were found matching the requirements that the application provided.">
-        No compatible devices found.
-      </message>
-      <message name="IDS_DEVICE_CHOOSER_DEVICE_NAME_WITH_ID" desc="To distinguish devices with the same name, the device chooser shows the device name with id.">
-        <ph name="DEVICE_NAME">$1<ex>device name</ex></ph> (<ph name="DEVICE_ID">$2<ex>device id</ex></ph>)
-      </message>
       <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no Bluetooth devices were found matching the requirements that the application provided.">
         No compatible devices found.
       </message>
@@ -10658,9 +10646,6 @@
       <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PAIR_BUTTON_TEXT" desc="Label on the button that closes the Bluetooth chooser popup and pairs the selected device.">
         Pair
       </message>
-      <message name="IDS_USB_DEVICE_CHOOSER_CONNECT_BUTTON_TEXT" desc="Label on the button that closes the USB chooser popup and connects the selected device.">
-        Connect
-      </message>
       <message name="IDS_DEVICE_CHOOSER_CANCEL_BUTTON_TEXT" desc="Label on the button that closes the chooser popup without selecting an option.">
         Cancel
       </message>
@@ -10677,6 +10662,21 @@
         <ph name="DEVICE_NAME">$1<ex>device name</ex></ph> - Paired
       </message>
     </if>
+    <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a website.">
+      <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect
+    </message>
+    <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a Chrome extension.">
+      "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect
+    </message>
+    <message name="IDS_DEVICE_CHOOSER_DEVICE_NAME_WITH_ID" desc="To distinguish devices with the same name, the device chooser shows the device name with id.">
+      <ph name="DEVICE_NAME">$1<ex>device name</ex></ph> (<ph name="DEVICE_ID">$2<ex>device id</ex></ph>)
+    </message>
+    <message name="IDS_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no USB devices were found matching the requirements that the application provided.">
+      No compatible devices found.
+    </message>
+    <message name="IDS_USB_DEVICE_CHOOSER_CONNECT_BUTTON_TEXT" desc="Label on the button that closes the USB chooser popup and connects the selected device.">
+      Connect
+    </message>
 
     <!-- Device Chooser Device Name Unknown -->
     <message name="IDS_DEVICE_CHOOSER_DEVICE_NAME_UNKNOWN_DEVICE_WITH_VENDOR_NAME" desc="String describing an unknown device by its vendor name.">
@@ -10877,9 +10877,6 @@
       <message name="IDS_PRESS_APP_TO_EXIT" desc="Text displayed in a transient bubble to tell users how to return from presentation mode (where the WebVR page occupies the entire screen) to normal mode.">
         Press App button to exit
       </message>
-      <message name="IDS_PRESS_APP_TO_EXIT_FULLSCREEN" desc="Text displayed in a transient bubble to tell users how to return from cinema mode (where the page displayed in a cinema like environment).">
-        Press App button to exit fullscreen
-      </message>
       <message name="IDS_VR_SHELL_SITE_IS_TRACKING_LOCATION" desc="Text displayed in a transient bubble to inform the user that the page is tracking location.">
         Site is tracking your location
       </message>
diff --git a/chrome/app/nibs/InfoBar.xib b/chrome/app/nibs/InfoBar.xib
index 95b2216..5f506ec9 100644
--- a/chrome/app/nibs/InfoBar.xib
+++ b/chrome/app/nibs/InfoBar.xib
@@ -23,7 +23,7 @@
             <rect key="frame" x="0.0" y="0.0" width="480" height="48"/>
             <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
             <subviews>
-                <customView id="1" customClass="InfoBarGradientView">
+                <customView id="1" customClass="InfoBarBackgroundView">
                     <rect key="frame" x="0.0" y="0.0" width="480" height="48"/>
                     <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
                     <subviews>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 51bb02e..7661789 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -226,6 +226,8 @@
     "budget_service/budget_service_impl.h",
     "cache_stats_recorder.cc",
     "cache_stats_recorder.h",
+    "chooser_controller/chooser_controller.cc",
+    "chooser_controller/chooser_controller.h",
     "chrome_browser_application_mac.h",
     "chrome_browser_application_mac.mm",
     "chrome_browser_field_trials.cc",
@@ -1510,10 +1512,14 @@
     "usb/usb_chooser_context.h",
     "usb/usb_chooser_context_factory.cc",
     "usb/usb_chooser_context_factory.h",
+    "usb/usb_chooser_controller.cc",
+    "usb/usb_chooser_controller.h",
     "usb/usb_tab_helper.cc",
     "usb/usb_tab_helper.h",
     "usb/usb_util.cc",
     "usb/usb_util.h",
+    "usb/web_usb_chooser_service.cc",
+    "usb/web_usb_chooser_service.h",
     "usb/web_usb_histograms.cc",
     "usb/web_usb_histograms.h",
     "usb/web_usb_permission_provider.cc",
@@ -2377,8 +2383,6 @@
       "bookmarks/bookmark_html_writer.cc",
       "bookmarks/bookmark_html_writer.h",
       "certificate_viewer.h",
-      "chooser_controller/chooser_controller.cc",
-      "chooser_controller/chooser_controller.h",
       "chrome_browser_field_trials_desktop.cc",
       "chrome_browser_field_trials_desktop.h",
       "chrome_browser_main_posix.cc",
@@ -2776,10 +2780,6 @@
       "upgrade_detector.cc",
       "upgrade_detector.h",
       "upgrade_observer.h",
-      "usb/usb_chooser_controller.cc",
-      "usb/usb_chooser_controller.h",
-      "usb/web_usb_chooser_service.cc",
-      "usb/web_usb_chooser_service.h",
       "usb/web_usb_chooser_service_desktop.cc",
       "usb/web_usb_chooser_service_desktop.h",
       "usb/web_usb_detector.cc",
diff --git a/chrome/browser/android/data_usage/data_use_tab_helper.cc b/chrome/browser/android/data_usage/data_use_tab_helper.cc
index 5b16cde..2bfe421 100644
--- a/chrome/browser/android/data_usage/data_use_tab_helper.cc
+++ b/chrome/browser/android/data_usage/data_use_tab_helper.cc
@@ -44,8 +44,8 @@
   android::DataUseUITabModel* data_use_ui_tab_model =
       android::DataUseUITabModelFactory::GetForBrowserContext(
           Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-  SessionID::id_type tab_id = SessionTabHelper::IdForTab(web_contents());
-  if (!data_use_ui_tab_model || tab_id < 0)
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents());
+  if (!data_use_ui_tab_model || !tab_id.is_valid())
     return;
 
   // The last committed navigation entry should correspond to the current
@@ -74,7 +74,7 @@
   android::DataUseUITabModel* data_use_ui_tab_model =
       android::DataUseUITabModelFactory::GetForBrowserContext(
           Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-  SessionID::id_type tab_id = SessionTabHelper::IdForTab(web_contents());
-  if (data_use_ui_tab_model && tab_id >= 0)
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents());
+  if (data_use_ui_tab_model && tab_id.is_valid())
     data_use_ui_tab_model->ReportTabClosure(tab_id);
 }
diff --git a/chrome/browser/android/data_usage/data_use_tab_model.cc b/chrome/browser/android/data_usage/data_use_tab_model.cc
index 91b961e..27e5d1b 100644
--- a/chrome/browser/android/data_usage/data_use_tab_model.cc
+++ b/chrome/browser/android/data_usage/data_use_tab_model.cc
@@ -75,11 +75,6 @@
   END_REASON_MAX = 4
 };
 
-// Returns true if |tab_id| is a valid tab ID.
-bool IsValidTabID(SessionID::id_type tab_id) {
-  return tab_id >= 0;
-}
-
 // Returns various parameters from the values specified in the field trial.
 size_t GetMaxTabEntries() {
   size_t max_tab_entries = kDefaultMaxTabEntries;
@@ -250,13 +245,13 @@
 }
 
 void DataUseTabModel::OnNavigationEvent(
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     TransitionType transition,
     const GURL& url,
     const std::string& package,
     content::NavigationEntry* navigation_entry) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsValidTabID(tab_id));
+  DCHECK(tab_id.is_valid());
   DCHECK(!navigation_entry || (navigation_entry->GetURL() == url));
 
   std::string current_label, new_label;
@@ -286,9 +281,9 @@
   }
 }
 
-void DataUseTabModel::OnTabCloseEvent(SessionID::id_type tab_id) {
+void DataUseTabModel::OnTabCloseEvent(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsValidTabID(tab_id));
+  DCHECK(tab_id.is_valid());
 
   TabEntryMap::iterator tab_entry_iterator = active_tabs_.find(tab_id);
   if (tab_entry_iterator == active_tabs_.end())
@@ -306,7 +301,7 @@
 }
 
 bool DataUseTabModel::GetTrackingInfoForTabAtTime(
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     const base::TimeTicks timestamp,
     TrackingInfo* output_tracking_info) const {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -314,7 +309,7 @@
   output_tracking_info->label = "";
 
   // Data use that cannot be attributed to a tab will not be labeled.
-  if (!IsValidTabID(tab_id))
+  if (!tab_id.is_valid())
     return false;
 
   TabEntryMap::const_iterator tab_entry_iterator = active_tabs_.find(tab_id);
@@ -334,12 +329,12 @@
 }
 
 bool DataUseTabModel::WouldNavigationEventEndTracking(
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     TransitionType transition,
     const GURL& url,
     const content::NavigationEntry* navigation_entry) const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsValidTabID(tab_id));
+  DCHECK(tab_id.is_valid());
   std::string current_label, new_label;
   bool is_package_match;
   GetCurrentAndNewLabelForNavigationEvent(
@@ -402,22 +397,20 @@
   return tick_clock_->NowTicks();
 }
 
-bool DataUseTabModel::IsCustomTabPackageMatch(SessionID::id_type tab_id) const {
+bool DataUseTabModel::IsCustomTabPackageMatch(SessionID tab_id) const {
   DCHECK(thread_checker_.CalledOnValidThread());
   TabEntryMap::const_iterator tab_entry_iterator = active_tabs_.find(tab_id);
   return (tab_entry_iterator != active_tabs_.end()) &&
          tab_entry_iterator->second.is_custom_tab_package_match();
 }
 
-void DataUseTabModel::NotifyObserversOfTrackingStarting(
-    SessionID::id_type tab_id) {
+void DataUseTabModel::NotifyObserversOfTrackingStarting(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
   for (TabDataUseObserver& observer : observers_)
     observer.NotifyTrackingStarting(tab_id);
 }
 
-void DataUseTabModel::NotifyObserversOfTrackingEnding(
-    SessionID::id_type tab_id) {
+void DataUseTabModel::NotifyObserversOfTrackingEnding(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
   for (TabDataUseObserver& observer : observers_)
     observer.NotifyTrackingEnding(tab_id);
@@ -430,7 +423,7 @@
 }
 
 void DataUseTabModel::GetCurrentAndNewLabelForNavigationEvent(
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     TransitionType transition,
     const GURL& url,
     const std::string& package,
@@ -439,7 +432,7 @@
     std::string* new_label,
     bool* is_package_match) const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(IsValidTabID(tab_id));
+  DCHECK(tab_id.is_valid());
 
   TabEntryMap::const_iterator tab_entry_iterator = active_tabs_.find(tab_id);
   *current_label =
@@ -520,7 +513,7 @@
 }
 
 void DataUseTabModel::StartTrackingDataUse(TransitionType transition,
-                                           SessionID::id_type tab_id,
+                                           SessionID tab_id,
                                            const std::string& label,
                                            bool is_custom_tab_package_match) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -550,7 +543,7 @@
 }
 
 void DataUseTabModel::EndTrackingDataUse(TransitionType transition,
-                                         SessionID::id_type tab_id) {
+                                         SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   TabEntryMap::iterator tab_entry_iterator = active_tabs_.find(tab_id);
diff --git a/chrome/browser/android/data_usage/data_use_tab_model.h b/chrome/browser/android/data_usage/data_use_tab_model.h
index 7b51b04..fc5320f4 100644
--- a/chrome/browser/android/data_usage/data_use_tab_model.h
+++ b/chrome/browser/android/data_usage/data_use_tab_model.h
@@ -90,8 +90,8 @@
     virtual ~TabDataUseObserver() {}
 
     // Notification callbacks when tab tracking sessions are started and ended.
-    virtual void NotifyTrackingStarting(SessionID::id_type tab_id) = 0;
-    virtual void NotifyTrackingEnding(SessionID::id_type tab_id) = 0;
+    virtual void NotifyTrackingStarting(SessionID tab_id) = 0;
+    virtual void NotifyTrackingEnding(SessionID tab_id) = 0;
 
     // Notification callback that DataUseTabModel is ready to process the UI
     // navigation events.
@@ -124,7 +124,7 @@
   // |navigation_entry| can be null in some cases where it cannot be retrieved
   // such as buffered navigation events or when support for back-forward
   // navigations is not needed such as custom tab navigation.
-  void OnNavigationEvent(SessionID::id_type tab_id,
+  void OnNavigationEvent(SessionID tab_id,
                          TransitionType transition,
                          const GURL& url,
                          const std::string& package,
@@ -133,7 +133,7 @@
   // Notifies the DataUseTabModel that tab with |tab_id| is closed. Any active
   // tracking sessions for the tab are terminated, and the tab is marked as
   // closed.
-  void OnTabCloseEvent(SessionID::id_type tab_id);
+  void OnTabCloseEvent(SessionID tab_id);
 
   // Notifies the DataUseTabModel that tracking label |label| is removed. Any
   // active tracking sessions with the label are ended, without notifying any of
@@ -146,7 +146,7 @@
   // |output_tracking_info| is populated with its information. Otherwise,
   // returns false.
   virtual bool GetTrackingInfoForTabAtTime(
-      SessionID::id_type tab_id,
+      SessionID tab_id,
       base::TimeTicks timestamp,
       TrackingInfo* output_tracking_info) const;
 
@@ -156,7 +156,7 @@
   // navigation entry of the current navigation in back-forward navigation
   // history.
   bool WouldNavigationEventEndTracking(
-      SessionID::id_type tab_id,
+      SessionID tab_id,
       TransitionType transition,
       const GURL& url,
       const content::NavigationEntry* navigation_entry) const;
@@ -193,7 +193,7 @@
 
   // Returns true if the |tab_id| is a custom tab and started tracking due to
   // package name match.
-  bool IsCustomTabPackageMatch(SessionID::id_type tab_id) const;
+  bool IsCustomTabPackageMatch(SessionID tab_id) const;
 
   // Returns true if DataUseTabModel is ready to process UI navigation events.
   bool is_ready_for_navigation_event() const {
@@ -203,11 +203,11 @@
  protected:
   // Notifies the observers that a data usage tracking session started for
   // |tab_id|. Protected for testing.
-  void NotifyObserversOfTrackingStarting(SessionID::id_type tab_id);
+  void NotifyObserversOfTrackingStarting(SessionID tab_id);
 
   // Notifies the observers that an active data usage tracking session ended for
   // |tab_id|. Protected for testing.
-  void NotifyObserversOfTrackingEnding(SessionID::id_type tab_id);
+  void NotifyObserversOfTrackingEnding(SessionID tab_id);
 
   // Notifies the observers that DataUseTabModel is ready to process navigation
   // events.
@@ -237,7 +237,7 @@
   FRIEND_TEST_ALL_PREFIXES(ExternalDataUseObserverTest,
                            MatchingRuleFetchOnControlAppInstall);
 
-  using TabEntryMap = std::map<SessionID::id_type, TabDataUseEntry>;
+  using TabEntryMap = std::map<SessionID, TabDataUseEntry>;
 
   // Gets the current label of a tab, and the new label if a navigation event
   // occurs in the tab. |tab_id| is the source tab of the generated event,
@@ -252,7 +252,7 @@
   // tracking session if the navigation happens. |is_package_match| will be set
   // to true if a tracking session will start due to package name match.
   void GetCurrentAndNewLabelForNavigationEvent(
-      SessionID::id_type tab_id,
+      SessionID tab_id,
       TransitionType transition,
       const GURL& url,
       const std::string& package,
@@ -265,12 +265,12 @@
   // |is_custom_tab_package_match| is true if |tab_id| is a custom tab and
   // started tracking due to package name match.
   void StartTrackingDataUse(TransitionType transition,
-                            SessionID::id_type tab_id,
+                            SessionID tab_id,
                             const std::string& label,
                             bool is_custom_tab_package_match);
 
   // Ends the current tracking session for tab with id |tab_id|.
-  void EndTrackingDataUse(TransitionType transition, SessionID::id_type tab_id);
+  void EndTrackingDataUse(TransitionType transition, SessionID tab_id);
 
   // Compacts the tab entry map |active_tabs_| by removing expired tab entries.
   // After removing expired tab entries, if the size of |active_tabs_| exceeds
diff --git a/chrome/browser/android/data_usage/data_use_tab_model_unittest.cc b/chrome/browser/android/data_usage/data_use_tab_model_unittest.cc
index a5f7960..1e04844 100644
--- a/chrome/browser/android/data_usage/data_use_tab_model_unittest.cc
+++ b/chrome/browser/android/data_usage/data_use_tab_model_unittest.cc
@@ -44,9 +44,9 @@
 const char kTestLabel2[] = "label_2";
 const char kTestLabel3[] = "label_3";
 
-const int kTabID1 = 1;
-const int kTabID2 = 2;
-const int kTabID3 = 3;
+const SessionID kTabID1 = SessionID::FromSerializedValue(1);
+const SessionID kTabID2 = SessionID::FromSerializedValue(2);
+const SessionID kTabID3 = SessionID::FromSerializedValue(3);
 
 const char kURLFoo[] = "http://foo.com/";
 const char kURLBar[] = "http://bar.com/";
@@ -71,8 +71,8 @@
 class MockTabDataUseObserver
     : public android::DataUseTabModel::TabDataUseObserver {
  public:
-  MOCK_METHOD1(NotifyTrackingStarting, void(SessionID::id_type tab_id));
-  MOCK_METHOD1(NotifyTrackingEnding, void(SessionID::id_type tab_id));
+  MOCK_METHOD1(NotifyTrackingStarting, void(SessionID tab_id));
+  MOCK_METHOD1(NotifyTrackingEnding, void(SessionID tab_id));
   MOCK_METHOD0(OnDataUseTabModelReady, void());
 };
 
@@ -130,7 +130,7 @@
   }
 
   // Returns true if tab entry for |tab_id| exists in |active_tabs_|.
-  bool IsTabEntryExists(SessionID::id_type tab_id) const {
+  bool IsTabEntryExists(SessionID tab_id) const {
     return data_use_tab_model_->active_tabs_.find(tab_id) !=
            data_use_tab_model_->active_tabs_.end();
   }
@@ -143,7 +143,7 @@
 
   // Returns true if |tab_id| is a custom tab and started tracking due to
   // package match.
-  bool IsCustomTabPackageMatch(SessionID::id_type tab_id) const {
+  bool IsCustomTabPackageMatch(SessionID tab_id) const {
     auto tab_entry_iterator = data_use_tab_model_->active_tabs_.find(tab_id);
     return (tab_entry_iterator != data_use_tab_model_->active_tabs_.end()) &&
            tab_entry_iterator->second.is_custom_tab_package_match();
@@ -151,7 +151,7 @@
 
   // Returns true if the tracking session for tab with id |tab_id| is currently
   // active.
-  bool IsTrackingDataUse(SessionID::id_type tab_id) const {
+  bool IsTrackingDataUse(SessionID tab_id) const {
     const auto& tab_entry_iterator =
         data_use_tab_model_->active_tabs_.find(tab_id);
     if (tab_entry_iterator == data_use_tab_model_->active_tabs_.end())
@@ -161,7 +161,7 @@
 
   // Checks if the DataUse object for the given |tab_id| with request start time
   // |at_time| is labeled as an empty tracking info.
-  void ExpectEmptyTrackingInfoAtTime(SessionID::id_type tab_id,
+  void ExpectEmptyTrackingInfoAtTime(SessionID tab_id,
                                      const base::TimeTicks& at_time) const {
     ExpectTrackingInfoAtTimeWithReturn(tab_id, at_time, false,
                                        DataUseTabModel::TrackingInfo());
@@ -169,14 +169,14 @@
 
   // Checks if the DataUse object for the given |tab_id| is labeled as an empty
   // tracking info.
-  void ExpectEmptyTrackingInfo(SessionID::id_type tab_id) {
+  void ExpectEmptyTrackingInfo(SessionID tab_id) {
     ExpectTrackingInfoAtTimeWithReturn(tab_id, tick_clock_.NowTicks(), false,
                                        DataUseTabModel::TrackingInfo());
   }
 
   // Checks if the DataUse object for given |tab_id| is labeled as
   // |expected_label| with custom tab indicated by |expected_is_custom_tab|.
-  void ExpectTrackingInfo(SessionID::id_type tab_id,
+  void ExpectTrackingInfo(SessionID tab_id,
                           const std::string& expected_label,
                           const std::string& expected_tag) {
     DataUseTabModel::TrackingInfo expected_tracking_info;
@@ -191,7 +191,7 @@
   // DataUse object for |tab_id| with request start time |at_time|, as
   // |expected_tracking_info| and returns |expected_return|.
   void ExpectTrackingInfoAtTimeWithReturn(
-      SessionID::id_type tab_id,
+      SessionID tab_id,
       const base::TimeTicks& at_time,
       bool expected_return,
       const DataUseTabModel::TrackingInfo& expected_tracking_info) const {
@@ -205,13 +205,12 @@
     }
   }
 
-  void StartTrackingDataUse(SessionID::id_type tab_id,
-                            const std::string& label) {
+  void StartTrackingDataUse(SessionID tab_id, const std::string& label) {
     data_use_tab_model_->StartTrackingDataUse(
         DataUseTabModel::TRANSITION_OMNIBOX_SEARCH, tab_id, label, false);
   }
 
-  void EndTrackingDataUse(SessionID::id_type tab_id) {
+  void EndTrackingDataUse(SessionID tab_id) {
     data_use_tab_model_->EndTrackingDataUse(
         DataUseTabModel::TRANSITION_OMNIBOX_SEARCH, tab_id);
   }
@@ -424,29 +423,31 @@
 TEST_F(DataUseTabModelTest, CompactTabEntriesWithinMaxLimit) {
   const int32_t max_tab_entries =
       static_cast<int32_t>(data_use_tab_model_->max_tab_entries_);
-  SessionID::id_type tab_id = 1;
+  std::list<SessionID> tab_ids;
 
   ExpectTabEntrySize(TabEntrySize::ZERO);
 
-  while (tab_id <= max_tab_entries) {
-    std::string tab_label = base::StringPrintf("label_%d", tab_id);
+  for (int i = 0; i < max_tab_entries; ++i) {
+    SessionID tab_id = SessionID::NewUnique();
+    tab_ids.push_back(tab_id);
+    std::string tab_label = base::StringPrintf("label_%d", tab_id.id());
     StartTrackingDataUse(tab_id, tab_label);
     tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
     EndTrackingDataUse(tab_id);
     tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
 
-    ExpectTabEntrySize(tab_id);
-    ++tab_id;
+    ExpectTabEntrySize(i + 1);
   }
 
-  // Oldest tab entry that will be removed first.
-  SessionID::id_type oldest_tab_id = 1;
-
   // Starting and ending more tracking tab entries does not increase the size of
   // |active_tabs_|.
-  while (tab_id < max_tab_entries + 10) {
+  for (int i = 0; i < 10; ++i) {
+    SessionID oldest_tab_id = tab_ids.front();
+
     EXPECT_TRUE(IsTabEntryExists(oldest_tab_id));
-    std::string tab_label = base::StringPrintf("label_%d", tab_id);
+    SessionID tab_id = SessionID::NewUnique();
+    tab_ids.push_back(tab_id);
+    std::string tab_label = base::StringPrintf("label_%d", tab_id.id());
     StartTrackingDataUse(tab_id, tab_label);
     tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
     EndTrackingDataUse(tab_id);
@@ -456,15 +457,17 @@
     EXPECT_FALSE(IsTabEntryExists(oldest_tab_id));
     ExpectTabEntrySize(max_tab_entries);
 
-    ++tab_id;
-    ++oldest_tab_id;  // next oldest tab entry.
+    tab_ids.pop_front();  // next oldest tab entry.
   }
 
   // Starting and not ending more tracking tab entries does not increase the
   // size of |active_tabs_|.
-  while (tab_id < max_tab_entries + 20) {
+  for (int i = 0; i < 10; ++i) {
+    SessionID oldest_tab_id = tab_ids.front();
     EXPECT_TRUE(IsTabEntryExists(oldest_tab_id));
-    std::string tab_label = base::StringPrintf("label_%d", tab_id);
+    SessionID tab_id = SessionID::NewUnique();
+    tab_ids.push_back(tab_id);
+    std::string tab_label = base::StringPrintf("label_%d", tab_id.id());
     StartTrackingDataUse(tab_id, tab_label);
     tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
 
@@ -472,8 +475,7 @@
     EXPECT_FALSE(IsTabEntryExists(oldest_tab_id));
     ExpectTabEntrySize(max_tab_entries);
 
-    ++tab_id;
-    ++oldest_tab_id;  // next oldest tab entry.
+    tab_ids.pop_front();  // next oldest tab entry.
   }
 }
 
@@ -513,20 +515,20 @@
   base::HistogramTester histogram_tester;
   const int32_t max_tab_entries =
       static_cast<int32_t>(data_use_tab_model_->max_tab_entries_);
-  SessionID::id_type tab_id = 1;
 
-  while (tab_id <= max_tab_entries) {
-    std::string tab_label = base::StringPrintf("label_%d", tab_id);
+  for (int i = 0; i < max_tab_entries; ++i) {
+    SessionID tab_id = SessionID::NewUnique();
+    std::string tab_label = base::StringPrintf("label_%d", tab_id.id());
     StartTrackingDataUse(tab_id, tab_label);
     EndTrackingDataUse(tab_id);
-    ++tab_id;
   }
 
   // Fast forward 10 minutes.
   tick_clock_.Advance(base::TimeDelta::FromMinutes(10));
 
   // Adding another tab entry triggers CompactTabEntries.
-  std::string tab_label = base::StringPrintf("label_%d", tab_id);
+  SessionID tab_id = SessionID::NewUnique();
+  std::string tab_label = base::StringPrintf("label_%d", tab_id.id());
   StartTrackingDataUse(tab_id, tab_label);
   EndTrackingDataUse(tab_id);
 
@@ -653,7 +655,6 @@
       {DataUseTabModel::TRANSITION_RELOAD, kURLFoo, std::string(), kTestLabel2},
   };
   std::vector<std::string> app_package_names, domain_regexes, labels;
-  SessionID::id_type tab_id = 1;
 
   app_package_names.push_back(kPackageFoo);
   domain_regexes.push_back(std::string());
@@ -667,7 +668,9 @@
 
   RegisterURLRegexes(app_package_names, domain_regexes, labels);
 
+  int num_tabs = 0;
   for (const auto& test : all_enter_transition_tests) {
+    SessionID tab_id = SessionID::NewUnique();
     EXPECT_FALSE(IsTrackingDataUse(tab_id));
     ExpectEmptyTrackingInfo(tab_id);
 
@@ -692,11 +695,9 @@
           tab_id, DataUseTabModel::TRANSITION_OMNIBOX_NAVIGATION, GURL(kURLBar),
           navigation_entry_bar.get()));
     }
-    ExpectTabEntrySize(tab_id);
+    ExpectTabEntrySize(++num_tabs);
     EXPECT_EQ(test.transition == DataUseTabModel::TRANSITION_CUSTOM_TAB,
               IsCustomTabPackageMatch(tab_id));
-
-    ++tab_id;
   }
 }
 
@@ -712,7 +713,6 @@
       {DataUseTabModel::TRANSITION_OMNIBOX_SEARCH, kURLFooBar},
   };
   std::vector<std::string> app_package_names, domain_regexes, labels;
-  SessionID::id_type tab_id = 1;
 
   app_package_names.push_back(std::string());
   domain_regexes.push_back(kURLFoo);
@@ -720,7 +720,9 @@
 
   RegisterURLRegexes(app_package_names, domain_regexes, labels);
 
+  int num_tabs = 0;
   for (const auto& test : all_exit_transition_tests) {
+    SessionID tab_id = SessionID::NewUnique();
     EXPECT_FALSE(IsTrackingDataUse(tab_id));
     auto navigation_entry = CreateNavigationEntry(kURLFoo);
     data_use_tab_model_->OnNavigationEvent(
@@ -741,8 +743,7 @@
     ExpectDataUseLabelInNavigationEntry(*navigation_entry, std::string());
 
     EXPECT_FALSE(IsTrackingDataUse(tab_id));
-    ExpectTabEntrySize(tab_id);
-    ++tab_id;
+    ExpectTabEntrySize(++num_tabs);
   }
 }
 
@@ -753,7 +754,6 @@
       DataUseTabModel::TRANSITION_OMNIBOX_SEARCH,
       DataUseTabModel::TRANSITION_OMNIBOX_NAVIGATION};
   std::vector<std::string> app_package_names, domain_regexes, labels;
-  SessionID::id_type tab_id = 1;
 
   app_package_names.push_back(std::string());
   domain_regexes.push_back(kURLFoo);
@@ -761,7 +761,9 @@
 
   RegisterURLRegexes(app_package_names, domain_regexes, labels);
 
+  int num_tabs = 0;
   for (const auto& transition : all_test_transitions) {
+    SessionID tab_id = SessionID::NewUnique();
     EXPECT_FALSE(IsTrackingDataUse(tab_id));
     auto navigation_entry = CreateNavigationEntry(kURLFoo);
     data_use_tab_model_->OnNavigationEvent(tab_id, transition, GURL(kURLFoo),
@@ -793,8 +795,7 @@
         tab_id, transition, GURL(), std::string(), navigation_entry.get());
 
     EXPECT_FALSE(IsTrackingDataUse(tab_id));
-    ExpectTabEntrySize(tab_id);
-    ++tab_id;
+    ExpectTabEntrySize(++num_tabs);
   }
 }
 
diff --git a/chrome/browser/android/data_usage/data_use_tab_ui_manager_android.cc b/chrome/browser/android/data_usage/data_use_tab_ui_manager_android.cc
index b7ed836..8c828b7 100644
--- a/chrome/browser/android/data_usage/data_use_tab_ui_manager_android.cc
+++ b/chrome/browser/android/data_usage/data_use_tab_ui_manager_android.cc
@@ -69,7 +69,8 @@
     const JavaParamRef<jclass>& clazz,
     jint tab_id,
     const JavaParamRef<jobject>& jprofile) {
-  DCHECK_LE(0, static_cast<SessionID::id_type>(tab_id));
+  SessionID casted_tab_id = SessionID::FromSerializedValue(tab_id);
+  DCHECK(casted_tab_id.is_valid());
 
   Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
   android::DataUseUITabModel* data_use_ui_tab_model =
@@ -78,7 +79,7 @@
     return false;
 
   return data_use_ui_tab_model->CheckAndResetDataUseTrackingStarted(
-      static_cast<SessionID::id_type>(tab_id));
+      casted_tab_id);
 }
 
 // static
@@ -87,7 +88,8 @@
     const JavaParamRef<jclass>& clazz,
     jint tab_id,
     const JavaParamRef<jobject>& jprofile) {
-  DCHECK_LE(0, static_cast<SessionID::id_type>(tab_id));
+  SessionID casted_tab_id = SessionID::FromSerializedValue(tab_id);
+  DCHECK(casted_tab_id.is_valid());
 
   Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
   android::DataUseUITabModel* data_use_ui_tab_model =
@@ -96,7 +98,7 @@
     return false;
 
   return data_use_ui_tab_model->CheckAndResetDataUseTrackingEnded(
-      static_cast<SessionID::id_type>(tab_id));
+      casted_tab_id);
 }
 
 // static
@@ -105,7 +107,8 @@
     const JavaParamRef<jclass>& clazz,
     jint tab_id,
     const JavaParamRef<jobject>& jprofile) {
-  DCHECK_LE(0, static_cast<SessionID::id_type>(tab_id));
+  SessionID casted_tab_id = SessionID::FromSerializedValue(tab_id);
+  DCHECK(casted_tab_id.is_valid());
 
   Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
   android::DataUseUITabModel* data_use_ui_tab_model =
@@ -113,8 +116,7 @@
   if (!data_use_ui_tab_model)
     return;
 
-  data_use_ui_tab_model->UserClickedContinueOnDialogBox(
-      static_cast<SessionID::id_type>(tab_id));
+  data_use_ui_tab_model->UserClickedContinueOnDialogBox(casted_tab_id);
 }
 
 // static
@@ -126,7 +128,8 @@
     const JavaParamRef<jstring>& url,
     jint transition_type,
     const JavaParamRef<jobject>& jprofile) {
-  DCHECK_LE(0, static_cast<SessionID::id_type>(tab_id));
+  SessionID casted_tab_id = SessionID::FromSerializedValue(tab_id);
+  DCHECK(casted_tab_id.is_valid());
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(j_web_contents);
   DCHECK(web_contents);
@@ -138,8 +141,7 @@
     return false;
 
   return data_use_ui_tab_model->WouldDataUseTrackingEnd(
-      ConvertJavaStringToUTF8(env, url), transition_type,
-      static_cast<SessionID::id_type>(tab_id),
+      ConvertJavaStringToUTF8(env, url), transition_type, casted_tab_id,
       web_contents->GetController().GetPendingEntry());
 }
 
@@ -151,7 +153,8 @@
     const JavaParamRef<jstring>& jpackage_name,
     const JavaParamRef<jstring>& jurl,
     const JavaParamRef<jobject>& jprofile) {
-  DCHECK_LE(0, static_cast<SessionID::id_type>(tab_id));
+  SessionID casted_tab_id = SessionID::FromSerializedValue(tab_id);
+  DCHECK(casted_tab_id.is_valid());
 
   Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
   android::DataUseUITabModel* data_use_ui_tab_model =
@@ -167,8 +170,8 @@
   if (!jpackage_name.is_null())
     ConvertJavaStringToUTF8(env, jpackage_name, &package_name);
 
-  data_use_ui_tab_model->ReportCustomTabInitialNavigation(
-      static_cast<SessionID::id_type>(tab_id), package_name, url);
+  data_use_ui_tab_model->ReportCustomTabInitialNavigation(casted_tab_id,
+                                                          package_name, url);
 }
 
 // static
diff --git a/chrome/browser/android/data_usage/data_use_ui_tab_model.cc b/chrome/browser/android/data_usage/data_use_ui_tab_model.cc
index 546797d..2f173af 100644
--- a/chrome/browser/android/data_usage/data_use_ui_tab_model.cc
+++ b/chrome/browser/android/data_usage/data_use_ui_tab_model.cc
@@ -21,6 +21,21 @@
 
 namespace android {
 
+DataUseUITabModel::DataUseUINavigationEvent::DataUseUINavigationEvent(
+    SessionID tab_id,
+    DataUseTabModel::TransitionType transition_type,
+    GURL url,
+    std::string package)
+    : tab_id(tab_id),
+      transition_type(transition_type),
+      url(url),
+      package(package) {}
+
+DataUseUITabModel::DataUseUINavigationEvent::DataUseUINavigationEvent(
+    const DataUseUINavigationEvent&) = default;
+
+DataUseUITabModel::DataUseUINavigationEvent::~DataUseUINavigationEvent() {}
+
 DataUseUITabModel::DataUseUITabModel()
     : data_use_ui_navigations_(new std::vector<DataUseUINavigationEvent>()),
       weak_factory_(this) {
@@ -37,10 +52,10 @@
 void DataUseUITabModel::ReportBrowserNavigation(
     const GURL& gurl,
     ui::PageTransition page_transition,
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     content::NavigationEntry* navigation_entry) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_LE(0, tab_id);
+  DCHECK(tab_id.is_valid());
   DCHECK_EQ(navigation_entry->GetURL(), gurl);
 
   DataUseTabModel::TransitionType transition_type;
@@ -59,9 +74,9 @@
   }
 }
 
-void DataUseUITabModel::ReportTabClosure(SessionID::id_type tab_id) {
+void DataUseUITabModel::ReportTabClosure(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_LE(0, tab_id);
+  DCHECK(tab_id.is_valid());
   if (data_use_tab_model_)
     data_use_tab_model_->OnTabCloseEvent(tab_id);
 
@@ -73,12 +88,12 @@
 }
 
 void DataUseUITabModel::ReportCustomTabInitialNavigation(
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     const std::string& package_name,
     const std::string& url) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (tab_id <= 0)
+  if (!tab_id.is_valid())
     return;
 
   if (data_use_ui_navigations_) {
@@ -116,7 +131,7 @@
   return weak_factory_.GetWeakPtr();
 }
 
-void DataUseUITabModel::NotifyTrackingStarting(SessionID::id_type tab_id) {
+void DataUseUITabModel::NotifyTrackingStarting(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Do not show tracking started UI for custom tabs with package match.
@@ -135,7 +150,7 @@
   RemoveTabEvent(tab_id, DATA_USE_TRACKING_ENDED);
 }
 
-void DataUseUITabModel::NotifyTrackingEnding(SessionID::id_type tab_id) {
+void DataUseUITabModel::NotifyTrackingEnding(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!data_use_tab_model_->IsCustomTabPackageMatch(tab_id));
 
@@ -154,8 +169,7 @@
   RemoveTabEvent(tab_id, DATA_USE_CONTINUE_CLICKED);
 }
 
-bool DataUseUITabModel::CheckAndResetDataUseTrackingStarted(
-    SessionID::id_type tab_id) {
+bool DataUseUITabModel::CheckAndResetDataUseTrackingStarted(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   TabEvents::iterator it = tab_events_.find(tab_id);
@@ -168,7 +182,7 @@
 bool DataUseUITabModel::WouldDataUseTrackingEnd(
     const std::string& url,
     int page_transition,
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     const content::NavigationEntry* navigation_entry) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -199,8 +213,7 @@
       tab_id, transition_type, GURL(url), navigation_entry);
 }
 
-void DataUseUITabModel::UserClickedContinueOnDialogBox(
-    SessionID::id_type tab_id) {
+void DataUseUITabModel::UserClickedContinueOnDialogBox(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   TabEvents::iterator it = tab_events_.find(tab_id);
@@ -210,8 +223,7 @@
   MaybeCreateTabEvent(tab_id, DATA_USE_CONTINUE_CLICKED);
 }
 
-bool DataUseUITabModel::CheckAndResetDataUseTrackingEnded(
-    SessionID::id_type tab_id) {
+bool DataUseUITabModel::CheckAndResetDataUseTrackingEnded(SessionID tab_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   TabEvents::iterator it = tab_events_.find(tab_id);
@@ -221,13 +233,13 @@
   return RemoveTabEvent(tab_id, DATA_USE_TRACKING_ENDED);
 }
 
-bool DataUseUITabModel::MaybeCreateTabEvent(SessionID::id_type tab_id,
+bool DataUseUITabModel::MaybeCreateTabEvent(SessionID tab_id,
                                             DataUseTrackingEvent event) {
   DCHECK(thread_checker_.CalledOnValidThread());
   return tab_events_.insert(std::make_pair(tab_id, event)).second;
 }
 
-bool DataUseUITabModel::RemoveTabEvent(SessionID::id_type tab_id,
+bool DataUseUITabModel::RemoveTabEvent(SessionID tab_id,
                                        DataUseTrackingEvent event) {
   DCHECK(thread_checker_.CalledOnValidThread());
   TabEvents::iterator it = tab_events_.find(tab_id);
@@ -309,7 +321,7 @@
 }
 
 void DataUseUITabModel::BufferNavigationEvent(
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     DataUseTabModel::TransitionType transition,
     const GURL& url,
     const std::string& package) {
diff --git a/chrome/browser/android/data_usage/data_use_ui_tab_model.h b/chrome/browser/android/data_usage/data_use_ui_tab_model.h
index a4e11ce..4d1c1ef9 100644
--- a/chrome/browser/android/data_usage/data_use_ui_tab_model.h
+++ b/chrome/browser/android/data_usage/data_use_ui_tab_model.h
@@ -53,27 +53,27 @@
   // back-forward navigation history, and should not be null.
   void ReportBrowserNavigation(const GURL& gurl,
                                ui::PageTransition page_transition,
-                               SessionID::id_type tab_id,
+                               SessionID tab_id,
                                content::NavigationEntry* navigation_entry);
 
   // Reports a tab closure for the tab with |tab_id| to the DataUseTabModel on
   // IO thread. The tab could either have been closed or evicted from the memory
   // by Android.
-  void ReportTabClosure(SessionID::id_type tab_id);
+  void ReportTabClosure(SessionID tab_id);
 
   // Reports a custom tab navigation to the DataUseTabModel on the IO thread.
   // Includes the |tab_id|, |url|, and |package_name| for the navigation.
-  void ReportCustomTabInitialNavigation(SessionID::id_type tab_id,
+  void ReportCustomTabInitialNavigation(SessionID tab_id,
                                         const std::string& package_name,
                                         const std::string& url);
 
   // Returns true if data use tracking has been started for the tab with id
   // |tab_id|. Calling this function resets the state of the tab.
-  bool CheckAndResetDataUseTrackingStarted(SessionID::id_type tab_id);
+  bool CheckAndResetDataUseTrackingStarted(SessionID tab_id);
 
   // Returns true if data use tracking has ended for the tab with id |tab_id|.
   // Calling this function resets the state of the tab.
-  bool CheckAndResetDataUseTrackingEnded(SessionID::id_type tab_id);
+  bool CheckAndResetDataUseTrackingEnded(SessionID tab_id);
 
   // Sets the pointer to DataUseTabModel. |data_use_tab_model| is owned by the
   // caller.
@@ -88,14 +88,14 @@
   bool WouldDataUseTrackingEnd(
       const std::string& url,
       int page_transition,
-      SessionID::id_type tab_id,
+      SessionID tab_id,
       const content::NavigationEntry* navigation_entry) const;
 
   // Notifies that user clicked "Continue" when the dialog box with data use
   // warning was shown. Includes the |tab_id| on which the warning was shown.
   // When the user clicks "Continue", additional UI warnings about exiting data
   // use tracking are not shown to the user.
-  void UserClickedContinueOnDialogBox(SessionID::id_type tab_id);
+  void UserClickedContinueOnDialogBox(SessionID tab_id);
 
   base::WeakPtr<DataUseUITabModel> GetWeakPtr();
 
@@ -119,37 +119,34 @@
 
   // Contains the details of a single UI navigation event.
   struct DataUseUINavigationEvent {
-    DataUseUINavigationEvent(SessionID::id_type tab_id,
+    DataUseUINavigationEvent(SessionID tab_id,
                              DataUseTabModel::TransitionType transition_type,
                              GURL url,
-                             std::string package)
-        : tab_id(tab_id),
-          transition_type(transition_type),
-          url(url),
-          package(package) {}
+                             std::string package);
+    DataUseUINavigationEvent(const DataUseUINavigationEvent&);
+    ~DataUseUINavigationEvent();
 
-    const SessionID::id_type tab_id;
+    const SessionID tab_id;
     const DataUseTabModel::TransitionType transition_type;
     const GURL url;
     const std::string package;
   };
 
-  typedef std::map<SessionID::id_type, DataUseTrackingEvent> TabEvents;
+  typedef std::map<SessionID, DataUseTrackingEvent> TabEvents;
 
   // DataUseTabModel::TabDataUseObserver implementation:
-  void NotifyTrackingStarting(SessionID::id_type tab_id) override;
-  void NotifyTrackingEnding(SessionID::id_type tab_id) override;
+  void NotifyTrackingStarting(SessionID tab_id) override;
+  void NotifyTrackingEnding(SessionID tab_id) override;
   void OnDataUseTabModelReady() override;
 
   // Creates |event| for tab with id |tab_id| and value |event|, if there is no
   // existing entry for |tab_id|, and returns true. Otherwise, returns false
   // without modifying the entry.
-  bool MaybeCreateTabEvent(SessionID::id_type tab_id,
-                           DataUseTrackingEvent event);
+  bool MaybeCreateTabEvent(SessionID tab_id, DataUseTrackingEvent event);
 
   // Removes event entry for |tab_id|, if the entry is equal to |event|, and
   // returns true. Otherwise, returns false without modifying the entry.
-  bool RemoveTabEvent(SessionID::id_type tab_id, DataUseTrackingEvent event);
+  bool RemoveTabEvent(SessionID tab_id, DataUseTrackingEvent event);
 
   // Converts |page_transition| to page with GURL |gurl| to
   // DataUseTabModel::TransitionType enum. Returns true if conversion was
@@ -161,7 +158,7 @@
       DataUseTabModel::TransitionType* transition_type) const;
 
   // Buffers the navigation event for later processing.
-  void BufferNavigationEvent(SessionID::id_type tab_id,
+  void BufferNavigationEvent(SessionID tab_id,
                              DataUseTabModel::TransitionType transition,
                              const GURL& url,
                              const std::string& package);
diff --git a/chrome/browser/android/data_usage/data_use_ui_tab_model_unittest.cc b/chrome/browser/android/data_usage/data_use_ui_tab_model_unittest.cc
index 1e06bc3..0676e384 100644
--- a/chrome/browser/android/data_usage/data_use_ui_tab_model_unittest.cc
+++ b/chrome/browser/android/data_usage/data_use_ui_tab_model_unittest.cc
@@ -42,9 +42,9 @@
 const char kFooLabel[] = "foo_label";
 const char kFooPackage[] = "com.foo";
 
-const SessionID::id_type kTabIDFoo = 1;
-const SessionID::id_type kTabIDBar = 2;
-const SessionID::id_type kTabIDBaz = 3;
+const SessionID kTabIDFoo = SessionID::FromSerializedValue(1);
+const SessionID kTabIDBar = SessionID::FromSerializedValue(2);
+const SessionID kTabIDBaz = SessionID::FromSerializedValue(3);
 
 const char kURLFoo[] = "https://www.foo.com/#q=abc";
 const char kURLBar[] = "https://www.bar.com/#q=abc";
@@ -61,8 +61,8 @@
 // Mock observer to track the calls to start and end tracking events.
 class MockTabDataUseObserver : public DataUseTabModel::TabDataUseObserver {
  public:
-  MOCK_METHOD1(NotifyTrackingStarting, void(SessionID::id_type tab_id));
-  MOCK_METHOD1(NotifyTrackingEnding, void(SessionID::id_type tab_id));
+  MOCK_METHOD1(NotifyTrackingStarting, void(SessionID tab_id));
+  MOCK_METHOD1(NotifyTrackingEnding, void(SessionID tab_id));
   MOCK_METHOD0(OnDataUseTabModelReady, void());
 };
 
@@ -100,7 +100,7 @@
                                             label);
   }
 
-  void ExpectDataUseTrackingInfo(SessionID::id_type tab_id,
+  void ExpectDataUseTrackingInfo(SessionID tab_id,
                                  const base::TimeTicks& at_time,
                                  const std::string& expected_label,
                                  const std::string& expected_tag) const {
@@ -185,11 +185,9 @@
       {ui::PageTransition::PAGE_TRANSITION_RELOAD, kFooLabel},
   };
 
-  SessionID::id_type foo_tab_id = 100;
-
   for (size_t i = 0; i < arraysize(tests); ++i) {
     // Start a new tab.
-    ++foo_tab_id;
+    SessionID foo_tab_id = SessionID::FromSerializedValue(100 + i);
     auto navigation_entry = CreateNavigationEntry(kURLFoo);
     data_use_ui_tab_model()->ReportBrowserNavigation(
         GURL(kURLFoo), tests[i].transition_type, foo_tab_id,
@@ -225,7 +223,7 @@
 
   // Start a custom tab with matching package name and verify if tracking
   // started is not being set.
-  const SessionID::id_type bar_tab_id = foo_tab_id + 1;
+  const SessionID bar_tab_id = SessionID::FromSerializedValue(200);
   EXPECT_FALSE(
       data_use_ui_tab_model()->CheckAndResetDataUseTrackingStarted(bar_tab_id));
   data_use_ui_tab_model()->ReportCustomTabInitialNavigation(
@@ -245,9 +243,9 @@
 TEST_F(DataUseUITabModelTest, EntranceExitState) {
   SetUpDataUseUITabModel();
 
-  const SessionID::id_type kFooTabId = 1;
-  const SessionID::id_type kBarTabId = 2;
-  const SessionID::id_type kBazTabId = 3;
+  const SessionID kFooTabId = SessionID::FromSerializedValue(1);
+  const SessionID kBarTabId = SessionID::FromSerializedValue(2);
+  const SessionID kBazTabId = SessionID::FromSerializedValue(3);
 
   // CheckAndResetDataUseTrackingStarted should return true only once.
   data_use_tab_model()->NotifyObserversOfTrackingStarting(kFooTabId);
@@ -322,8 +320,6 @@
 TEST_F(DataUseUITabModelTest, EntranceExitStateForDialog) {
   SetUpDataUseUITabModel();
 
-  const SessionID::id_type kFooTabId = 1;
-
   std::vector<std::string> url_regexes;
   url_regexes.push_back(
       "http://www[.]foo[.]com/#q=.*|https://www[.]foo[.]com/#q=.*");
@@ -331,8 +327,6 @@
                      url_regexes,
                      std::vector<std::string>(url_regexes.size(), kFooLabel));
 
-  SessionID::id_type foo_tab_id = kFooTabId;
-
   const struct {
     // True if a dialog box was shown to the user. It may not be shown if the
     // user has previously selected the option to opt out.
@@ -344,7 +338,7 @@
 
   for (size_t i = 0; i < arraysize(tests); ++i) {
     // Start a new tab.
-    ++foo_tab_id;
+    SessionID foo_tab_id = SessionID::FromSerializedValue(100 + i);
     auto navigation_entry_foo = CreateNavigationEntry(kURLFoo);
     data_use_ui_tab_model()->ReportBrowserNavigation(
         GURL(kURLFoo), ui::PAGE_TRANSITION_GENERATED, foo_tab_id,
diff --git a/chrome/browser/android/data_usage/external_data_use_observer_unittest.cc b/chrome/browser/android/data_usage/external_data_use_observer_unittest.cc
index 67e2904..bca89fe 100644
--- a/chrome/browser/android/data_usage/external_data_use_observer_unittest.cc
+++ b/chrome/browser/android/data_usage/external_data_use_observer_unittest.cc
@@ -34,7 +34,7 @@
 namespace {
 
 const char kDefaultLabel[] = "label";
-const SessionID::id_type kDefaultTabId = 0;
+const SessionID kDefaultTabId = SessionID::FromSerializedValue(1);
 const char kDefaultURL[] = "http://www.google.com/#q=abc";
 
 }  // namespace
diff --git a/chrome/browser/android/data_usage/external_data_use_reporter.h b/chrome/browser/android/data_usage/external_data_use_reporter.h
index cdc473c..53711b8 100644
--- a/chrome/browser/android/data_usage/external_data_use_reporter.h
+++ b/chrome/browser/android/data_usage/external_data_use_reporter.h
@@ -39,9 +39,8 @@
 // must only be accessed on UI thread.
 class ExternalDataUseReporter {
  public:
-  typedef base::Callback<bool(SessionID::id_type,
-                              const base::TimeTicks,
-                              DataUseTabModel::TrackingInfo*)>
+  typedef base::Callback<
+      bool(SessionID, const base::TimeTicks, DataUseTabModel::TrackingInfo*)>
       GetTrackingInfoCallback;
 
   typedef base::Callback<void(const std::string&,
diff --git a/chrome/browser/android/data_usage/external_data_use_reporter_unittest.cc b/chrome/browser/android/data_usage/external_data_use_reporter_unittest.cc
index 5213765..9f6ba873 100644
--- a/chrome/browser/android/data_usage/external_data_use_reporter_unittest.cc
+++ b/chrome/browser/android/data_usage/external_data_use_reporter_unittest.cc
@@ -41,7 +41,7 @@
     "DataUsage.Perf.ReportSubmissionDuration";
 
 const char kDefaultLabel[] = "label";
-const SessionID::id_type kDefaultTabId = 0;
+const SessionID kDefaultTabId = SessionID::FromSerializedValue(1);
 const char kDefaultURL[] = "http://www.google.com/#q=abc";
 
 const char kFooMccMnc[] = "foo_mccmnc";
@@ -312,8 +312,9 @@
       kDefaultTabId, DataUseTabModel::TRANSITION_OMNIBOX_SEARCH,
       GURL("http://www.foo.com/#q=abc"), std::string(), nullptr);
 
+  const SessionID kTabIdBar = SessionID::FromSerializedValue(2);
   data_use_tab_model()->OnNavigationEvent(
-      kDefaultTabId + 1, DataUseTabModel::TRANSITION_OMNIBOX_SEARCH,
+      kTabIdBar, DataUseTabModel::TRANSITION_OMNIBOX_SEARCH,
       GURL("http://www.bar.com/#q=abc"), std::string(), nullptr);
 
   EXPECT_EQ(0U, external_data_use_reporter()->buffered_data_reports_.size());
@@ -328,7 +329,7 @@
 
   // Check |kLabelBar| matching rule.
   data_usage::DataUse data_bar = default_data_use();
-  data_bar.tab_id = kDefaultTabId + 1;
+  data_bar.tab_id = kTabIdBar;
   data_bar.url = GURL("http://www.bar.com/#q=abc");
   data_bar.mcc_mnc = kBarMccMnc;
   OnDataUse(data_bar);
diff --git a/chrome/browser/android/foreign_session_helper.cc b/chrome/browser/android/foreign_session_helper.cc
index 3ed8093..7a3c719 100644
--- a/chrome/browser/android/foreign_session_helper.cc
+++ b/chrome/browser/android/foreign_session_helper.cc
@@ -246,26 +246,9 @@
         ConvertUTF8ToJavaString(env, session.session_name), session.device_type,
         session.modified_time.ToJavaTime()));
 
-    const std::string group_name =
-        base::FieldTrialList::FindFullName("TabSyncByRecency");
-    if (group_name == "Enabled") {
-      // Create a custom window with tabs from all windows included and ordered
-      // by recency (GetForeignSessionTabs will do ordering automatically).
-      std::vector<const sessions::SessionTab*> tabs;
-      open_tabs->GetForeignSessionTabs(session.session_tag, &tabs);
-      ScopedJavaLocalRef<jobject> last_pushed_window(
-          Java_ForeignSessionHelper_pushWindow(
-              env, last_pushed_session, session.modified_time.ToJavaTime(), 0));
-      for (const sessions::SessionTab* tab : tabs) {
-         if (ShouldSkipTab(*tab))
-           continue;
-         JNI_ForeignSessionHelper_CopyTabToJava(env, *tab, last_pushed_window);
-      }
-    } else {
-      // Push the full session, with tabs ordered by visual position.
-      JNI_ForeignSessionHelper_CopySessionToJava(env, session,
-                                                 last_pushed_session);
-    }
+    // Push the full session, with tabs ordered by visual position.
+    JNI_ForeignSessionHelper_CopySessionToJava(env, session,
+                                               last_pushed_session);
   }
 
   return true;
diff --git a/chrome/browser/android/sessions/session_tab_helper_android.cc b/chrome/browser/android/sessions/session_tab_helper_android.cc
index 6111044..03b79736 100644
--- a/chrome/browser/android/sessions/session_tab_helper_android.cc
+++ b/chrome/browser/android/sessions/session_tab_helper_android.cc
@@ -17,5 +17,5 @@
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(java_web_contents);
   CHECK(web_contents);
-  return SessionTabHelper::IdForTab(web_contents);
+  return SessionTabHelper::IdForTab(web_contents).id();
 }
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index 02fa6de..4818ce8 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -266,8 +266,8 @@
       env, weak_java_tab_.get(env), reinterpret_cast<intptr_t>(&predicate));
 }
 
-void TabAndroid::SetWindowSessionID(SessionID::id_type window_id) {
-  session_window_id_.set_id(window_id);
+void TabAndroid::SetWindowSessionID(SessionID window_id) {
+  session_window_id_ = window_id;
 
   if (!web_contents())
     return;
@@ -415,7 +415,7 @@
   AttachTabHelpers(web_contents_.get());
   WebContentsObserver::Observe(web_contents_.get());
 
-  SetWindowSessionID(session_window_id_.id());
+  SetWindowSessionID(session_window_id_);
 
   session_tab_id_.set_id(
       SessionTabHelper::FromWebContents(web_contents())->session_id().id());
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index c53cdbea..716c7a95 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -120,7 +120,7 @@
   void DeleteFrozenNavigationEntries(
       const WebContentsState::DeletionPredicate& predicate);
 
-  void SetWindowSessionID(SessionID::id_type window_id);
+  void SetWindowSessionID(SessionID window_id);
   void SetSyncId(int sync_id);
 
   void HandlePopupNavigation(NavigateParams* params);
diff --git a/chrome/browser/android/usb/web_usb_chooser_service_android.cc b/chrome/browser/android/usb/web_usb_chooser_service_android.cc
index f38f1ab..539f47c8 100644
--- a/chrome/browser/android/usb/web_usb_chooser_service_android.cc
+++ b/chrome/browser/android/usb/web_usb_chooser_service_android.cc
@@ -7,27 +7,23 @@
 #include <utility>
 
 #include "chrome/browser/ui/android/usb_chooser_dialog_android.h"
+#include "chrome/browser/usb/usb_chooser_controller.h"
 #include "content/public/browser/browser_thread.h"
 
 WebUsbChooserServiceAndroid::WebUsbChooserServiceAndroid(
     content::RenderFrameHost* render_frame_host)
-    : render_frame_host_(render_frame_host) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(render_frame_host_);
-}
+    : WebUsbChooserService(render_frame_host) {}
 
 WebUsbChooserServiceAndroid::~WebUsbChooserServiceAndroid() {}
 
-void WebUsbChooserServiceAndroid::GetPermission(
-    std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-    GetPermissionCallback callback) {
-  usb_chooser_dialog_android_.push_back(
-      std::make_unique<UsbChooserDialogAndroid>(
-          std::move(device_filters), render_frame_host_, std::move(callback)));
+void WebUsbChooserServiceAndroid::ShowChooser(
+    std::unique_ptr<UsbChooserController> controller) {
+  dialog_ = UsbChooserDialogAndroid::Create(
+      render_frame_host(), std::move(controller),
+      base::BindOnce(&WebUsbChooserServiceAndroid::OnDialogClosed,
+                     base::Unretained(this)));
 }
 
-void WebUsbChooserServiceAndroid::Bind(
-    mojo::InterfaceRequest<device::mojom::UsbChooserService> request) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  bindings_.AddBinding(this, std::move(request));
+void WebUsbChooserServiceAndroid::OnDialogClosed() {
+  dialog_.reset();
 }
diff --git a/chrome/browser/android/usb/web_usb_chooser_service_android.h b/chrome/browser/android/usb/web_usb_chooser_service_android.h
index 0d6ed26..00ab9f1 100644
--- a/chrome/browser/android/usb/web_usb_chooser_service_android.h
+++ b/chrome/browser/android/usb/web_usb_chooser_service_android.h
@@ -9,38 +9,28 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "device/usb/public/mojom/chooser_service.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
+#include "chrome/browser/usb/web_usb_chooser_service.h"
 
+class UsbChooserController;
 class UsbChooserDialogAndroid;
 
-namespace content {
-class RenderFrameHost;
-}
-
 // Implementation of the public device::usb::ChooserService interface.
 // This interface can be used by a webpage to request permission from user
 // to access a certain device.
-class WebUsbChooserServiceAndroid : public device::mojom::UsbChooserService {
+class WebUsbChooserServiceAndroid : public WebUsbChooserService {
  public:
   explicit WebUsbChooserServiceAndroid(
       content::RenderFrameHost* render_frame_host);
-
   ~WebUsbChooserServiceAndroid() override;
 
-  // device::usb::ChooserService:
-  void GetPermission(
-      std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
-      GetPermissionCallback callback) override;
-
-  void Bind(mojo::InterfaceRequest<device::mojom::UsbChooserService> request);
+  // WebUsbChooserService implementation
+  void ShowChooser(std::unique_ptr<UsbChooserController> controller) override;
 
  private:
-  content::RenderFrameHost* const render_frame_host_;
-  mojo::BindingSet<device::mojom::UsbChooserService> bindings_;
-  std::vector<std::unique_ptr<UsbChooserDialogAndroid>>
-      usb_chooser_dialog_android_;
+  void OnDialogClosed();
+
+  // Only a single dialog can be shown at a time.
+  std::unique_ptr<UsbChooserDialogAndroid> dialog_;
 
   DISALLOW_COPY_AND_ASSIGN(WebUsbChooserServiceAndroid);
 };
diff --git a/chrome/browser/android/vr/vr_shell.cc b/chrome/browser/android/vr/vr_shell.cc
index 72260bc..adbaaf413 100644
--- a/chrome/browser/android/vr/vr_shell.cc
+++ b/chrome/browser/android/vr/vr_shell.cc
@@ -772,6 +772,13 @@
   }
 }
 
+void VrShell::RecordPresentationStartAction(PresentationStartAction action) {
+  SessionMetricsHelper* metrics_helper =
+      SessionMetricsHelper::FromWebContents(web_contents_);
+  if (metrics_helper)
+    metrics_helper->RecordPresentationStartAction(action);
+}
+
 void VrShell::ShowSoftInput(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj,
                             bool show) {
@@ -832,6 +839,9 @@
       Java_VrShellImpl_onNeedsKeyboardUpdate(env, j_vr_shell_);
       return;
     }
+    // kSearchEnginePromo should directly DOFF without showing a promo. So it
+    // should never be used from VR ui thread.
+    case UiUnsupportedMode::kSearchEnginePromo:
     case UiUnsupportedMode::kCount:
       NOTREACHED();  // Should never be used as a mode.
       return;
diff --git a/chrome/browser/android/vr/vr_shell.h b/chrome/browser/android/vr/vr_shell.h
index 6e6c89c..0160747 100644
--- a/chrome/browser/android/vr/vr_shell.h
+++ b/chrome/browser/android/vr/vr_shell.h
@@ -187,6 +187,7 @@
   void ExitFullscreen();
   void LogUnsupportedModeUserMetric(UiUnsupportedMode mode);
   void RecordVrStartAction(VrStartAction action);
+  void RecordPresentationStartAction(PresentationStartAction action);
   void OnUnsupportedMode(UiUnsupportedMode mode);
   void OnExitVrPromptResult(UiUnsupportedMode reason,
                             ExitVrPromptChoice choice);
diff --git a/chrome/browser/android/vr/vr_shell_delegate.cc b/chrome/browser/android/vr/vr_shell_delegate.cc
index a50d0ea7..6fa8055 100644
--- a/chrome/browser/android/vr/vr_shell_delegate.cc
+++ b/chrome/browser/android/vr/vr_shell_delegate.cc
@@ -132,12 +132,21 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
     jint start_action) {
+  VrStartAction action = static_cast<VrStartAction>(start_action);
+
+  if (action == VrStartAction::kDeepLinkedApp) {
+    // If this is a deep linked app we expect a DisplayActivate to be coming
+    // down the pipeline shortly.
+    possible_presentation_start_action_ =
+        PresentationStartAction::kDeepLinkedApp;
+  }
+
   if (!vr_shell_) {
-    pending_vr_start_action_ = static_cast<VrStartAction>(start_action);
+    pending_vr_start_action_ = action;
     return;
   }
 
-  vr_shell_->RecordVrStartAction(static_cast<VrStartAction>(start_action));
+  vr_shell_->RecordVrStartAction(action);
 }
 
 void VrShellDelegate::OnPresentResult(
@@ -149,6 +158,7 @@
     bool success) {
   if (!success) {
     std::move(callback).Run(false, nullptr);
+    possible_presentation_start_action_ = base::nullopt;
     return;
   }
 
@@ -163,6 +173,15 @@
     return;
   }
 
+  // If possible_presentation_start_action_ is not set at this point, then this
+  // request present probably came from blink, and has already been reported
+  // from there.
+  if (possible_presentation_start_action_) {
+    vr_shell_->RecordPresentationStartAction(
+        *possible_presentation_start_action_);
+    possible_presentation_start_action_ = base::nullopt;
+  }
+
   vr_shell_->ConnectPresentingService(
       std::move(submit_client), std::move(request), std::move(display_info),
       std::move(present_options));
@@ -174,6 +193,16 @@
                                       const JavaParamRef<jobject>& obj) {
   device::GvrDevice* device = static_cast<device::GvrDevice*>(GetDevice());
   if (device) {
+    if (!possible_presentation_start_action_ ||
+        possible_presentation_start_action_ !=
+            PresentationStartAction::kDeepLinkedApp) {
+      // The only possible sources for DisplayActivate are at the moment DLAs
+      // and HeadsetActivations. Therefore if it's not a DLA it must be a
+      // HeadsetActivation.
+      possible_presentation_start_action_ =
+          PresentationStartAction::kHeadsetActivation;
+    }
+
     device->Activate(
         device::mojom::VRDisplayEventReason::MOUNTED,
         base::BindRepeating(&VrShellDelegate::OnActivateDisplayHandled,
@@ -268,6 +297,8 @@
     // WebVR page didn't request presentation in the vrdisplayactivate handler.
     // Tell VrShell that we are in VR Browsing Mode.
     ExitWebVRPresent();
+    // Reset possible_presentation_start_action_ as it may have been set.
+    possible_presentation_start_action_ = base::nullopt;
   }
 }
 
diff --git a/chrome/browser/android/vr/vr_shell_delegate.h b/chrome/browser/android/vr/vr_shell_delegate.h
index e396ecd..914d322 100644
--- a/chrome/browser/android/vr/vr_shell_delegate.h
+++ b/chrome/browser/android/vr/vr_shell_delegate.h
@@ -99,6 +99,7 @@
   base::OnceCallback<void(bool)> on_present_result_callback_;
   bool pending_successful_present_request_ = false;
   base::Optional<VrStartAction> pending_vr_start_action_;
+  base::Optional<PresentationStartAction> possible_presentation_start_action_;
 
   base::CancelableClosure clear_activate_task_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc
index 64240b4f..c98d1eb 100644
--- a/chrome/browser/autocomplete/search_provider_unittest.cc
+++ b/chrome/browser/autocomplete/search_provider_unittest.cc
@@ -3587,9 +3587,21 @@
   // Inject a scored result, which will trigger answer retrieval.
   base::string16 query = base::ASCIIToUTF16("weather los angeles");
   SearchSuggestionParser::SuggestResult suggest_result(
-      query, AutocompleteMatchType::SEARCH_HISTORY, 0, query, base::string16(),
-      base::string16(), base::string16(), base::string16(), nullptr,
-      std::string(), std::string(), false, 1200, false, false, query);
+      query, AutocompleteMatchType::SEARCH_HISTORY,
+      /*subtype_identifier=*/0,
+      /*match_contents=*/query,
+      /*match_contents_prefix=*/base::string16(),
+      /*annotation=*/base::string16(),
+      /*answer_contents=*/base::string16(),
+      /*answer_type=*/base::string16(),
+      /*answer=*/nullptr,
+      /*suggest_query_params=*/std::string(),
+      /*deletion_url=*/std::string(),
+      /*from_keyword_provider=*/false,
+      /*relevance=*/1200,
+      /*relevance_from_server=*/false,
+      /*should_prefetch=*/false,
+      /*input_text=*/query);
   QueryForInput(ASCIIToUTF16("weather l"), false, false);
   provider_->transformed_default_history_results_.push_back(suggest_result);
   answer = provider_->FindAnswersPrefetchData();
diff --git a/chrome/browser/browsing_data/counters/site_data_counting_helper.cc b/chrome/browser/browsing_data/counters/site_data_counting_helper.cc
index dc77ed3..e08f007 100644
--- a/chrome/browser/browsing_data/counters/site_data_counting_helper.cc
+++ b/chrome/browser/browsing_data/counters/site_data_counting_helper.cc
@@ -233,11 +233,11 @@
   DCHECK(tasks_ > 0);
   for (const GURL& origin : origins) {
     if (BrowsingDataHelper::HasWebScheme(origin))
-      unique_origins_.insert(origin);
+      unique_hosts_.insert(origin.host());
   }
   if (--tasks_ > 0)
     return;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(completion_callback_, unique_origins_.size()));
+      FROM_HERE, base::BindOnce(completion_callback_, unique_hosts_.size()));
   base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
 }
diff --git a/chrome/browser/browsing_data/counters/site_data_counting_helper.h b/chrome/browser/browsing_data/counters/site_data_counting_helper.h
index 22ebb8a5..604e066 100644
--- a/chrome/browser/browsing_data/counters/site_data_counting_helper.h
+++ b/chrome/browser/browsing_data/counters/site_data_counting_helper.h
@@ -68,7 +68,7 @@
   base::Time begin_;
   base::Callback<void(int)> completion_callback_;
   int tasks_;
-  std::set<GURL> unique_origins_;
+  std::set<std::string> unique_hosts_;
   scoped_refptr<BrowsingDataFlashLSOHelper> flash_lso_helper_;
 };
 
diff --git a/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc b/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc
index 7d2593aa..e45d0539 100644
--- a/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc
+++ b/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc
@@ -115,8 +115,8 @@
       cookie_store->SetCanonicalCookieAsync(
           net::CanonicalCookie::CreateSanitizedCookie(
               url, "name", "A=1", url.host(), url.path(), time, base::Time(),
-              time, true, false, net::CookieSameSite::DEFAULT_MODE,
-              net::COOKIE_PRIORITY_DEFAULT),
+              time, url.SchemeIsCryptographic(), false,
+              net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT),
           url.SchemeIsCryptographic(), true /*modify_http_only*/,
           base::BindOnce(&SiteDataCountingHelperTest::DoneOnIOThread,
                          base::Unretained(this)));
@@ -195,7 +195,7 @@
 
 TEST_F(SiteDataCountingHelperTest, CookiesAndLocalStorage) {
   base::Time now = base::Time::Now();
-  CreateCookies(now, {"https://example.com", "https://google.com"});
+  CreateCookies(now, {"http://example.com", "https://google.com"});
   CreateLocalStorage(now,
                      {FILE_PATH_LITERAL("https_example.com_443.localstorage"),
                       FILE_PATH_LITERAL("https_bing.com_443.localstorage")});
@@ -205,3 +205,15 @@
   WaitForTasksOnIOThread();
   DCHECK_EQ(3, GetResult());
 }
+
+TEST_F(SiteDataCountingHelperTest, SameHostDifferentScheme) {
+  base::Time now = base::Time::Now();
+  CreateCookies(now, {"http://google.com", "https://google.com"});
+  CreateLocalStorage(now,
+                     {FILE_PATH_LITERAL("https_google.com_443.localstorage"),
+                      FILE_PATH_LITERAL("http_google.com_80.localstorage")});
+  WaitForTasksOnIOThread();
+  CountEntries(base::Time());
+  WaitForTasksOnIOThread();
+  DCHECK_EQ(1, GetResult());
+}
diff --git a/chrome/browser/chooser_controller/chooser_controller.cc b/chrome/browser/chooser_controller/chooser_controller.cc
index a792688f..2694473 100644
--- a/chrome/browser/chooser_controller/chooser_controller.cc
+++ b/chrome/browser/chooser_controller/chooser_controller.cc
@@ -9,11 +9,15 @@
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/constants.h"
+#include "extensions/buildflags/buildflags.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/origin.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#endif
+
 namespace {
 
 base::string16 CreateTitle(content::RenderFrameHost* render_frame_host,
@@ -21,6 +25,7 @@
                            int title_string_id_extension) {
   url::Origin origin = render_frame_host->GetLastCommittedOrigin();
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   if (origin.scheme() == extensions::kExtensionScheme) {
     content::WebContents* web_contents =
         content::WebContents::FromRenderFrameHost(render_frame_host);
@@ -37,6 +42,7 @@
       }
     }
   }
+#endif
 
   return l10n_util::GetStringFUTF16(
       title_string_id_origin,
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 3ba0472..f3fafb84 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -715,7 +715,7 @@
 #if !defined(OS_ANDROID)
 // A TaskRunner that defers tasks until the real task runner is up and running.
 // This is used during early initialization, before the real task runner has
-// been created. DeferredTaskRunner has the following states.
+// been created. DeferringTaskRunner has the following states.
 //
 // . kInstalled: the initial state. Tasks are added to |deferred_runner_|. In
 //   this state this is installed as the active ThreadTaskRunnerHandle.
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index fc235c55..d3e35e18 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -51,7 +51,7 @@
         "patch": [ "patch_file" ],
         "pdf_compositor": [ "compositor" ],
         "profile_import": [ "import" ],
-        "profiling": [ "profiling", "heap_profiler" ],
+        "heap_profiling": [ "profiling", "heap_profiler" ],
         "proxy_resolver": [ "factory" ],
         "preferences": [ "pref_client", "pref_control" ],
         "removable_storage_writer": [ "removable_storage_writer" ],
diff --git a/chrome/browser/chromeos/arc/arc_optin_uma.cc b/chrome/browser/chromeos/arc/arc_optin_uma.cc
index 2f2138e..e4bf7795 100644
--- a/chrome/browser/chromeos/arc/arc_optin_uma.cc
+++ b/chrome/browser/chromeos/arc/arc_optin_uma.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
+#include "chrome/browser/profiles/profile.h"
 #include "components/arc/arc_util.h"
 
 namespace arc {
@@ -18,11 +19,12 @@
 // Adds a suffix to the name based on the account type.
 std::string GetHistogramName(const std::string& base_name,
                              const Profile* profile) {
+  if (IsRobotAccountMode())
+    return base_name + "RobotAccount";
+  if (profile->IsChild())
+    return base_name + "Child";
   return base_name +
-         (IsRobotAccountMode()
-              ? "RobotAccount"
-              : (policy_util::IsAccountManaged(profile) ? "Managed"
-                                                        : "Unmanaged"));
+         (policy_util::IsAccountManaged(profile) ? "Managed" : "Unmanaged");
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
index 445de5e..f62d473d0 100644
--- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
+++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -1881,6 +1881,31 @@
       base::Bind(&OnRemoveServiceRecordError, repeating_callback));
 }
 
+template <typename... Args>
+void ArcBluetoothBridge::AddAdvertisementTask(
+    base::OnceCallback<void(base::OnceCallback<void(Args...)>)> task,
+    base::OnceCallback<void(Args...)> callback) {
+  advertisement_task_queue_.emplace(base::BindOnce(
+      std::move(task),
+      base::BindOnce(&ArcBluetoothBridge::CompleteAdvertisementTask<Args...>,
+                     weak_factory_.GetWeakPtr(), std::move(callback))));
+  if (advertisement_task_queue_.size() != 1)
+    return;
+  // No task pending, run immediately.
+  std::move(advertisement_task_queue_.front()).Run();
+}
+
+template <typename... Args>
+void ArcBluetoothBridge::CompleteAdvertisementTask(
+    base::OnceCallback<void(Args...)> callback,
+    Args... args) {
+  std::move(callback).Run(std::forward<Args>(args)...);
+  advertisement_task_queue_.pop();  // Current task is done. Pop it from queue.
+  if (advertisement_task_queue_.empty())
+    return;
+  std::move(advertisement_task_queue_.front()).Run();  // Run next task.
+}
+
 bool ArcBluetoothBridge::GetAdvertisementHandle(int32_t* adv_handle) {
   for (int i = 0; i < kMaxAdvertisements; i++) {
     if (advertisements_.find(i) == advertisements_.end()) {
@@ -1893,6 +1918,14 @@
 
 void ArcBluetoothBridge::ReserveAdvertisementHandle(
     ReserveAdvertisementHandleCallback callback) {
+  AddAdvertisementTask(
+      base::BindOnce(&ArcBluetoothBridge::ReserveAdvertisementHandleImpl,
+                     weak_factory_.GetWeakPtr()),
+      std::move(callback));
+}
+
+void ArcBluetoothBridge::ReserveAdvertisementHandleImpl(
+    ReserveAdvertisementHandleCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Find an empty advertisement slot.
@@ -1912,37 +1945,96 @@
   std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS, adv_handle);
 }
 
-void ArcBluetoothBridge::BroadcastAdvertisement(
+void ArcBluetoothBridge::EnableAdvertisement(
     int32_t adv_handle,
     std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
-    BroadcastAdvertisementCallback callback) {
+    EnableAdvertisementCallback callback) {
+  AddAdvertisementTask(
+      base::BindOnce(&ArcBluetoothBridge::EnableAdvertisementImpl,
+                     weak_factory_.GetWeakPtr(), adv_handle,
+                     std::move(advertisement)),
+      std::move(callback));
+}
+
+void ArcBluetoothBridge::EnableAdvertisementImpl(
+    int32_t adv_handle,
+    std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
+    EnableAdvertisementCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (advertisements_.find(adv_handle) == advertisements_.end()) {
-    std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
-    return;
-  }
 
-  if (!advertisements_[adv_handle]) {
-    OnReadyToRegisterAdvertisement(std::move(callback), adv_handle,
-                                   std::move(advertisement));
-    return;
-  }
-
-  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
-  // the callee interface.
+  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by
+  // updating the callee interface.
   auto repeating_callback =
       base::AdaptCallbackForRepeating(std::move(callback));
-  advertisements_[adv_handle]->Unregister(
+  base::Callback<void(void)> done_callback =
       base::Bind(&ArcBluetoothBridge::OnReadyToRegisterAdvertisement,
                  weak_factory_.GetWeakPtr(), repeating_callback, adv_handle,
-                 base::Passed(std::move(advertisement))),
+                 base::Passed(std::move(advertisement)));
+  base::Callback<void(BluetoothAdvertisement::ErrorCode)> error_callback =
       base::Bind(&ArcBluetoothBridge::OnRegisterAdvertisementError,
-                 weak_factory_.GetWeakPtr(), repeating_callback, adv_handle));
+                 weak_factory_.GetWeakPtr(), repeating_callback, adv_handle);
+
+  auto it = advertisements_.find(adv_handle);
+  if (it == advertisements_.end()) {
+    repeating_callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
+    return;
+  }
+  if (it->second == nullptr) {
+    done_callback.Run();
+    return;
+  }
+  it->second->Unregister(done_callback, error_callback);
+}
+
+void ArcBluetoothBridge::DisableAdvertisement(
+    int32_t adv_handle,
+    EnableAdvertisementCallback callback) {
+  AddAdvertisementTask(
+      base::BindOnce(&ArcBluetoothBridge::DisableAdvertisementImpl,
+                     weak_factory_.GetWeakPtr(), adv_handle),
+      std::move(callback));
+}
+
+void ArcBluetoothBridge::DisableAdvertisementImpl(
+    int32_t adv_handle,
+    EnableAdvertisementCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by
+  // updating the callee interface.
+  auto repeating_callback =
+      base::AdaptCallbackForRepeating(std::move(callback));
+  base::Callback<void(void)> done_callback =
+      base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementDone,
+                 weak_factory_.GetWeakPtr(), repeating_callback, adv_handle);
+  base::Callback<void(BluetoothAdvertisement::ErrorCode)> error_callback =
+      base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementError,
+                 weak_factory_.GetWeakPtr(), repeating_callback, adv_handle);
+
+  auto it = advertisements_.find(adv_handle);
+  if (it == advertisements_.end()) {
+    repeating_callback.Run(mojom::BluetoothGattStatus::GATT_FAILURE);
+    return;
+  }
+  if (it->second == nullptr) {
+    done_callback.Run();
+    return;
+  }
+  it->second->Unregister(done_callback, error_callback);
 }
 
 void ArcBluetoothBridge::ReleaseAdvertisementHandle(
     int32_t adv_handle,
     ReleaseAdvertisementHandleCallback callback) {
+  AddAdvertisementTask(
+      base::BindOnce(&ArcBluetoothBridge::ReleaseAdvertisementHandleImpl,
+                     weak_factory_.GetWeakPtr(), adv_handle),
+      std::move(callback));
+}
+
+void ArcBluetoothBridge::ReleaseAdvertisementHandleImpl(
+    int32_t adv_handle,
+    ReleaseAdvertisementHandleCallback callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (advertisements_.find(adv_handle) == advertisements_.end()) {
     std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
@@ -1955,14 +2047,14 @@
     return;
   }
 
-  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
-  // the callee interface.
+  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by
+  // updating the callee interface.
   auto repeating_callback =
       base::AdaptCallbackForRepeating(std::move(callback));
   advertisements_[adv_handle]->Unregister(
-      base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementDone,
+      base::Bind(&ArcBluetoothBridge::OnReleaseAdvertisementHandleDone,
                  weak_factory_.GetWeakPtr(), repeating_callback, adv_handle),
-      base::Bind(&ArcBluetoothBridge::OnUnregisterAdvertisementError,
+      base::Bind(&ArcBluetoothBridge::OnReleaseAdvertisementHandleError,
                  weak_factory_.GetWeakPtr(), repeating_callback, adv_handle));
 }
 
@@ -2007,7 +2099,7 @@
     ArcBluetoothBridge::GattStatusCallback callback,
     int32_t adv_handle) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  advertisements_.erase(adv_handle);
+  advertisements_[adv_handle] = nullptr;
   std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
 }
 
@@ -2018,6 +2110,25 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   LOG(WARNING) << "Failed to unregister advertisement for handle " << adv_handle
                << ", error code = " << error_code;
+  advertisements_[adv_handle] = nullptr;
+  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
+}
+
+void ArcBluetoothBridge::OnReleaseAdvertisementHandleDone(
+    ArcBluetoothBridge::GattStatusCallback callback,
+    int32_t adv_handle) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  advertisements_.erase(adv_handle);
+  std::move(callback).Run(mojom::BluetoothGattStatus::GATT_SUCCESS);
+}
+
+void ArcBluetoothBridge::OnReleaseAdvertisementHandleError(
+    ArcBluetoothBridge::GattStatusCallback callback,
+    int32_t adv_handle,
+    BluetoothAdvertisement::ErrorCode error_code) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  LOG(WARNING) << "Failed to relase advertisement handle " << adv_handle
+               << ", error code = " << error_code;
   advertisements_.erase(adv_handle);
   std::move(callback).Run(mojom::BluetoothGattStatus::GATT_FAILURE);
 }
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h
index 68508c3..d5ce371 100644
--- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h
+++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge.h
@@ -301,15 +301,36 @@
   // Set up or disable multiple advertising.
   void ReserveAdvertisementHandle(
       ReserveAdvertisementHandleCallback callback) override;
-  void BroadcastAdvertisement(
+  void EnableAdvertisement(
       int32_t adv_handle,
       std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
-      BroadcastAdvertisementCallback callback) override;
+      EnableAdvertisementCallback callback) override;
+  void DisableAdvertisement(int32_t adv_handle,
+                            DisableAdvertisementCallback callback) override;
   void ReleaseAdvertisementHandle(
       int32_t adv_handle,
       ReleaseAdvertisementHandleCallback callback) override;
 
  private:
+  template <typename... Args>
+  void AddAdvertisementTask(
+      base::OnceCallback<void(base::OnceCallback<void(Args...)>)> task,
+      base::OnceCallback<void(Args...)> callback);
+  template <typename... Args>
+  void CompleteAdvertisementTask(base::OnceCallback<void(Args...)> callback,
+                                 Args... args);
+  void ReserveAdvertisementHandleImpl(
+      ReserveAdvertisementHandleCallback callback);
+  void EnableAdvertisementImpl(
+      int32_t adv_handle,
+      std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement,
+      EnableAdvertisementCallback callback);
+  void DisableAdvertisementImpl(int32_t adv_handle,
+                                DisableAdvertisementCallback callback);
+  void ReleaseAdvertisementHandleImpl(
+      int32_t adv_handle,
+      ReleaseAdvertisementHandleCallback callback);
+
   template <typename InstanceType, typename HostType>
   class ConnectionObserverImpl;
 
@@ -466,16 +487,21 @@
       GattStatusCallback callback,
       int32_t adv_handle,
       device::BluetoothAdvertisement::ErrorCode error_code);
-  // Both of the following are called after we've tried to unregister
-  // the advertisement for |adv_handle|. Either way, we will no
-  // longer be broadcasting this advertisement, so in either case, the
-  // handle can be released.
   void OnUnregisterAdvertisementDone(GattStatusCallback callback,
                                      int32_t adv_handle);
   void OnUnregisterAdvertisementError(
       GattStatusCallback callback,
       int32_t adv_handle,
       device::BluetoothAdvertisement::ErrorCode error_code);
+  // Both of the following are called after we've tried to release |adv_handle|.
+  // Either way, we will no longer be broadcasting this advertisement, so in
+  // either case, the handle can be released.
+  void OnReleaseAdvertisementHandleDone(GattStatusCallback callback,
+                                        int32_t adv_handle);
+  void OnReleaseAdvertisementHandleError(
+      GattStatusCallback callback,
+      int32_t adv_handle,
+      device::BluetoothAdvertisement::ErrorCode error_code);
   // Find the next free advertisement handle and put it in *adv_handle,
   // or return false if the advertisement map is full.
   bool GetAdvertisementHandle(int32_t* adv_handle);
@@ -544,6 +570,7 @@
   enum { kMaxAdvertisements = 1 };
   std::map<int32_t, scoped_refptr<device::BluetoothAdvertisement>>
       advertisements_;
+  base::queue<base::OnceClosure> advertisement_task_queue_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
index 67e54b15..fa7a896 100644
--- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
@@ -127,11 +127,11 @@
     return last_adv_handle_;
   }
 
-  mojom::BluetoothGattStatus BroadcastAdvertisement(
+  mojom::BluetoothGattStatus EnableAdvertisement(
       int adv_handle,
       std::unique_ptr<device::BluetoothAdvertisement::Data> data) {
     last_status_ = mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
-    arc_bluetooth_bridge_->BroadcastAdvertisement(
+    arc_bluetooth_bridge_->EnableAdvertisement(
         adv_handle, std::move(data),
         base::BindOnce(&ArcBluetoothBridgeTest::StatusSetterCallback,
                        base::Unretained(this)));
@@ -142,6 +142,19 @@
     return last_status_;
   }
 
+  mojom::BluetoothGattStatus DisableAdvertisement(int adv_handle) {
+    last_status_ = mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
+    arc_bluetooth_bridge_->DisableAdvertisement(
+        adv_handle,
+        base::BindOnce(&ArcBluetoothBridgeTest::StatusSetterCallback,
+                       base::Unretained(this)));
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_NE(mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED,
+              last_status_);
+    return last_status_;
+  }
+
   mojom::BluetoothGattStatus ReleaseAdvertisementHandle(int adv_handle) {
     last_status_ = mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED;
     arc_bluetooth_bridge_->ReleaseAdvertisementHandle(
@@ -331,10 +344,14 @@
   auto adv_data = std::make_unique<device::BluetoothAdvertisement::Data>(
       device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST);
   mojom::BluetoothGattStatus status =
-      BroadcastAdvertisement(handle, std::move(adv_data));
+      EnableAdvertisement(handle, std::move(adv_data));
   EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
   EXPECT_EQ(1, NumActiveAdvertisements());
 
+  status = DisableAdvertisement(handle);
+  EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
+  EXPECT_EQ(0, NumActiveAdvertisements());
+
   status = ReleaseAdvertisementHandle(handle);
   EXPECT_EQ(mojom::BluetoothGattStatus::GATT_SUCCESS, status);
   EXPECT_EQ(0, NumActiveAdvertisements());
diff --git a/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc b/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc
index 8be6aa2..4a3ff6e 100644
--- a/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc
+++ b/chrome/browser/chromeos/dbus/chrome_component_updater_service_provider_delegate.cc
@@ -26,6 +26,8 @@
     case component_updater::CrOSComponentManager::Error::
         COMPATIBILITY_CHECK_FAILED:
       return "COMPATIBILITY_CHECK_FAILED";
+    case component_updater::CrOSComponentManager::Error::ERROR_MAX:
+      return "ERROR_MAX";
   }
   return "Unknown error code";
 }
diff --git a/chrome/browser/chromeos/policy/active_directory_policy_manager.cc b/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
index d606c99..1c0b365 100644
--- a/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
+++ b/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
@@ -14,10 +14,11 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
 #include "components/policy/core/common/policy_bundle.h"
-#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/policy_constants.h"
 #include "net/url_request/url_request_context_getter.h"
 
+namespace policy {
 namespace {
 
 // Fetch policy every 90 minutes which matches the Windows default:
@@ -29,35 +30,20 @@
   std::move(callback).Run(error == authpolicy::ERROR_NONE);
 }
 
+// Gets the AuthPolicy D-Bus interface.
+chromeos::AuthPolicyClient* GetAuthPolicyClient() {
+  chromeos::DBusThreadManager* thread_manager =
+      chromeos::DBusThreadManager::Get();
+  DCHECK(thread_manager);
+  chromeos::AuthPolicyClient* auth_policy_client =
+      thread_manager->GetAuthPolicyClient();
+  DCHECK(auth_policy_client);
+  return auth_policy_client;
+}
+
 }  // namespace
 
-namespace policy {
-
-ActiveDirectoryPolicyManager::~ActiveDirectoryPolicyManager() {}
-
-// static
-std::unique_ptr<ActiveDirectoryPolicyManager>
-ActiveDirectoryPolicyManager::CreateForDevicePolicy(
-    std::unique_ptr<CloudPolicyStore> store) {
-  // Can't use MakeUnique<> because the constructor is private.
-  return base::WrapUnique(new ActiveDirectoryPolicyManager(
-      EmptyAccountId(), base::TimeDelta(), base::OnceClosure(),
-      std::move(store), nullptr /* external_data_manager */));
-}
-
-// static
-std::unique_ptr<ActiveDirectoryPolicyManager>
-ActiveDirectoryPolicyManager::CreateForUserPolicy(
-    const AccountId& account_id,
-    base::TimeDelta initial_policy_fetch_timeout,
-    base::OnceClosure exit_session,
-    std::unique_ptr<CloudPolicyStore> store,
-    std::unique_ptr<CloudExternalDataManager> external_data_manager) {
-  // Can't use MakeUnique<> because the constructor is private.
-  return base::WrapUnique(new ActiveDirectoryPolicyManager(
-      account_id, initial_policy_fetch_timeout, std::move(exit_session),
-      std::move(store), std::move(external_data_manager)));
-}
+ActiveDirectoryPolicyManager::~ActiveDirectoryPolicyManager() = default;
 
 void ActiveDirectoryPolicyManager::Init(SchemaRegistry* registry) {
   ConfigurationPolicyProvider::Init(registry);
@@ -96,12 +82,8 @@
 
 bool ActiveDirectoryPolicyManager::IsInitializationComplete(
     PolicyDomain domain) const {
-  if (waiting_for_initial_policy_fetch_) {
-    return false;
-  }
-  if (domain == POLICY_DOMAIN_CHROME) {
+  if (domain == POLICY_DOMAIN_CHROME)
     return store_->is_initialized();
-  }
   return true;
 }
 
@@ -133,36 +115,11 @@
   }
 }
 
-void ActiveDirectoryPolicyManager::ForceTimeoutForTest() {
-  DCHECK(initial_policy_timeout_.IsRunning());
-  // Stop the timer to mimic what happens when a real timer fires, then invoke
-  // the timer callback directly.
-  initial_policy_timeout_.Stop();
-  OnBlockingFetchTimeout();
-}
-
 ActiveDirectoryPolicyManager::ActiveDirectoryPolicyManager(
-    const AccountId& account_id,
-    base::TimeDelta initial_policy_fetch_timeout,
-    base::OnceClosure exit_session,
     std::unique_ptr<CloudPolicyStore> store,
     std::unique_ptr<CloudExternalDataManager> external_data_manager)
-    : account_id_(account_id),
-      waiting_for_initial_policy_fetch_(
-          !initial_policy_fetch_timeout.is_zero()),
-      initial_policy_fetch_may_fail_(!initial_policy_fetch_timeout.is_max()),
-      exit_session_(std::move(exit_session)),
-      store_(std::move(store)),
-      external_data_manager_(std::move(external_data_manager)) {
-  // Delaying initialization complete is intended for user policy only.
-  DCHECK(account_id != EmptyAccountId() || !waiting_for_initial_policy_fetch_);
-  if (waiting_for_initial_policy_fetch_ && initial_policy_fetch_may_fail_) {
-    initial_policy_timeout_.Start(
-        FROM_HERE, initial_policy_fetch_timeout,
-        base::Bind(&ActiveDirectoryPolicyManager::OnBlockingFetchTimeout,
-                   weak_ptr_factory_.GetWeakPtr()));
-  }
-}
+    : store_(std::move(store)),
+      external_data_manager_(std::move(external_data_manager)) {}
 
 void ActiveDirectoryPolicyManager::PublishPolicy() {
   if (!store_->is_initialized()) {
@@ -181,23 +138,6 @@
   UpdatePolicy(std::move(bundle));
 }
 
-void ActiveDirectoryPolicyManager::DoPolicyFetch(
-    base::OnceCallback<void(bool success)> callback) {
-  chromeos::DBusThreadManager* thread_manager =
-      chromeos::DBusThreadManager::Get();
-  DCHECK(thread_manager);
-  chromeos::AuthPolicyClient* auth_policy_client =
-      thread_manager->GetAuthPolicyClient();
-  DCHECK(auth_policy_client);
-  if (account_id_ == EmptyAccountId()) {
-    auth_policy_client->RefreshDevicePolicy(
-        base::BindOnce(&RunRefreshCallback, std::move(callback)));
-  } else {
-    auth_policy_client->RefreshUserPolicy(
-        account_id_, base::BindOnce(&RunRefreshCallback, std::move(callback)));
-  }
-}
-
 void ActiveDirectoryPolicyManager::OnPolicyFetched(bool success) {
   fetch_ever_completed_ = true;
   if (success) {
@@ -215,14 +155,54 @@
   store_->Load();
 }
 
-void ActiveDirectoryPolicyManager::OnBlockingFetchTimeout() {
-  DCHECK(waiting_for_initial_policy_fetch_);
-  LOG(WARNING) << "Timed out while waiting for the policy fetch. "
-               << "The session will start with the cached policy.";
-  CancelWaitForInitialPolicy(false);
+UserActiveDirectoryPolicyManager::UserActiveDirectoryPolicyManager(
+    const AccountId& account_id,
+    base::TimeDelta initial_policy_fetch_timeout,
+    base::OnceClosure exit_session,
+    std::unique_ptr<CloudPolicyStore> store,
+    std::unique_ptr<CloudExternalDataManager> external_data_manager)
+    : ActiveDirectoryPolicyManager(std::move(store),
+                                   std::move(external_data_manager)),
+      account_id_(account_id),
+      waiting_for_initial_policy_fetch_(
+          !initial_policy_fetch_timeout.is_zero()),
+      initial_policy_fetch_may_fail_(!initial_policy_fetch_timeout.is_max()),
+      exit_session_(std::move(exit_session)) {
+  // Delaying initialization complete is intended for user policy only.
+  if (waiting_for_initial_policy_fetch_ && initial_policy_fetch_may_fail_) {
+    initial_policy_timeout_.Start(
+        FROM_HERE, initial_policy_fetch_timeout,
+        base::Bind(&UserActiveDirectoryPolicyManager::OnBlockingFetchTimeout,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
 }
 
-void ActiveDirectoryPolicyManager::CancelWaitForInitialPolicy(bool success) {
+UserActiveDirectoryPolicyManager::~UserActiveDirectoryPolicyManager() = default;
+
+bool UserActiveDirectoryPolicyManager::IsInitializationComplete(
+    PolicyDomain domain) const {
+  if (waiting_for_initial_policy_fetch_)
+    return false;
+
+  return ActiveDirectoryPolicyManager::IsInitializationComplete(domain);
+}
+
+void UserActiveDirectoryPolicyManager::ForceTimeoutForTesting() {
+  DCHECK(initial_policy_timeout_.IsRunning());
+  // Stop the timer to mimic what happens when a real timer fires, then invoke
+  // the timer callback directly.
+  initial_policy_timeout_.Stop();
+  OnBlockingFetchTimeout();
+}
+
+void UserActiveDirectoryPolicyManager::DoPolicyFetch(
+    PolicyScheduler::TaskCallback callback) {
+  GetAuthPolicyClient()->RefreshUserPolicy(
+      account_id_, base::BindOnce(&RunRefreshCallback, std::move(callback)));
+}
+
+void UserActiveDirectoryPolicyManager::CancelWaitForInitialPolicy(
+    bool success) {
   if (!waiting_for_initial_policy_fetch_)
     return;
 
@@ -231,7 +211,7 @@
   // If the conditions to continue profile initialization are not met, the user
   // session is exited and initialization is not set as completed.
   // TODO(tnagel): Maybe add code to retry policy fetch?
-  if (!store_->has_policy()) {
+  if (!store()->has_policy()) {
     // If there's no policy at all (not even cached) the user session must not
     // continue.
     LOG(ERROR) << "Policy could not be obtained. "
@@ -260,4 +240,25 @@
   PublishPolicy();
 }
 
+void UserActiveDirectoryPolicyManager::OnBlockingFetchTimeout() {
+  DCHECK(waiting_for_initial_policy_fetch_);
+  LOG(WARNING) << "Timed out while waiting for the policy fetch. "
+               << "The session will start with the cached policy.";
+  CancelWaitForInitialPolicy(false /* success */);
+}
+
+DeviceActiveDirectoryPolicyManager::DeviceActiveDirectoryPolicyManager(
+    std::unique_ptr<CloudPolicyStore> store)
+    : ActiveDirectoryPolicyManager(std::move(store),
+                                   nullptr /* external_data_manager */) {}
+
+DeviceActiveDirectoryPolicyManager::~DeviceActiveDirectoryPolicyManager() =
+    default;
+
+void DeviceActiveDirectoryPolicyManager::DoPolicyFetch(
+    base::OnceCallback<void(bool success)> callback) {
+  GetAuthPolicyClient()->RefreshDevicePolicy(
+      base::BindOnce(&RunRefreshCallback, std::move(callback)));
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/active_directory_policy_manager.h b/chrome/browser/chromeos/policy/active_directory_policy_manager.h
index ebcf0cd..135868a 100644
--- a/chrome/browser/chromeos/policy/active_directory_policy_manager.h
+++ b/chrome/browser/chromeos/policy/active_directory_policy_manager.h
@@ -21,9 +21,8 @@
 
 class CloudExternalDataManager;
 
-// ConfigurationPolicyProvider for device or user policy from Active Directory.
-// The choice of constructor determines whether device or user policy is
-// provided.
+// ConfigurationPolicyProvider for policy from Active Directory.
+// Derived classes implement specializations for user and device policy.
 // Data flow: Triggered by DoPolicyFetch(), policy is fetched by authpolicyd and
 // stored in session manager with completion indicated by OnPolicyFetched().
 // From there policy load from session manager is triggered, completion of which
@@ -33,18 +32,6 @@
  public:
   ~ActiveDirectoryPolicyManager() override;
 
-  // Create manager for device policy.
-  static std::unique_ptr<ActiveDirectoryPolicyManager> CreateForDevicePolicy(
-      std::unique_ptr<CloudPolicyStore> store);
-
-  // Create manager for |accound_id| user policy.
-  static std::unique_ptr<ActiveDirectoryPolicyManager> CreateForUserPolicy(
-      const AccountId& account_id,
-      base::TimeDelta initial_policy_fetch_timeout,
-      base::OnceClosure exit_session,
-      std::unique_ptr<CloudPolicyStore> store,
-      std::unique_ptr<CloudExternalDataManager> external_data_manager);
-
   // ConfigurationPolicyProvider:
   void Init(SchemaRegistry* registry) override;
   void Shutdown() override;
@@ -56,28 +43,13 @@
   void OnStoreError(CloudPolicyStore* cloud_policy_store) override;
 
   CloudPolicyStore* store() const { return store_.get(); }
+  CloudExternalDataManager* external_data_manager() const {
+    return external_data_manager_.get();
+  }
   PolicyScheduler* scheduler() { return scheduler_.get(); }
 
-  // Helper function to force a policy fetch timeout.
-  void ForceTimeoutForTest();
-
- private:
-  // |account_id| specifies the user to manage policy for. If |account_id| is
-  // empty, device policy is managed.
-  //
-  // The following applies to user policy only: If
-  // |initial_policy_fetch_timeout| is non-zero, IsInitializationComplete() is
-  // forced to false until either there has been a successful policy fetch from
-  // the server and a subsequent successful load from session manager or
-  // |initial_policy_fetch_timeout| has expired and there has been a successful
-  // load from session manager. The timeout may be set to TimeDelta::Max() to
-  // enforce successful policy fetch. In case the conditions for signaling
-  // initialization complete are not met, the user session is aborted by calling
-  // |exit_session|.
+ protected:
   ActiveDirectoryPolicyManager(
-      const AccountId& account_id,
-      base::TimeDelta initial_policy_fetch_timeout,
-      base::OnceClosure exit_session,
       std::unique_ptr<CloudPolicyStore> store,
       std::unique_ptr<CloudExternalDataManager> external_data_manager);
 
@@ -86,48 +58,27 @@
 
   // Calls into authpolicyd to fetch policy. Reports success or failure via
   // |callback|.
-  void DoPolicyFetch(PolicyScheduler::TaskCallback callback);
+  virtual void DoPolicyFetch(PolicyScheduler::TaskCallback callback) = 0;
 
+  // Allows derived classes to cancel waiting for the initial policy fetch/load
+  // and to flag the ConfigurationPolicyProvider ready (assuming all other
+  // initialization tasks have completed) or to exit the session in case the
+  // requirements to continue have not been met. |success| denotes whether the
+  // policy fetch was successful.
+  virtual void CancelWaitForInitialPolicy(bool success) {}
+
+ private:
   // Called by scheduler with result of policy fetch. This covers policy
   // download, parsing and storing into session manager. (To access and publish
   // the policy, the store needs to be reloaded from session manager.)
   void OnPolicyFetched(bool success);
 
-  // Called when |initial_policy_timeout_| times out, to cancel the blocking
-  // wait for the initial policy fetch.
-  void OnBlockingFetchTimeout();
-
-  // Cancels waiting for the initial policy fetch/load and flags the
-  // ConfigurationPolicyProvider ready (assuming all other initialization tasks
-  // have completed) or exits the session in case the requirements to continue
-  // have not been met. |success| denotes whether the policy fetch was
-  // successful.
-  void CancelWaitForInitialPolicy(bool success);
-
-  const AccountId account_id_;
-
-  // Whether we're waiting for a policy fetch to complete before reporting
-  // IsInitializationComplete().
-  bool waiting_for_initial_policy_fetch_;
-
-  // Whether the user session is continued in case of failure of initial policy
-  // fetch.
-  bool initial_policy_fetch_may_fail_;
-
   // Whether policy fetch has ever been reported as completed by authpolicyd.
   bool fetch_ever_completed_ = false;
 
   // Whether policy fetch has ever been reported as successful by authpolicyd.
   bool fetch_ever_succeeded_ = false;
 
-  // A timer that puts a hard limit on the maximum time to wait for the initial
-  // policy fetch/load.
-  base::Timer initial_policy_timeout_{false /* retain_user_task */,
-                                      false /* is_repeating */};
-
-  // Callback to exit the session.
-  base::OnceClosure exit_session_;
-
   // Store used to serialize policy, usually sends data to Session Manager.
   std::unique_ptr<CloudPolicyStore> store_;
 
@@ -142,6 +93,82 @@
   DISALLOW_COPY_AND_ASSIGN(ActiveDirectoryPolicyManager);
 };
 
+// Manages user policy for Active Directory managed devices.
+class UserActiveDirectoryPolicyManager : public ActiveDirectoryPolicyManager {
+ public:
+  // If |initial_policy_fetch_timeout| is non-zero, IsInitializationComplete()
+  // is forced to false until either there has been a successful policy fetch
+  // from the server and a subsequent successful load from session manager or
+  // |initial_policy_fetch_timeout| has expired and there has been a successful
+  // load from session manager. The timeout may be set to TimeDelta::Max() to
+  // enforce successful policy fetch. In case the conditions for signaling
+  // initialization complete are not met, the user session is aborted by calling
+  // |exit_session|.
+  UserActiveDirectoryPolicyManager(
+      const AccountId& account_id,
+      base::TimeDelta initial_policy_fetch_timeout,
+      base::OnceClosure exit_session,
+      std::unique_ptr<CloudPolicyStore> store,
+      std::unique_ptr<CloudExternalDataManager> external_data_manager);
+
+  ~UserActiveDirectoryPolicyManager() override;
+
+  // ConfigurationPolicyProvider:
+  bool IsInitializationComplete(PolicyDomain domain) const override;
+
+  // Helper function to force a policy fetch timeout.
+  void ForceTimeoutForTesting();
+
+ protected:
+  // ActiveDirectoryPolicyManager:
+  void DoPolicyFetch(PolicyScheduler::TaskCallback callback) override;
+  void CancelWaitForInitialPolicy(bool success) override;
+
+ private:
+  // Called when |initial_policy_timeout_| times out, to cancel the blocking
+  // wait for the initial policy fetch.
+  void OnBlockingFetchTimeout();
+
+  // The user's account id.
+  AccountId account_id_;
+
+  // Whether we're waiting for a policy fetch to complete before reporting
+  // IsInitializationComplete().
+  bool waiting_for_initial_policy_fetch_ = false;
+
+  // Whether the user session is continued in case of failure of initial policy
+  // fetch.
+  bool initial_policy_fetch_may_fail_ = false;
+
+  // A timer that puts a hard limit on the maximum time to wait for the initial
+  // policy fetch/load.
+  base::Timer initial_policy_timeout_{false /* retain_user_task */,
+                                      false /* is_repeating */};
+
+  // Callback to exit the session.
+  base::OnceClosure exit_session_;
+
+  // Must be last member.
+  base::WeakPtrFactory<UserActiveDirectoryPolicyManager> weak_ptr_factory_{
+      this};
+
+  DISALLOW_COPY_AND_ASSIGN(UserActiveDirectoryPolicyManager);
+};
+
+// Manages device policy for Active Directory managed devices.
+class DeviceActiveDirectoryPolicyManager : public ActiveDirectoryPolicyManager {
+ public:
+  DeviceActiveDirectoryPolicyManager(std::unique_ptr<CloudPolicyStore> store);
+  ~DeviceActiveDirectoryPolicyManager() override;
+
+ protected:
+  // ActiveDirectoryPolicyManager:
+  void DoPolicyFetch(PolicyScheduler::TaskCallback callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeviceActiveDirectoryPolicyManager);
+};
+
 }  // namespace policy
 
 #endif  // CHROME_BROWSER_CHROMEOS_POLICY_ACTIVE_DIRECTORY_POLICY_MANAGER_H_
diff --git a/chrome/browser/chromeos/policy/active_directory_policy_manager_unittest.cc b/chrome/browser/chromeos/policy/active_directory_policy_manager_unittest.cc
index 53f0c21..7188d96 100644
--- a/chrome/browser/chromeos/policy/active_directory_policy_manager_unittest.cc
+++ b/chrome/browser/chromeos/policy/active_directory_policy_manager_unittest.cc
@@ -87,70 +87,92 @@
 // been fired.
 class ActiveDirectoryPolicyManagerTest : public testing::Test {
  public:
-  ActiveDirectoryPolicyManagerTest() {
+  ActiveDirectoryPolicyManagerTest() = default;
+
+  // testing::Test overrides:
+  void SetUp() override {
     auto mock_client_unique_ptr = std::make_unique<TestAuthPolicyClient>();
     mock_client_ = mock_client_unique_ptr.get();
     chromeos::DBusThreadManager::GetSetterForTesting()->SetAuthPolicyClient(
         std::move(mock_client_unique_ptr));
   }
 
-  ~ActiveDirectoryPolicyManagerTest() override {
-    EXPECT_EQ(session_exit_expected_, session_exited_);
-    if (mock_external_data_manager_)
-      EXPECT_CALL(*mock_external_data_manager_, Disconnect());
+  void TearDown() override {
+    if (mock_external_data_manager())
+      EXPECT_CALL(*mock_external_data_manager(), Disconnect());
     policy_manager_->Shutdown();
   }
 
  protected:
-  // Creates |mock_store_|, |mock_external_data_manager_| and |policy_manager_|
-  // with fake AD account id and |initial_policy_fetch_timeout| as timeout.
-  void CreateUserPolicyManager(base::TimeDelta initial_policy_fetch_timeout) {
-    auto account_id = AccountId::AdFromUserEmailObjGuid("bla", "ble");
-
-    auto mock_store_unique_ptr = std::make_unique<MockCloudPolicyStore>();
-    mock_store_ = mock_store_unique_ptr.get();
-
-    auto mock_external_data_manager_unique_ptr =
-        std::make_unique<MockCloudExternalDataManager>();
-    mock_external_data_manager_ = mock_external_data_manager_unique_ptr.get();
-
-    base::OnceClosure exit_session = base::BindOnce(
-        &ActiveDirectoryPolicyManagerTest::ExitSession, base::Unretained(this));
-
-    policy_manager_ = ActiveDirectoryPolicyManager::CreateForUserPolicy(
-        account_id, initial_policy_fetch_timeout, std::move(exit_session),
-        std::move(mock_store_unique_ptr),
-        std::move(mock_external_data_manager_unique_ptr));
-
-    ASSERT_TRUE(policy_manager_);
-    EXPECT_FALSE(
-        policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  // Gets the store passed to the policy manager during construction.
+  MockCloudPolicyStore* mock_store() {
+    DCHECK(policy_manager_);
+    return static_cast<MockCloudPolicyStore*>(policy_manager_->store());
   }
 
-  // Creates |mock_store_| and |policy_manager_|.
-  void CreateDevicePolicyManager() {
-    auto mock_store_unique_ptr = std::make_unique<MockCloudPolicyStore>();
-    mock_store_ = mock_store_unique_ptr.get();
-
-    policy_manager_ = ActiveDirectoryPolicyManager::CreateForDevicePolicy(
-        std::move(mock_store_unique_ptr));
-
-    ASSERT_TRUE(policy_manager_);
-    EXPECT_FALSE(
-        policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  // Gets the store passed to the policy manager during construction.
+  MockCloudExternalDataManager* mock_external_data_manager() {
+    DCHECK(policy_manager_);
+    return static_cast<MockCloudExternalDataManager*>(
+        policy_manager_->external_data_manager());
   }
 
   // Initializes the policy manager and verifies expectations on mock classes.
   void InitPolicyManagerAndVerifyExpectations() {
-    EXPECT_CALL(*mock_store_, Load());
-    if (mock_external_data_manager_) {
-      EXPECT_CALL(*mock_external_data_manager_,
+    EXPECT_CALL(*mock_store(), Load());
+    if (mock_external_data_manager()) {
+      EXPECT_CALL(*mock_external_data_manager(),
                   Connect(scoped_refptr<net::URLRequestContextGetter>()));
     }
     policy_manager_->Init(&schema_registry_);
-    testing::Mock::VerifyAndClearExpectations(mock_store_);
-    if (mock_external_data_manager_)
-      testing::Mock::VerifyAndClearExpectations(mock_external_data_manager_);
+    testing::Mock::VerifyAndClearExpectations(mock_store());
+    if (mock_external_data_manager())
+      testing::Mock::VerifyAndClearExpectations(mock_external_data_manager());
+  }
+
+  // Owned by DBusThreadManager.
+  TestAuthPolicyClient* mock_client_ = nullptr;
+
+  // Initialized by the individual tests but owned by the test class so that it
+  // can be shut down automatically after the test has run.
+  std::unique_ptr<ActiveDirectoryPolicyManager> policy_manager_;
+
+  SchemaRegistry schema_registry_;
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  DISALLOW_COPY_AND_ASSIGN(ActiveDirectoryPolicyManagerTest);
+};
+
+class UserActiveDirectoryPolicyManagerTest
+    : public ActiveDirectoryPolicyManagerTest {
+ public:
+  ~UserActiveDirectoryPolicyManagerTest() override {
+    EXPECT_EQ(session_exit_expected_, session_exited_);
+  }
+
+ protected:
+  UserActiveDirectoryPolicyManager* user_policy_manager() const {
+    return static_cast<UserActiveDirectoryPolicyManager*>(
+        policy_manager_.get());
+  }
+
+  // Creates |policy_manager_| with fake AD account id and
+  // |initial_policy_fetch_timeout| as timeout.
+  void CreatePolicyManager(base::TimeDelta initial_policy_fetch_timeout) {
+    auto account_id = AccountId::AdFromUserEmailObjGuid("bla", "ble");
+
+    base::OnceClosure exit_session =
+        base::BindOnce(&UserActiveDirectoryPolicyManagerTest::ExitSession,
+                       base::Unretained(this));
+
+    policy_manager_ = std::make_unique<UserActiveDirectoryPolicyManager>(
+        account_id, initial_policy_fetch_timeout, std::move(exit_session),
+        std::make_unique<MockCloudPolicyStore>(),
+        std::make_unique<MockCloudExternalDataManager>());
+
+    EXPECT_FALSE(
+        policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
   }
 
   // Expect that session exit will be called below. (Must only be called once.)
@@ -175,26 +197,10 @@
 
   bool session_exited_ = false;
   bool session_exit_expected_ = false;
-
-  // Created by CreateUserPolicyManager().
-  MockCloudPolicyStore* mock_store_ = nullptr;
-  MockCloudExternalDataManager* mock_external_data_manager_ = nullptr;
-
-  // Owned by DBusThreadManager.
-  TestAuthPolicyClient* mock_client_ = nullptr;
-
-  SchemaRegistry schema_registry_;
-
-  // Initialized by the individual tests but owned by the test class so that it
-  // can be shut down automatically after the test has run.
-  std::unique_ptr<ActiveDirectoryPolicyManager> policy_manager_;
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
-TEST_F(ActiveDirectoryPolicyManagerTest, UserManager_DontWait) {
-  CreateUserPolicyManager(base::TimeDelta());
+TEST_F(UserActiveDirectoryPolicyManagerTest, DontWait) {
+  CreatePolicyManager(base::TimeDelta());
 
   // Configure mock policy fetch to fail.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_UNKNOWN);
@@ -205,24 +211,24 @@
 
   // Simulate failed store load. Initialization is reported complete at this
   // point.
-  mock_store_->NotifyStoreError();
+  mock_store()->NotifyStoreError();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Simulate failed store load.
-  mock_store_->NotifyStoreError();
+  mock_store()->NotifyStoreError();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
 // If the initial fetch timeout is infinite, initialization is only complete
 // after policy has been fetched and after that has been loaded.
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitInfinite_LoadSuccess_FetchSuccess) {
-  CreateUserPolicyManager(base::TimeDelta::Max());
+TEST_F(UserActiveDirectoryPolicyManagerTest,
+       WaitInfinite_LoadSuccess_FetchSuccess) {
+  CreatePolicyManager(base::TimeDelta::Max());
 
   // Configure mock policy fetch to succeed.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_NONE);
@@ -231,25 +237,25 @@
   InitPolicyManagerAndVerifyExpectations();
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Simulate successful store load. At this point initialization is complete.
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
 // If the initial fetch timeout is infinite, initialization does not complete if
 // load after fetch fails.
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitInfinite_LoadSuccess_FetchSuccess_LoadFail) {
-  CreateUserPolicyManager(base::TimeDelta::Max());
+TEST_F(UserActiveDirectoryPolicyManagerTest,
+       WaitInfinite_LoadSuccess_FetchSuccess_LoadFail) {
+  CreatePolicyManager(base::TimeDelta::Max());
 
   // Configure mock policy fetch to succeed.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_NONE);
@@ -258,33 +264,33 @@
   InitPolicyManagerAndVerifyExpectations();
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   ExpectSessionExit();
 
   // Simulate failed store load.
-  mock_store_->NotifyStoreError();
+  mock_store()->NotifyStoreError();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   ExpectSessionExited();
 
   // Simulate successful store load.
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
 // If the initial fetch timeout is infinite, failure in policy fetch prevents
 // initialization from finishing, ever.
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitInfinite_LoadSuccess_FetchFail) {
-  CreateUserPolicyManager(base::TimeDelta::Max());
+TEST_F(UserActiveDirectoryPolicyManagerTest,
+       WaitInfinite_LoadSuccess_FetchFail) {
+  CreatePolicyManager(base::TimeDelta::Max());
 
   // Configure mock policy fetch to fail.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_UNKNOWN);
@@ -293,30 +299,29 @@
   InitPolicyManagerAndVerifyExpectations();
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   ExpectSessionExit();
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   ExpectSessionExited();
 
   // Simulate successful store load.
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
 // If the initial fetch timeout is not infinite, we're in best-effort mode but
 // still require the policy load to succeed so that there's *some* policy
 // present (though possibly outdated).
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitFinite_LoadSuccess_FetchFail) {
-  CreateUserPolicyManager(base::TimeDelta::FromDays(365));
+TEST_F(UserActiveDirectoryPolicyManagerTest, WaitFinite_LoadSuccess_FetchFail) {
+  CreatePolicyManager(base::TimeDelta::FromDays(365));
 
   // Configure mock policy fetch to fail.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_UNKNOWN);
@@ -325,19 +330,19 @@
   InitPolicyManagerAndVerifyExpectations();
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Process reply for mock policy fetch. At this point initialization is
   // complete (we have waited for the fetch but now that we know it has failed
   // we continue).
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Simulate successful store load.
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
@@ -345,9 +350,8 @@
 // still require the policy load to succeed so that there's *some* policy
 // present (though possibly outdated). Here the sequence is inverted: Fetch
 // returns before load.
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitFinite_FetchFail_LoadSuccess) {
-  CreateUserPolicyManager(base::TimeDelta::FromDays(365));
+TEST_F(UserActiveDirectoryPolicyManagerTest, WaitFinite_FetchFail_LoadSuccess) {
+  CreatePolicyManager(base::TimeDelta::FromDays(365));
 
   // Configure mock policy fetch to fail.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_UNKNOWN);
@@ -356,21 +360,20 @@
   InitPolicyManagerAndVerifyExpectations();
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
 // If the initial fetch timeout is not infinite, we're in best-effort mode but
 // if we can't load existing policy from disk we have to give up.
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitFinite_LoadFail_FetchFail) {
-  CreateUserPolicyManager(base::TimeDelta::FromDays(365));
+TEST_F(UserActiveDirectoryPolicyManagerTest, WaitFinite_LoadFail_FetchFail) {
+  CreatePolicyManager(base::TimeDelta::FromDays(365));
 
   // Configure mock policy fetch to fail.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_UNKNOWN);
@@ -379,30 +382,30 @@
   InitPolicyManagerAndVerifyExpectations();
 
   // Simulate failed store load.
-  mock_store_->NotifyStoreError();
+  mock_store()->NotifyStoreError();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   ExpectSessionExit();
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   ExpectSessionExited();
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
 // If the initial fetch timeout is not infinite, we're in best-effort mode and
 // upon timeout initialization is complete if any policy could be loaded from
 // disk.
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitFinite_LoadSuccess_FetchTimeout) {
-  CreateUserPolicyManager(base::TimeDelta::FromDays(365));
+TEST_F(UserActiveDirectoryPolicyManagerTest,
+       WaitFinite_LoadSuccess_FetchTimeout) {
+  CreatePolicyManager(base::TimeDelta::FromDays(365));
 
   // Configure mock policy fetch to fail.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_UNKNOWN);
@@ -411,29 +414,29 @@
   InitPolicyManagerAndVerifyExpectations();
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Simulate policy fetch timeout.
-  policy_manager_->ForceTimeoutForTest();
+  user_policy_manager()->ForceTimeoutForTesting();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Simulate failed store load.
-  mock_store_->NotifyStoreError();
+  mock_store()->NotifyStoreError();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
 // If the initial fetch timeout is not infinite, we're in best-effort mode but
 // without a successful policy load we still can't continue.
-TEST_F(ActiveDirectoryPolicyManagerTest,
-       UserManager_WaitFinite_LoadTimeout_FetchTimeout) {
-  CreateUserPolicyManager(base::TimeDelta::FromDays(365));
+TEST_F(UserActiveDirectoryPolicyManagerTest,
+       WaitFinite_LoadTimeout_FetchTimeout) {
+  CreatePolicyManager(base::TimeDelta::FromDays(365));
 
   // Configure mock policy fetch to fail.
   mock_client_->SetRefreshUserPolicyCallbackError(authpolicy::ERROR_UNKNOWN);
@@ -444,24 +447,37 @@
   ExpectSessionExit();
 
   // Simulate policy fetch timeout.
-  policy_manager_->ForceTimeoutForTest();
+  user_policy_manager()->ForceTimeoutForTesting();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   ExpectSessionExited();
 
   // Process reply for mock policy fetch.
-  EXPECT_CALL(*mock_store_, Load());
+  EXPECT_CALL(*mock_store(), Load());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 
   // Simulate successful store load.
-  mock_store_->policy_ = std::make_unique<enterprise_management::PolicyData>();
-  mock_store_->NotifyStoreLoaded();
+  mock_store()->policy_ = std::make_unique<enterprise_management::PolicyData>();
+  mock_store()->NotifyStoreLoaded();
   EXPECT_TRUE(policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
 }
 
-TEST_F(ActiveDirectoryPolicyManagerTest, DeviceManager_Initialization) {
-  CreateDevicePolicyManager();
+class DeviceActiveDirectoryPolicyManagerTest
+    : public ActiveDirectoryPolicyManagerTest {
+ protected:
+  // Creates |mock_store()| and |policy_manager_|.
+  void CreatePolicyManager() {
+    policy_manager_ = std::make_unique<DeviceActiveDirectoryPolicyManager>(
+        std::make_unique<MockCloudPolicyStore>());
+
+    EXPECT_FALSE(
+        policy_manager_->IsInitializationComplete(POLICY_DOMAIN_CHROME));
+  }
+};
+
+TEST_F(DeviceActiveDirectoryPolicyManagerTest, Initialization) {
+  CreatePolicyManager();
   InitPolicyManagerAndVerifyExpectations();
 }
 
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
index 82419315..a7ac918a 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
@@ -120,9 +120,8 @@
           ->StartAuthPolicyService();
 
       device_active_directory_policy_manager_ =
-          ActiveDirectoryPolicyManager::CreateForDevicePolicy(
-              std::move(device_cloud_policy_store))
-              .release();
+          new DeviceActiveDirectoryPolicyManager(
+              std::move(device_cloud_policy_store));
       providers_for_init_.push_back(
           base::WrapUnique<ConfigurationPolicyProvider>(
               device_active_directory_policy_manager_));
diff --git a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
index 5888138c..27ad44e8 100644
--- a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
@@ -356,11 +356,10 @@
     store->LoadImmediately();
 
   if (is_active_directory) {
-    std::unique_ptr<ActiveDirectoryPolicyManager> manager =
-        ActiveDirectoryPolicyManager::CreateForUserPolicy(
-            account_id, policy_refresh_timeout,
-            base::BindOnce(&chrome::AttemptUserExit), std::move(store),
-            std::move(external_data_manager));
+    auto manager = std::make_unique<UserActiveDirectoryPolicyManager>(
+        account_id, policy_refresh_timeout,
+        base::BindOnce(&chrome::AttemptUserExit), std::move(store),
+        std::move(external_data_manager));
     manager->Init(
         SchemaRegistryServiceFactory::GetForContext(profile)->registry());
 
diff --git a/chrome/browser/component_updater/cros_component_installer.cc b/chrome/browser/component_updater/cros_component_installer.cc
index adb838a..3da68e16 100644
--- a/chrome/browser/component_updater/cros_component_installer.cc
+++ b/chrome/browser/component_updater/cros_component_installer.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/files/file_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task_scheduler/post_task.h"
@@ -86,6 +87,13 @@
   return configs;
 }
 
+// Report Error code.
+CrOSComponentManager::Error ReportError(CrOSComponentManager::Error error) {
+  UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.ChromeOS.InstallResult", error,
+                            CrOSComponentManager::Error::ERROR_MAX);
+  return error;
+}
+
 }  // namespace
 
 CrOSComponentInstallerPolicy::CrOSComponentInstallerPolicy(
@@ -202,8 +210,9 @@
     LoadInternal(name, std::move(load_callback));
   } else {
     // A compatible component is installed, do not load it.
-    base::PostTask(FROM_HERE, base::BindOnce(std::move(load_callback),
-                                             Error::NONE, base::FilePath()));
+    base::PostTask(FROM_HERE,
+                   base::BindOnce(std::move(load_callback),
+                                  ReportError(Error::NONE), base::FilePath()));
   }
 }
 
@@ -259,7 +268,8 @@
   if (!config) {
     base::PostTask(FROM_HERE,
                    base::BindOnce(std::move(load_callback),
-                                  Error::UNKNOWN_COMPONENT, base::FilePath()));
+                                  ReportError(Error::UNKNOWN_COMPONENT),
+                                  base::FilePath()));
     return;
   }
   Register(
@@ -283,14 +293,16 @@
                                          LoadCallback load_callback,
                                          update_client::Error error) {
   if (error != update_client::Error::NONE) {
-    base::PostTask(FROM_HERE,
-                   base::BindOnce(std::move(load_callback),
-                                  Error::INSTALL_FAILURE, base::FilePath()));
+    base::PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(load_callback),
+                       ReportError(Error::INSTALL_FAILURE), base::FilePath()));
   } else if (mount_policy == MountPolicy::kMount) {
     LoadInternal(name, std::move(load_callback));
   } else {
-    base::PostTask(FROM_HERE, base::BindOnce(std::move(load_callback),
-                                             Error::NONE, base::FilePath()));
+    base::PostTask(FROM_HERE,
+                   base::BindOnce(std::move(load_callback),
+                                  ReportError(Error::NONE), base::FilePath()));
   }
 }
 
@@ -305,23 +317,31 @@
         ->LoadComponentAtPath(
             name, path,
             base::BindOnce(&CrOSComponentManager::FinishLoad,
-                           base::Unretained(this), std::move(load_callback)));
+                           base::Unretained(this), std::move(load_callback),
+                           base::TimeTicks::Now()));
   } else {
-    base::PostTask(FROM_HERE, base::BindOnce(std::move(load_callback),
-                                             Error::COMPATIBILITY_CHECK_FAILED,
-                                             base::FilePath()));
+    base::PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(load_callback),
+                       ReportError(Error::COMPATIBILITY_CHECK_FAILED),
+                       base::FilePath()));
   }
 }
 
 void CrOSComponentManager::FinishLoad(LoadCallback load_callback,
+                                      const base::TimeTicks start_time,
                                       base::Optional<base::FilePath> result) {
+  // Report component image mount time.
+  UMA_HISTOGRAM_LONG_TIMES("ComponentUpdater.ChromeOS.MountTime",
+                           base::TimeTicks::Now() - start_time);
   if (!result.has_value()) {
+    base::PostTask(FROM_HERE, base::BindOnce(std::move(load_callback),
+                                             ReportError(Error::MOUNT_FAILURE),
+                                             base::FilePath()));
+  } else {
     base::PostTask(FROM_HERE,
                    base::BindOnce(std::move(load_callback),
-                                  Error::MOUNT_FAILURE, base::FilePath()));
-  } else {
-    base::PostTask(FROM_HERE, base::BindOnce(std::move(load_callback),
-                                             Error::NONE, result.value()));
+                                  ReportError(Error::NONE), result.value()));
   }
 }
 
diff --git a/chrome/browser/component_updater/cros_component_installer.h b/chrome/browser/component_updater/cros_component_installer.h
index 4c85b9c..c1ae1e1 100644
--- a/chrome/browser/component_updater/cros_component_installer.h
+++ b/chrome/browser/component_updater/cros_component_installer.h
@@ -68,12 +68,15 @@
 // This class contains functions used to register and install a component.
 class CrOSComponentManager {
  public:
+  // Error needs to be consistent with CrosComponentManagerError in
+  // src/tools/metrics/histograms/enums.xml.
   enum class Error {
     NONE = 0,
     UNKNOWN_COMPONENT = 1,  // Component requested does not exist.
     INSTALL_FAILURE = 2,    // update_client fails to install component.
     MOUNT_FAILURE = 3,      // Component can not be mounted.
     COMPATIBILITY_CHECK_FAILED = 4,  // Compatibility check failed.
+    ERROR_MAX
   };
   using LoadCallback =
       base::OnceCallback<void(Error error, const base::FilePath&)>;
@@ -146,6 +149,7 @@
   // Calls load_callback and pass in the parameter |result| (component mount
   // point).
   void FinishLoad(LoadCallback load_callback,
+                  const base::TimeTicks start_time,
                   base::Optional<base::FilePath> result);
 
   // Registers component |configs| to be updated.
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win.cc b/chrome/browser/conflicts/problematic_programs_updater_win.cc
index 1d43888..81a76a24 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win.cc
+++ b/chrome/browser/conflicts/problematic_programs_updater_win.cc
@@ -251,10 +251,14 @@
     const ModuleInfoData& module_data) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Only consider loaded modules.
-  // TODO(pmonette): Also consider blocked modules when that becomes possible.
-  if ((module_data.module_types & ModuleInfoData::kTypeLoadedModule) == 0)
+  // Only consider loaded modules that are not shell extensions or IMEs.
+  static constexpr uint32_t kModuleTypesBitmask =
+      ModuleInfoData::kTypeLoadedModule | ModuleInfoData::kTypeShellExtension |
+      ModuleInfoData::kTypeIme;
+  if ((module_data.module_types & kModuleTypesBitmask) !=
+      ModuleInfoData::kTypeLoadedModule) {
     return;
+  }
 
   // Explicitly whitelist modules whose signing cert's Subject field matches the
   // one in the current executable. No attempt is made to check the validity of
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc b/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
index afdb372..9025068 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
+++ b/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
@@ -306,3 +306,31 @@
   auto program_names = ProblematicProgramsUpdater::GetCachedPrograms();
   ASSERT_EQ(0u, program_names.size());
 }
+
+// Registered modules are defined as either a shell extension or an IME.
+TEST_F(ProblematicProgramsUpdaterTest, IgnoreRegisteredModules) {
+  AddProblematicProgram(dll1_, L"Shell Extension", Option::ADD_REGISTRY_ENTRY);
+  AddProblematicProgram(dll2_, L"Input Method Editor",
+                        Option::ADD_REGISTRY_ENTRY);
+
+  auto problematic_programs_updater =
+      std::make_unique<ProblematicProgramsUpdater>(
+          exe_certificate_info(), module_list_filter(), installed_programs());
+
+  // Set the respective bit for registered modules.
+  auto module_data1 = CreateLoadedModuleInfoData();
+  module_data1.module_types |= ModuleInfoData::kTypeShellExtension;
+  auto module_data2 = CreateLoadedModuleInfoData();
+  module_data2.module_types |= ModuleInfoData::kTypeIme;
+
+  // Simulate the modules loading into the process.
+  problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
+                                                 module_data1);
+  problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll2_, 0, 0, 0),
+                                                 module_data2);
+  problematic_programs_updater->OnModuleDatabaseIdle();
+
+  EXPECT_FALSE(ProblematicProgramsUpdater::HasCachedPrograms());
+  auto program_names = ProblematicProgramsUpdater::GetCachedPrograms();
+  ASSERT_EQ(0u, program_names.size());
+}
diff --git a/chrome/browser/data_usage/tab_id_annotator.cc b/chrome/browser/data_usage/tab_id_annotator.cc
index 30c534d..a30ff66 100644
--- a/chrome/browser/data_usage/tab_id_annotator.cc
+++ b/chrome/browser/data_usage/tab_id_annotator.cc
@@ -32,9 +32,9 @@
 // Attempts to get the associated tab info for render frame identified by
 // |render_process_id| and |render_frame_id|. |global_request_id| is also
 // populated in the tab info.
-int32_t GetTabInfoForRequest(int render_process_id,
-                             int render_frame_id,
-                             content::GlobalRequestID global_request_id) {
+SessionID GetTabInfoForRequest(int render_process_id,
+                               int render_frame_id,
+                               content::GlobalRequestID global_request_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // TODO(sclittle): For prerendering tabs, investigate if it's possible to find
   // the original tab that initiated the prerender.
@@ -51,7 +51,7 @@
 void AnnotateDataUse(
     std::unique_ptr<DataUse> data_use,
     const data_usage::DataUseAnnotator::DataUseConsumerCallback& callback,
-    int32_t tab_info) {
+    SessionID tab_info) {
   DCHECK(data_use);
 
   data_use->tab_id = tab_info;
@@ -86,7 +86,8 @@
           request, &render_process_id, &render_frame_id)) {
     // Run the callback immediately with a tab ID of -1 if the request has no
     // render frame.
-    AnnotateDataUse(std::move(data_use), callback, -1 /* tab_id */);
+    AnnotateDataUse(std::move(data_use), callback,
+                    /*tab_id=*/SessionID::InvalidValue());
     return;
   }
 
diff --git a/chrome/browser/data_usage/tab_id_annotator_unittest.cc b/chrome/browser/data_usage/tab_id_annotator_unittest.cc
index 5ce30cde..ba51436 100644
--- a/chrome/browser/data_usage/tab_id_annotator_unittest.cc
+++ b/chrome/browser/data_usage/tab_id_annotator_unittest.cc
@@ -53,7 +53,8 @@
 };
 
 // Synthesizes a DataUse object with the given |tab_id|.
-std::unique_ptr<DataUse> CreateDataUse(int32_t tab_id, int render_process_id) {
+std::unique_ptr<DataUse> CreateDataUse(SessionID tab_id,
+                                       int render_process_id) {
   auto data_use = std::unique_ptr<DataUse>(new DataUse(
       GURL("http://foo.com"), base::TimeTicks(), GURL(), tab_id,
       net::NetworkChangeNotifier::CONNECTION_UNKNOWN, std::string(), 100, 100));
@@ -92,7 +93,7 @@
 void TestAnnotateOnIOThread(base::RunLoop* ui_run_loop,
                             int render_process_id,
                             int render_frame_id,
-                            int32_t expected_tab_id) {
+                            SessionID expected_tab_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(ui_run_loop);
 
@@ -113,9 +114,10 @@
         content::PREVIEWS_OFF, nullptr);
   }
 
-  // An invalid tab ID to check that the annotator always sets the tab ID. -2 is
-  // used because a tab ID of -1 is a valid value that means "no tab was found".
-  const int32_t kInvalidTabId = -2;
+  // An invalid tab ID to check that the annotator always sets the tab ID. An
+  // arbitrary number is used because SessionID::InvalidValue() means "no tab
+  // was found".
+  const SessionID kInvalidTabId = SessionID::FromSerializedValue(123456);
 
   // Annotate two separate DataUse objects to ensure that repeated annotations
   // for the same URLRequest work properly.
@@ -140,21 +142,21 @@
       BrowserThread::IO, FROM_HERE,
       base::BindOnce(&TestAnnotateOnIOThread, &ui_run_loop,
                      -1 /* render_process_id */, -1 /* render_frame_id */,
-                     -1 /* expected_tab_id */));
+                     SessionID::InvalidValue() /* expected_tab_id */));
   ui_run_loop.Run();
 }
 
 TEST_F(TabIdAnnotatorTest, AnnotateWithRenderFrameAndNoTab) {
   base::RunLoop ui_run_loop;
   // |web_contents()| isn't a tab, so it shouldn't have a tab ID.
-  EXPECT_EQ(-1, SessionTabHelper::IdForTab(web_contents()));
+  EXPECT_FALSE(SessionTabHelper::IdForTab(web_contents()).is_valid());
 
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::BindOnce(&TestAnnotateOnIOThread, &ui_run_loop,
                      web_contents()->GetMainFrame()->GetProcess()->GetID(),
                      web_contents()->GetMainFrame()->GetRoutingID(),
-                     -1 /* expected_tab_id */));
+                     SessionID::InvalidValue() /* expected_tab_id */));
   ui_run_loop.Run();
 }
 
@@ -162,9 +164,9 @@
   base::RunLoop ui_run_loop;
   // Make |web_contents()| into a tab.
   SessionTabHelper::CreateForWebContents(web_contents());
-  int32_t expected_tab_id = SessionTabHelper::IdForTab(web_contents());
+  SessionID expected_tab_id = SessionTabHelper::IdForTab(web_contents());
   // |web_contents()| is a tab, so it should have a tab ID.
-  EXPECT_NE(-1, expected_tab_id);
+  EXPECT_TRUE(expected_tab_id.is_valid());
 
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
diff --git a/chrome/browser/data_usage/tab_id_provider.cc b/chrome/browser/data_usage/tab_id_provider.cc
index d190720..ccd7eb9 100644
--- a/chrome/browser/data_usage/tab_id_provider.cc
+++ b/chrome/browser/data_usage/tab_id_provider.cc
@@ -21,8 +21,8 @@
 namespace {
 
 // Convenience typedefs for clarity.
-typedef base::OnceCallback<int32_t(void)> TabIdGetter;
-typedef base::OnceCallback<void(int32_t)> TabIdCallback;
+typedef base::OnceCallback<SessionID(void)> TabIdGetter;
+typedef base::OnceCallback<void(SessionID)> TabIdCallback;
 
 }  // namespace
 
@@ -44,7 +44,7 @@
 
   // Runs all the callbacks in the order that they were added. This method must
   // not be called more than once.
-  void RunAll(int32_t tab_info) {
+  void RunAll(SessionID tab_info) {
     DCHECK(thread_checker_.CalledOnValidThread());
     DCHECK(!is_done_);
     is_done_ = true;
@@ -73,7 +73,7 @@
 TabIdProvider::TabIdProvider(base::TaskRunner* task_runner,
                              const base::Location& from_here,
                              TabIdGetter tab_id_getter)
-    : is_tab_info_ready_(false), tab_info_(-1), weak_ptr_factory_(this) {
+    : weak_ptr_factory_(this) {
   std::unique_ptr<CallbackRunner> callback_runner(new CallbackRunner());
   weak_callback_runner_ = callback_runner->GetWeakPtr();
   callback_runner->AddCallback(
@@ -93,8 +93,8 @@
 
 void TabIdProvider::ProvideTabId(TabIdCallback callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (is_tab_info_ready_) {
-    std::move(callback).Run(tab_info_);
+  if (tab_info_.has_value()) {
+    std::move(callback).Run(*tab_info_);
     return;
   }
   if (weak_callback_runner_) {
@@ -102,9 +102,9 @@
     return;
   }
   // If no cached tab ID is available and |weak_callback_runner_| has been
-  // destroyed, pass a tab ID of -1 to the callback indicating that no tab was
-  // found.
-  std::move(callback).Run(-1);
+  // destroyed, pass an invalid tab ID to the callback indicating that no tab
+  // was found.
+  std::move(callback).Run(SessionID::InvalidValue());
 }
 
 base::WeakPtr<TabIdProvider> TabIdProvider::GetWeakPtr() {
@@ -116,12 +116,11 @@
 const void* const TabIdProvider::kTabIdProviderUserDataKey =
     "TabIdProviderUserDataKey";
 
-void TabIdProvider::OnTabIdReady(int32_t tab_info) {
+void TabIdProvider::OnTabIdReady(SessionID tab_info) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!is_tab_info_ready_);
+  DCHECK(!tab_info_.has_value());
 
   tab_info_ = tab_info;
-  is_tab_info_ready_ = true;
 }
 
 }  // namespace chrome_browser_data_usage
diff --git a/chrome/browser/data_usage/tab_id_provider.h b/chrome/browser/data_usage/tab_id_provider.h
index eca8987..3c17f839 100644
--- a/chrome/browser/data_usage/tab_id_provider.h
+++ b/chrome/browser/data_usage/tab_id_provider.h
@@ -10,8 +10,10 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/supports_user_data.h"
 #include "base/threading/thread_checker.h"
+#include "components/sessions/core/session_id.h"
 #include "content/public/browser/global_request_id.h"
 
 namespace base {
@@ -29,18 +31,18 @@
  public:
   struct URLRequestTabInfo {
     URLRequestTabInfo()
-        : tab_id(-1),
+        : tab_id(SessionID::InvalidValue()),
           main_frame_global_request_id(content::GlobalRequestID()) {}
 
     URLRequestTabInfo(
-        int32_t tab_id,
+        SessionID tab_id,
         const content::GlobalRequestID& main_frame_global_request_id)
         : tab_id(tab_id),
           main_frame_global_request_id(main_frame_global_request_id) {}
 
     // ID of the Tab for which this request happens. See comments of the same
     // field in data_usage::DataUse for when this is field is populated.
-    int32_t tab_id;
+    SessionID tab_id;
 
     // GlobalRequestID of the mainframe request. See comments of the same field
     // in data_usage::DataUse for when this is field is populated.
@@ -51,13 +53,13 @@
   // |task_runner|.
   TabIdProvider(base::TaskRunner* task_runner,
                 const base::Location& from_here,
-                base::OnceCallback<int32_t(void)> tab_id_getter);
+                base::OnceCallback<SessionID(void)> tab_id_getter);
 
   ~TabIdProvider() override;
 
   // Calls |callback| with the tab ID, either immediately if it's already
   // available, or later once it becomes available.
-  void ProvideTabId(base::OnceCallback<void(int32_t)> callback);
+  void ProvideTabId(base::OnceCallback<void(SessionID)> callback);
 
   base::WeakPtr<TabIdProvider> GetWeakPtr();
 
@@ -67,11 +69,10 @@
   class CallbackRunner;
 
   // Called when the |tab_info| is ready.
-  void OnTabIdReady(int32_t tab_info);
+  void OnTabIdReady(SessionID tab_info);
 
   base::ThreadChecker thread_checker_;
-  bool is_tab_info_ready_;
-  int32_t tab_info_;
+  base::Optional<SessionID> tab_info_;
   base::WeakPtr<CallbackRunner> weak_callback_runner_;
   base::WeakPtrFactory<TabIdProvider> weak_ptr_factory_;
 
diff --git a/chrome/browser/data_usage/tab_id_provider_unittest.cc b/chrome/browser/data_usage/tab_id_provider_unittest.cc
index b915559..8c64885 100644
--- a/chrome/browser/data_usage/tab_id_provider_unittest.cc
+++ b/chrome/browser/data_usage/tab_id_provider_unittest.cc
@@ -24,7 +24,7 @@
 
 namespace {
 
-const int32_t kTabId = 10;
+const SessionID kTabId = SessionID::FromSerializedValue(10);
 
 class TabIdProviderTest : public testing::Test {
  public:
@@ -34,7 +34,7 @@
 
   ~TabIdProviderTest() override {}
 
-  base::OnceCallback<int32_t(void)> TabIdGetterCallback() {
+  base::OnceCallback<SessionID(void)> TabIdGetterCallback() {
     return base::BindOnce(&TabIdProviderTest::GetTabInfo,
                           base::Unretained(this));
   }
@@ -44,7 +44,7 @@
   int tab_id_getter_call_count() const { return tab_id_getter_call_count_; }
 
  private:
-  int32_t GetTabInfo() {
+  SessionID GetTabInfo() {
     ++tab_id_getter_call_count_;
     return kTabId;
   }
@@ -58,14 +58,14 @@
 };
 
 // Copies |tab_id| into |capture|.
-void CaptureTabId(int32_t* capture, int32_t tab_info) {
+void CaptureTabId(SessionID* capture, SessionID tab_info) {
   *capture = tab_info;
 }
 
 TEST_F(TabIdProviderTest, ProvideTabId) {
   TabIdProvider provider(task_runner(), FROM_HERE, TabIdGetterCallback());
 
-  int32_t tab_id = -1;
+  SessionID tab_id = SessionID::InvalidValue();
   provider.ProvideTabId(base::BindOnce(&CaptureTabId, &tab_id));
   run_loop()->RunUntilIdle();
 
@@ -77,13 +77,13 @@
   TabIdProvider provider(task_runner(), FROM_HERE, TabIdGetterCallback());
 
   // First, ask for the first tab ID to kick things off.
-  int32_t first_tab_id = -1;
+  SessionID first_tab_id = SessionID::InvalidValue();
   provider.ProvideTabId(base::BindOnce(&CaptureTabId, &first_tab_id));
 
   // The first tab ID callback should still be pending, with the tab ID not
   // available yet, so this second callback should piggyback off of the first
   // callback.
-  int32_t piggyback_tab_id = -1;
+  SessionID piggyback_tab_id = SessionID::InvalidValue();
   provider.ProvideTabId(base::BindOnce(&CaptureTabId, &piggyback_tab_id));
 
   run_loop()->RunUntilIdle();
@@ -98,7 +98,7 @@
   TabIdProvider provider(task_runner(), FROM_HERE, TabIdGetterCallback());
 
   // First, ask for the first tab ID to kick things off.
-  int32_t first_tab_id = -1;
+  SessionID first_tab_id = SessionID::InvalidValue();
   provider.ProvideTabId(base::BindOnce(&CaptureTabId, &first_tab_id));
 
   // Wait for the first tab ID callback to finish.
@@ -109,7 +109,7 @@
 
   // Ask for another tab ID, which should be satisfied by the cached tab ID from
   // the first callback.
-  int32_t cache_hit_tab_id = -1;
+  SessionID cache_hit_tab_id = SessionID::InvalidValue();
   provider.ProvideTabId(base::BindOnce(&CaptureTabId, &cache_hit_tab_id));
 
   // This cache hit callback should run synchronously, without causing the tab
@@ -123,7 +123,8 @@
       new TabIdProvider(task_runner(), FROM_HERE, TabIdGetterCallback()));
 
   // Ask for two tab IDs.
-  int32_t first_tab_id = -1, second_tab_id = -1;
+  SessionID first_tab_id = SessionID::InvalidValue(),
+            second_tab_id = SessionID::InvalidValue();
   provider->ProvideTabId(base::BindOnce(&CaptureTabId, &first_tab_id));
   provider->ProvideTabId(base::BindOnce(&CaptureTabId, &second_tab_id));
 
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc
index aef2bea..2683893a 100644
--- a/chrome/browser/download/download_browsertest.cc
+++ b/chrome/browser/download/download_browsertest.cc
@@ -2600,12 +2600,12 @@
        // the headers are successfully received.
        "download-anchor-attrib-404.html", "there_IS_no_spoon.zip",
        DOWNLOAD_NAVIGATE,
-       download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, false, false},
+       download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, true, false},
       {// Similar to the above, but the resulting response contains a status
        // code of 400.
        "download-anchor-attrib-400.html", "zip_file_not_found.zip",
        DOWNLOAD_NAVIGATE, download::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED,
-       false, false},
+       true, false},
       {// Direct download of a URL where the hostname doesn't resolve.
        "http://doesnotexist/shouldnotdownloadsuccessfully",
        "http://doesnotexist/shouldnotdownloadsuccessfully", DOWNLOAD_DIRECT,
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
index 3970ab9f..65273566 100644
--- a/chrome/browser/extensions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -125,9 +125,7 @@
     TabHelper::CreateForWebContents(web_contents());
   }
 
-  int tab_id() {
-    return SessionTabHelper::IdForTab(web_contents());
-  }
+  int tab_id() { return SessionTabHelper::IdForTab(web_contents()).id(); }
 
   ActiveTabPermissionGranter* active_tab_permission_granter() {
     return extensions::TabHelper::FromWebContents(web_contents())->
@@ -194,7 +192,7 @@
   bool IsGrantedForTab(const Extension* extension,
                        const content::WebContents* web_contents) {
     return extension->permissions_data()->HasAPIPermissionForTab(
-        SessionTabHelper::IdForTab(web_contents), APIPermission::kTab);
+        SessionTabHelper::IdForTab(web_contents).id(), APIPermission::kTab);
   }
 
   // TODO(justinlin): Remove when tabCapture is moved to stable.
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
index 3c912638..519623a 100644
--- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api_unittest.cc
@@ -175,7 +175,7 @@
 
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetWebContentsAt(0);
-    tab_id_ = SessionTabHelper::IdForTab(web_contents);
+    tab_id_ = SessionTabHelper::IdForTab(web_contents).id();
     PermissionRequestManager::CreateForWebContents(web_contents);
     prompt_factory_ = std::make_unique<MockPermissionPromptFactory>(
         PermissionRequestManager::FromWebContents(web_contents));
diff --git a/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
index 92fae33..28f88e20 100644
--- a/chrome/browser/extensions/api/debugger/debugger_apitest.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
@@ -86,7 +86,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
 
   // Attach by tabId.
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   std::string debugee_by_tab = base::StringPrintf("{\"tabId\": %d}", tab_id);
   testing::AssertionResult result =
       RunAttachFunctionOnTarget(debugee_by_tab, expected_error);
@@ -191,7 +191,8 @@
 
 IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) {
   int tab_id = SessionTabHelper::IdForTab(
-      browser()->tab_strip_model()->GetActiveWebContents());
+                   browser()->tab_strip_model()->GetActiveWebContents())
+                   .id();
   scoped_refptr<DebuggerAttachFunction> attach_function;
   scoped_refptr<DebuggerDetachFunction> detach_function;
 
@@ -200,7 +201,8 @@
   AddBlankTabAndShow(another_browser);
   AddBlankTabAndShow(another_browser);
   int tab_id2 = SessionTabHelper::IdForTab(
-      another_browser->tab_strip_model()->GetActiveWebContents());
+                    another_browser->tab_strip_model()->GetActiveWebContents())
+                    .id();
 
   InfoBarService* service1 = InfoBarService::FromWebContents(
       browser()->tab_strip_model()->GetActiveWebContents());
diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc
index fa344ec..cbda955 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action.cc
@@ -381,9 +381,7 @@
   content::RenderFrameHost* render_frame_host = contents->GetMainFrame();
   render_frame_host->Send(new ExtensionMsg_ExecuteDeclarativeScript(
       render_frame_host->GetRoutingID(),
-      SessionTabHelper::IdForTab(contents),
-      extension->id(),
-      script_.id(),
+      SessionTabHelper::IdForTab(contents).id(), extension->id(), script_.id(),
       contents->GetLastCommittedURL()));
 }
 
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
index 51be4bb..37063408 100644
--- a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
+++ b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
@@ -345,13 +345,13 @@
   OpenPopupViaAPI(false);
   ExtensionService* service = extensions::ExtensionSystem::Get(
       browser()->profile())->extension_service();
-  ASSERT_FALSE(
-      service->GetExtensionById(last_loaded_extension_id(), false)
-          ->permissions_data()
-          ->HasAPIPermissionForTab(
-              SessionTabHelper::IdForTab(
-                  browser()->tab_strip_model()->GetActiveWebContents()),
-              APIPermission::kTab));
+  ASSERT_FALSE(service->GetExtensionById(last_loaded_extension_id(), false)
+                   ->permissions_data()
+                   ->HasAPIPermissionForTab(
+                       SessionTabHelper::IdForTab(
+                           browser()->tab_strip_model()->GetActiveWebContents())
+                           .id(),
+                       APIPermission::kTab));
   ClosePopup();
 }
 
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index 6466d63..3d48f3a 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -239,7 +239,7 @@
 void ExtensionActionAPI::ClearAllValuesForTab(
     content::WebContents* web_contents) {
   DCHECK(web_contents);
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  const SessionID tab_id = SessionTabHelper::IdForTab(web_contents);
   content::BrowserContext* browser_context = web_contents->GetBrowserContext();
   const ExtensionSet& enabled_extensions =
       ExtensionRegistry::Get(browser_context_)->enabled_extensions();
@@ -251,7 +251,7 @@
     ExtensionAction* extension_action =
         action_manager->GetExtensionAction(**iter);
     if (extension_action) {
-      extension_action->ClearAllValuesForTab(tab_id);
+      extension_action->ClearAllValuesForTab(tab_id.id());
       NotifyChange(extension_action, web_contents, browser_context);
     }
   }
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
index 1ad1768..fb57664c 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc
@@ -119,7 +119,7 @@
          domAutomationController.send('pass');)";
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents);
   constexpr char kBrowserActionKey[] = "browser_action";
   TestStateStoreObserver test_state_store_observer(profile(), extension->id());
 
@@ -128,7 +128,7 @@
                                                      extension->id());
     // First, update a specific tab.
     std::string update_options =
-        base::StringPrintf("{text: 'New Text', tabId: %d}", tab_id);
+        base::StringPrintf("{text: 'New Text', tabId: %d}", tab_id.id());
     EXPECT_EQ("pass", browsertest_util::ExecuteScriptInBackgroundPage(
                           profile(), extension->id(),
                           base::StringPrintf(kUpdate, update_options.c_str())));
diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc
index 276159c9..9f8179e 100644
--- a/chrome/browser/extensions/api/processes/processes_api.cc
+++ b/chrome/browser/extensions/api/processes/processes_api.cc
@@ -131,9 +131,9 @@
   for (const auto& task_id : tasks_on_process) {
     api::processes::TaskInfo task_info;
     task_info.title = base::UTF16ToUTF8(task_manager->GetTitle(task_id));
-    const int tab_id = task_manager->GetTabId(task_id);
-    if (tab_id != -1)
-      task_info.tab_id.reset(new int(tab_id));
+    const SessionID tab_id = task_manager->GetTabId(task_id);
+    if (tab_id.is_valid())
+      task_info.tab_id.reset(new int(tab_id.id()));
 
     out_process->tasks.push_back(std::move(task_info));
   }
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc
index 09a7f1b2..2978c2b 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc
@@ -204,7 +204,7 @@
   // Make sure either we have been granted permission to capture through an
   // extension icon click or our extension is whitelisted.
   if (!extension()->permissions_data()->HasAPIPermissionForTab(
-          SessionTabHelper::IdForTab(target_contents),
+          SessionTabHelper::IdForTab(target_contents).id(),
           APIPermission::kTabCaptureForTab) &&
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kWhitelistedExtensionID) != extension_id &&
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
index 6cd5062..c8a4493 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc
@@ -192,7 +192,7 @@
   }
 
   void GetCaptureInfo(tab_capture::CaptureInfo* info) const {
-    info->tab_id = SessionTabHelper::IdForTab(web_contents());
+    info->tab_id = SessionTabHelper::IdForTab(web_contents()).id();
     info->status = capture_state_;
     info->fullscreen = is_fullscreened_;
   }
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index b8604ff..b83c1da 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/extensions/window_controller_list.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
@@ -986,8 +987,6 @@
     TabStripModel* tab_strip = browser->tab_strip_model();
     for (int i = 0; i < tab_strip->count(); ++i) {
       WebContents* web_contents = tab_strip->GetWebContentsAt(i);
-      resource_coordinator::TabManager* tab_manager =
-          g_browser_process->GetTabManager();
 
       if (index > -1 && i != index)
         continue;
@@ -1016,13 +1015,17 @@
         continue;
       }
 
+      auto* tab_lifecycle_unit_external =
+          resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
+              web_contents);
+
       if (!MatchesBool(params->query_info.discarded.get(),
-                       tab_manager->IsTabDiscarded(web_contents))) {
+                       tab_lifecycle_unit_external->IsDiscarded())) {
         continue;
       }
 
       if (!MatchesBool(params->query_info.auto_discardable.get(),
-                       tab_manager->IsTabAutoDiscardable(web_contents))) {
+                       tab_lifecycle_unit_external->IsAutoDiscardable())) {
         continue;
       }
 
@@ -1252,7 +1255,7 @@
       error_ = keys::kNoSelectedTabError;
       return false;
     }
-    tab_id = SessionTabHelper::IdForTab(contents);
+    tab_id = SessionTabHelper::IdForTab(contents).id();
   } else {
     tab_id = *params->tab_id;
   }
@@ -1734,7 +1737,7 @@
   }
 
   if (!extension()->permissions_data()->CanCaptureVisiblePage(
-          SessionTabHelper::IdForTab(contents), &error_)) {
+          SessionTabHelper::IdForTab(contents).id(), &error_)) {
     return NULL;
   }
   return contents;
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index bd67c2e..89c2d712 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -20,27 +20,15 @@
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_builder.h"
+#include "ui/display/test/scoped_screen_override.h"
 #include "ui/display/test/test_screen.h"
 
 namespace extensions {
 
+using display::test::ScopedScreenOverride;
+
 namespace {
 
-class ScopedScreenOverride {
- public:
-  ~ScopedScreenOverride() {
-    display::Screen::SetScreenInstance(original_screen_);
-  }
-
-  void SetScreenInstance(display::Screen* instance) {
-    original_screen_ = display::Screen::GetScreen();
-    display::Screen::SetScreenInstance(instance);
-  }
-
- private:
-  display::Screen* original_screen_ = nullptr;
-};
-
 std::unique_ptr<base::ListValue> RunTabsQueryFunction(
     Browser* browser,
     const Extension* extension,
@@ -88,14 +76,13 @@
   params.type = Browser::TYPE_TABBED;
   params.window = browser_window_.get();
   browser_.reset(new Browser(params));
-  scoped_screen_override_.reset(new ScopedScreenOverride);
-  scoped_screen_override_->SetScreenInstance(&test_screen_);
+  scoped_screen_override_ =
+      std::make_unique<ScopedScreenOverride>(&test_screen_);
 }
 
 void TabsApiUnitTest::TearDown() {
   browser_.reset();
   browser_window_.reset();
-  scoped_screen_override_.reset();
   content::BrowserSideNavigationTearDown();
   ExtensionServiceTestBase::TearDown();
 }
@@ -266,7 +253,7 @@
   EXPECT_EQ(kGoogle, web_contents->GetVisibleURL());
 
   SessionTabHelper::CreateForWebContents(web_contents);
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   browser()->tab_strip_model()->AppendWebContents(web_contents, true);
 
   scoped_refptr<TabsUpdateFunction> function = new TabsUpdateFunction();
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index abb1c3a..aa7aea4 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #include "chrome/browser/resource_coordinator/time.h"
 #include "chrome/browser/ui/browser.h"
@@ -1460,10 +1461,10 @@
           discard.get(), base::StringPrintf("[%u]", tab_id), browser())));
 
   // Confirms that TabManager sees the tab as discarded.
-  resource_coordinator::TabManager* tab_manager =
-      g_browser_process->GetTabManager();
   web_contents = browser()->tab_strip_model()->GetWebContentsAt(1);
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(web_contents));
+  EXPECT_TRUE(resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
+                  web_contents)
+                  ->IsDiscarded());
 
   // Make sure the returned tab is the one discarded and its discarded state is
   // correct.
@@ -1507,8 +1508,9 @@
       discard.get(), base::StringPrintf("[%u]", tab_invalid_id), browser());
 
   // Discarded state should still be false as no tab was discarded.
-  EXPECT_FALSE(g_browser_process->GetTabManager()->IsTabDiscarded(
-      browser()->tab_strip_model()->GetWebContentsAt(1)));
+  EXPECT_FALSE(resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
+                   browser()->tab_strip_model()->GetWebContentsAt(1))
+                   ->IsDiscarded());
 
   // Check error message.
   EXPECT_TRUE(base::MatchPattern(error, keys::kTabNotFoundError));
@@ -1537,7 +1539,9 @@
 
   // Confirms that TabManager sees the tab as discarded.
   web_contents = browser()->tab_strip_model()->GetWebContentsAt(1);
-  EXPECT_TRUE(g_browser_process->GetTabManager()->IsTabDiscarded(web_contents));
+  EXPECT_TRUE(resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
+                  web_contents)
+                  ->IsDiscarded());
 
   // Make sure the returned tab is the one discarded and its discarded state is
   // correct.
@@ -1567,10 +1571,12 @@
       utils::RunFunctionAndReturnError(discard.get(), "[]", browser());
 
   // Discarded state should be false for both tabs as no tab was discarded.
-  EXPECT_FALSE(g_browser_process->GetTabManager()->IsTabDiscarded(
-      browser()->tab_strip_model()->GetWebContentsAt(1)));
-  EXPECT_FALSE(g_browser_process->GetTabManager()->IsTabDiscarded(
-      browser()->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_FALSE(resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
+                   browser()->tab_strip_model()->GetWebContentsAt(1))
+                   ->IsDiscarded());
+  EXPECT_FALSE(resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
+                   browser()->tab_strip_model()->GetWebContentsAt(0))
+                   ->IsDiscarded());
 
   // Check error message.
   EXPECT_TRUE(base::MatchPattern(error, keys::kCannotFindTabToDiscard));
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 1446ca5..8f3d556 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -45,11 +45,9 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
-#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/page_type.h"
 #include "content/public/test/browser_test_utils.h"
-#include "content/public/test/simple_url_loader_test_helper.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "extensions/browser/api/web_request/web_request_api.h"
 #include "extensions/browser/blocked_action_type.h"
@@ -74,8 +72,6 @@
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_interceptor.h"
 #include "services/network/public/cpp/features.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/simple_url_loader.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 
 #if defined(OS_CHROMEOS)
@@ -912,11 +908,12 @@
 }
 
 // Verify that requests to clientsX.google.com are protected properly.
-// First test requests from a standard renderer and then a request from the
-// browser process.
+// First test requests from a standard renderer and a webui renderer.
+// Then test a request from the browser process.
 IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest,
                        WebRequestClientsGoogleComProtection) {
   ASSERT_TRUE(embedded_test_server()->Start());
+  int port = embedded_test_server()->port();
 
   // Load an extension that registers a listener for webRequest events, and
   // wait until it's initialized.
@@ -926,60 +923,96 @@
   ASSERT_TRUE(extension) << message_;
   EXPECT_TRUE(listener.WaitUntilSatisfied());
 
-  EXPECT_EQ(0, GetWebRequestCountFromBackgroundPage(extension, profile()));
+  // Perform requests to https://client1.google.com from renderer processes.
 
-  GURL main_frame_url =
-      embedded_test_server()->GetURL("www.example.com", "/simple.html");
-  NavigateParams params(browser(), main_frame_url, ui::PAGE_TRANSITION_TYPED);
-  ui_test_utils::NavigateToURL(&params);
+  struct TestCase {
+    const char* main_frame_url;
+    bool request_to_clients1_google_com_visible;
+  } testcases[] = {
+      {"http://www.example.com", true}, {"chrome://settings", false},
+  };
 
-  EXPECT_EQ(0, GetWebRequestCountFromBackgroundPage(extension, profile()));
+  // Expected number of requests to clients1.google.com observed so far.
+  int expected_requests_observed = 0;
+  EXPECT_EQ(expected_requests_observed,
+            GetWebRequestCountFromBackgroundPage(extension, profile()));
 
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(web_contents);
+  for (const auto& testcase : testcases) {
+    SCOPED_TRACE(testcase.main_frame_url);
 
-  // Attempt to issue a request to clients1.google.com from the renderer. This
-  // will fail, but should still be visible to the WebRequest API.
-  const char kRequest[] = R"(
-      var xhr = new XMLHttpRequest();
-      xhr.open('GET', 'http://clients1.google.com');
-      xhr.onload = () => {window.domAutomationController.send(true);};
-      xhr.onerror = () => {window.domAutomationController.send(false);};
-      xhr.send();)";
-  bool success = false;
-  EXPECT_TRUE(ExecuteScriptAndExtractBool(web_contents->GetMainFrame(),
-                                          kRequest, &success));
-  // Requests always fail due to cross origin nature.
-  EXPECT_FALSE(success);
+    GURL url;
+    if (base::StartsWith(testcase.main_frame_url, "chrome://",
+                         base::CompareCase::INSENSITIVE_ASCII)) {
+      url = GURL(testcase.main_frame_url);
+    } else {
+      url = GURL(base::StringPrintf("%s:%d/simple.html",
+                                    testcase.main_frame_url, port));
+    }
 
-  EXPECT_EQ(1, GetWebRequestCountFromBackgroundPage(extension, profile()));
+    NavigateParams params(browser(), url, ui::PAGE_TRANSITION_TYPED);
+    ui_test_utils::NavigateToURL(&params);
 
-  // Now perform a request to client1.google.com from the browser process. This
-  // should *not* be visible to the WebRequest API.
+    EXPECT_EQ(expected_requests_observed,
+              GetWebRequestCountFromBackgroundPage(extension, profile()));
 
-  auto request = std::make_unique<network::ResourceRequest>();
-  request->url =
-      embedded_test_server()->GetURL("clients1.google.com", "/simple.html");
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    ASSERT_TRUE(web_contents);
 
-  auto* url_loader_factory =
-      content::BrowserContext::GetDefaultStoragePartition(profile())
-          ->GetURLLoaderFactoryForBrowserProcess()
-          .get();
-  content::SimpleURLLoaderTestHelper loader_helper;
-  auto loader = network::SimpleURLLoader::Create(std::move(request),
-                                                 TRAFFIC_ANNOTATION_FOR_TESTS);
-  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory, loader_helper.GetCallback());
+    const char kRequest[] =
+        "var xhr = new XMLHttpRequest();\n"
+        "xhr.open('GET', 'https://clients1.google.com');\n"
+        "xhr.onload = () => {window.domAutomationController.send(true);};\n"
+        "xhr.onerror = () => {window.domAutomationController.send(false);};\n"
+        "xhr.send();\n";
 
-  // Wait for the response to complete.
-  loader_helper.WaitForCallback();
-  EXPECT_TRUE(loader_helper.response_body());
-  EXPECT_EQ(200, loader->ResponseInfo()->headers->response_code());
+    bool success = false;
+    EXPECT_TRUE(ExecuteScriptAndExtractBool(web_contents->GetMainFrame(),
+                                            kRequest, &success));
+    // Requests always fail due to cross origin nature.
+    EXPECT_FALSE(success);
 
-  // We should still have only seen the single render-initiated request from the
-  // first half of the test.
-  EXPECT_EQ(1, GetWebRequestCountFromBackgroundPage(extension, profile()));
+    if (testcase.request_to_clients1_google_com_visible)
+      ++expected_requests_observed;
+
+    EXPECT_EQ(expected_requests_observed,
+              GetWebRequestCountFromBackgroundPage(extension, profile()));
+  }
+
+  // Perform request to https://client1.google.com from browser process.
+
+  class TestURLFetcherDelegate : public net::URLFetcherDelegate {
+   public:
+    explicit TestURLFetcherDelegate(const base::Closure& quit_loop_func)
+        : quit_loop_func_(quit_loop_func) {}
+    ~TestURLFetcherDelegate() override {}
+
+    void OnURLFetchComplete(const net::URLFetcher* source) override {
+      EXPECT_EQ(net::HTTP_OK, source->GetResponseCode());
+      quit_loop_func_.Run();
+    }
+
+   private:
+    base::Closure quit_loop_func_;
+  };
+  base::RunLoop run_loop;
+  TestURLFetcherDelegate delegate(run_loop.QuitClosure());
+
+  net::URLFetcherImplFactory url_fetcher_impl_factory;
+  net::FakeURLFetcherFactory url_fetcher_factory(&url_fetcher_impl_factory);
+  url_fetcher_factory.SetFakeResponse(GURL("https://client1.google.com"),
+                                      "hello my friend", net::HTTP_OK,
+                                      net::URLRequestStatus::SUCCESS);
+  std::unique_ptr<net::URLFetcher> fetcher =
+      url_fetcher_factory.CreateURLFetcher(
+          1, GURL("https://client1.google.com"), net::URLFetcher::GET,
+          &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+  fetcher->Start();
+  run_loop.Run();
+
+  // This request should not be observed by the extension.
+  EXPECT_EQ(expected_requests_observed,
+            GetWebRequestCountFromBackgroundPage(extension, profile()));
 }
 
 // Verify that requests for PAC scripts are protected properly.
diff --git a/chrome/browser/extensions/extension_action_runner.cc b/chrome/browser/extensions/extension_action_runner.cc
index c97dac8..dbf216e 100644
--- a/chrome/browser/extensions/extension_action_runner.cc
+++ b/chrome/browser/extensions/extension_action_runner.cc
@@ -111,7 +111,7 @@
 
   // Anything that gets here should have a page or browser action.
   DCHECK(extension_action);
-  int tab_id = SessionTabHelper::IdForTab(web_contents());
+  int tab_id = SessionTabHelper::IdForTab(web_contents()).id();
   if (!extension_action->GetIsVisible(tab_id))
     return ExtensionAction::ACTION_NONE;
 
@@ -204,7 +204,7 @@
     return PermissionsData::ACCESS_ALLOWED;
 
   GURL url = web_contents()->GetVisibleURL();
-  int tab_id = SessionTabHelper::IdForTab(web_contents());
+  int tab_id = SessionTabHelper::IdForTab(web_contents()).id();
   switch (type) {
     case UserScript::CONTENT_SCRIPT:
       return extension->permissions_data()->GetContentScriptAccess(
diff --git a/chrome/browser/extensions/extension_action_test_util.cc b/chrome/browser/extensions/extension_action_test_util.cc
index 5314e6b..b4ff1103 100644
--- a/chrome/browser/extensions/extension_action_test_util.cc
+++ b/chrome/browser/extensions/extension_action_test_util.cc
@@ -28,7 +28,7 @@
                           bool only_count_visible) {
   DCHECK(web_contents);
   size_t count = 0u;
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents);
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   ToolbarActionsModel* toolbar_model = ToolbarActionsModel::Get(profile);
@@ -44,7 +44,7 @@
       ExtensionAction* extension_action =
           action_manager->GetPageAction(*extension);
       if (extension_action &&
-          (!only_count_visible || extension_action->GetIsVisible(tab_id)))
+          (!only_count_visible || extension_action->GetIsVisible(tab_id.id())))
         ++count;
     }
   }
diff --git a/chrome/browser/extensions/extension_context_menu_model.cc b/chrome/browser/extensions/extension_context_menu_model.cc
index dc2099a..6c601b4 100644
--- a/chrome/browser/extensions/extension_context_menu_model.cc
+++ b/chrome/browser/extensions/extension_context_menu_model.cc
@@ -209,7 +209,7 @@
       content::WebContents* web_contents = GetActiveWebContents();
       return web_contents && extension_action_ &&
              extension_action_->HasPopup(
-                 SessionTabHelper::IdForTab(web_contents));
+                 SessionTabHelper::IdForTab(web_contents).id());
     }
     case UNINSTALL:
       return !IsExtensionRequiredByPolicy(extension, profile_);
diff --git a/chrome/browser/extensions/extension_context_menu_model_unittest.cc b/chrome/browser/extensions/extension_context_menu_model_unittest.cc
index 15faf18..e2f01a5 100644
--- a/chrome/browser/extensions/extension_context_menu_model_unittest.cc
+++ b/chrome/browser/extensions/extension_context_menu_model_unittest.cc
@@ -43,11 +43,14 @@
 #include "extensions/common/value_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/display/test/scoped_screen_override.h"
 #include "ui/display/test/test_screen.h"
 #include "ui/gfx/image/image.h"
 
 namespace extensions {
 
+using display::test::ScopedScreenOverride;
+
 namespace {
 
 void Increment(int* i) {
@@ -191,6 +194,7 @@
   std::unique_ptr<TestBrowserWindow> test_window_;
   std::unique_ptr<Browser> browser_;
   display::test::TestScreen test_screen_;
+  std::unique_ptr<ScopedScreenOverride> scoped_screen_override_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionContextMenuModelTest);
 };
@@ -243,7 +247,8 @@
 void ExtensionContextMenuModelTest::SetUp() {
   ExtensionServiceTestBase::SetUp();
   content::BrowserSideNavigationSetUp();
-  display::Screen::SetScreenInstance(&test_screen_);
+  scoped_screen_override_ =
+      std::make_unique<ScopedScreenOverride>(&test_screen_);
 }
 
 void ExtensionContextMenuModelTest::TearDown() {
diff --git a/chrome/browser/extensions/extension_keybinding_apitest.cc b/chrome/browser/extensions/extension_keybinding_apitest.cc
index ee4fdc5..429ca97c 100644
--- a/chrome/browser/extensions/extension_keybinding_apitest.cc
+++ b/chrome/browser/extensions/extension_keybinding_apitest.cc
@@ -140,7 +140,7 @@
   bool IsGrantedForTab(const Extension* extension,
                        const content::WebContents* web_contents) {
     return extension->permissions_data()->HasAPIPermissionForTab(
-        SessionTabHelper::IdForTab(web_contents), APIPermission::kTab);
+        SessionTabHelper::IdForTab(web_contents).id(), APIPermission::kTab);
   }
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index bc0755e..fae948e 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -17,7 +17,7 @@
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -101,7 +101,7 @@
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   if (browser && !ExtensionTabUtil::BrowserSupportsTabs(browser))
     return -1;
-  return SessionTabHelper::IdForTab(web_contents);
+  return SessionTabHelper::IdForTab(web_contents).id();
 }
 
 ExtensionTabUtil::Delegate* g_extension_tab_util_delegate = nullptr;
@@ -305,7 +305,7 @@
 }
 
 int ExtensionTabUtil::GetTabId(const WebContents* web_contents) {
-  return SessionTabHelper::IdForTab(web_contents);
+  return SessionTabHelper::IdForTab(web_contents).id();
 }
 
 std::string ExtensionTabUtil::GetTabStatusText(bool is_loading) {
@@ -313,7 +313,7 @@
 }
 
 int ExtensionTabUtil::GetWindowIdOfTab(const WebContents* web_contents) {
-  return SessionTabHelper::IdForWindowContainingTab(web_contents);
+  return SessionTabHelper::IdForWindowContainingTab(web_contents).id();
 }
 
 // static
@@ -352,10 +352,13 @@
   tab_object->highlighted = tab_strip && tab_strip->IsTabSelected(tab_index);
   tab_object->pinned = tab_strip && tab_strip->IsTabPinned(tab_index);
   tab_object->audible = std::make_unique<bool>(contents->WasRecentlyAudible());
+  auto* tab_lifeycle_unit_external =
+      resource_coordinator::TabLifecycleUnitExternal::FromWebContents(contents);
   tab_object->discarded =
-      g_browser_process->GetTabManager()->IsTabDiscarded(contents);
+      tab_lifeycle_unit_external && tab_lifeycle_unit_external->IsDiscarded();
   tab_object->auto_discardable =
-      g_browser_process->GetTabManager()->IsTabAutoDiscardable(contents);
+      !tab_lifeycle_unit_external ||
+      tab_lifeycle_unit_external->IsAutoDiscardable();
   tab_object->muted_info = CreateMutedInfo(contents);
   tab_object->incognito = contents->GetBrowserContext()->IsOffTheRecord();
   gfx::Size contents_size = contents->GetContainerBounds().size();
@@ -568,7 +571,7 @@
       TabStripModel* target_tab_strip = target_browser->tab_strip_model();
       for (int i = 0; i < target_tab_strip->count(); ++i) {
         WebContents* target_contents = target_tab_strip->GetWebContentsAt(i);
-        if (SessionTabHelper::IdForTab(target_contents) == tab_id) {
+        if (SessionTabHelper::IdForTab(target_contents).id() == tab_id) {
           if (browser)
             *browser = target_browser;
           if (tab_strip)
diff --git a/chrome/browser/extensions/native_bindings_apitest.cc b/chrome/browser/extensions/native_bindings_apitest.cc
index 2a826f7..b37a4de 100644
--- a/chrome/browser/extensions/native_bindings_apitest.cc
+++ b/chrome/browser/extensions/native_bindings_apitest.cc
@@ -102,7 +102,7 @@
       ExtensionActionManager::Get(profile())->GetPageAction(*extension);
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   EXPECT_FALSE(page_action->GetIsVisible(tab_id));
   EXPECT_TRUE(page_action->GetDeclarativeIcon(tab_id).IsEmpty());
 
diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc
index 23ef319a..32a5003 100644
--- a/chrome/browser/extensions/tab_helper.cc
+++ b/chrome/browser/extensions/tab_helper.cc
@@ -171,9 +171,7 @@
   web_contents->ForEachFrame(
       base::BindRepeating(&TabHelper::SetTabId, base::Unretained(this)));
   active_tab_permission_granter_.reset(new ActiveTabPermissionGranter(
-      web_contents,
-      SessionTabHelper::IdForTab(web_contents),
-      profile_));
+      web_contents, SessionTabHelper::IdForTab(web_contents).id(), profile_));
 
   AddScriptExecutionObserver(ActivityLog::GetInstance(profile_));
 
@@ -612,9 +610,9 @@
 }
 
 void TabHelper::SetTabId(content::RenderFrameHost* render_frame_host) {
-  render_frame_host->Send(
-      new ExtensionMsg_SetTabId(render_frame_host->GetRoutingID(),
-                                SessionTabHelper::IdForTab(web_contents())));
+  render_frame_host->Send(new ExtensionMsg_SetTabId(
+      render_frame_host->GetRoutingID(),
+      SessionTabHelper::IdForTab(web_contents()).id()));
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/feedback/feedback_dialog_utils.cc b/chrome/browser/feedback/feedback_dialog_utils.cc
index 7946c42b..3c4a96f 100644
--- a/chrome/browser/feedback/feedback_dialog_utils.cc
+++ b/chrome/browser/feedback/feedback_dialog_utils.cc
@@ -21,7 +21,7 @@
 
 namespace chrome {
 
-GURL GetTargetTabUrl(int session_id, int index) {
+GURL GetTargetTabUrl(SessionID session_id, int index) {
   Browser* browser = chrome::FindBrowserWithID(session_id);
   // Sanity checks.
   if (!browser || index >= browser->tab_strip_model()->count())
diff --git a/chrome/browser/feedback/feedback_dialog_utils.h b/chrome/browser/feedback/feedback_dialog_utils.h
index fa7f0f6..ff049e6 100644
--- a/chrome/browser/feedback/feedback_dialog_utils.h
+++ b/chrome/browser/feedback/feedback_dialog_utils.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_FEEDBACK_FEEDBACK_DIALOG_UTILS_H_
 #define CHROME_BROWSER_FEEDBACK_FEEDBACK_DIALOG_UTILS_H_
 
+#include "components/sessions/core/session_id.h"
+
 class Browser;
 class GURL;
 class Profile;
@@ -14,7 +16,7 @@
 
 // Get the GURL of the active tab when the feedback dialog was invoked, if
 // any.
-GURL GetTargetTabUrl(int session_id, int index);
+GURL GetTargetTabUrl(SessionID session_id, int index);
 
 // Get the profile that should be used to open the feedback dialog.
 Profile* GetFeedbackProfile(Browser* browser);
diff --git a/chrome/browser/feedback/show_feedback_page.cc b/chrome/browser/feedback/show_feedback_page.cc
index 31047965e..4c24fdf 100644
--- a/chrome/browser/feedback/show_feedback_page.cc
+++ b/chrome/browser/feedback/show_feedback_page.cc
@@ -25,7 +25,7 @@
                       const std::string& extra_diagnostics) {
   GURL page_url;
   if (browser) {
-    page_url = GetTargetTabUrl(browser->session_id().id(),
+    page_url = GetTargetTabUrl(browser->session_id(),
                                browser->tab_strip_model()->active_index());
   }
 
diff --git a/chrome/browser/media/cast_remoting_connector.cc b/chrome/browser/media/cast_remoting_connector.cc
index 4bddaf91..a298d9e2 100644
--- a/chrome/browser/media/cast_remoting_connector.cc
+++ b/chrome/browser/media/cast_remoting_connector.cc
@@ -129,8 +129,8 @@
   if (!connector) {
     // TODO(xjz): Use TabAndroid::GetAndroidId() to get the tab ID when support
     // remoting on Android.
-    const SessionID::id_type tab_id = SessionTabHelper::IdForTab(contents);
-    if (tab_id == -1)
+    const SessionID tab_id = SessionTabHelper::IdForTab(contents);
+    if (!tab_id.is_valid())
       return nullptr;
     connector = new CastRemotingConnector(
         media_router::MediaRouterFactory::GetApiForBrowserContext(
@@ -157,7 +157,7 @@
 }
 
 CastRemotingConnector::CastRemotingConnector(media_router::MediaRouter* router,
-                                             int32_t tab_id)
+                                             SessionID tab_id)
     : media_router_(router),
       tab_id_(tab_id),
       active_bridge_(nullptr),
diff --git a/chrome/browser/media/cast_remoting_connector.h b/chrome/browser/media/cast_remoting_connector.h
index 1fe3f7c..6a53a34 100644
--- a/chrome/browser/media/cast_remoting_connector.h
+++ b/chrome/browser/media/cast_remoting_connector.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/supports_user_data.h"
+#include "components/sessions/core/session_id.h"
 #include "media/mojo/interfaces/mirror_service_remoting.mojom.h"
 #include "media/mojo/interfaces/remoting.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -107,7 +108,7 @@
 
   // Main constructor. |tab_id| refers to any remoted content managed
   // by this instance (i.e., any remoted content from one tab/WebContents).
-  CastRemotingConnector(media_router::MediaRouter* router, int32_t tab_id);
+  CastRemotingConnector(media_router::MediaRouter* router, SessionID tab_id);
 
   // Creates a RemotingBridge that implements the requested Remoter service, and
   // binds it to the interface |request|.
@@ -163,7 +164,7 @@
 
   media_router::MediaRouter* const media_router_;
 
-  const int32_t tab_id_;
+  const SessionID tab_id_;
 
   // Describes the remoting sink's metadata and the enabled features. The sink's
   // metadata is updated by the mirror service calling OnSinkAvailable() and
diff --git a/chrome/browser/media/cast_remoting_connector_unittest.cc b/chrome/browser/media/cast_remoting_connector_unittest.cc
index 668e80e..2e15e81 100644
--- a/chrome/browser/media/cast_remoting_connector_unittest.cc
+++ b/chrome/browser/media/cast_remoting_connector_unittest.cc
@@ -42,7 +42,7 @@
 
 namespace {
 
-constexpr int32_t kRemotingTabId = 2;
+constexpr SessionID kRemotingTabId = SessionID::FromSerializedValue(2);
 
 RemotingSinkMetadataPtr GetDefaultSinkMetadata() {
   RemotingSinkMetadataPtr metadata = RemotingSinkMetadata::New();
@@ -63,21 +63,21 @@
   FakeMediaRouter() : weak_factory_(this) {}
   ~FakeMediaRouter() final {}
 
-  void RegisterRemotingSource(int32_t tab_id,
+  void RegisterRemotingSource(SessionID tab_id,
                               CastRemotingConnector* remoting_source) final {
-    EXPECT_EQ(-1, tab_id_);
+    EXPECT_FALSE(tab_id_.is_valid());
     tab_id_ = tab_id;
     connector_ = remoting_source;
   }
 
-  void UnregisterRemotingSource(int32_t tab_id) final {
+  void UnregisterRemotingSource(SessionID tab_id) final {
     EXPECT_EQ(tab_id, tab_id_);
-    tab_id_ = -1;
+    tab_id_ = SessionID::InvalidValue();
     connector_ = nullptr;
   }
 
   void OnMediaRemoterCreated(
-      int32_t tab_id,
+      SessionID tab_id,
       MirrorServiceRemoterPtr remoter,
       MirrorServiceRemotingSourceRequest remoting_source) {
     if (tab_id != tab_id_)
@@ -89,10 +89,10 @@
   }
 
   // Get the registered tab ID.
-  int32_t tab_id() const { return tab_id_; }
+  SessionID tab_id() const { return tab_id_; }
 
  private:
-  int32_t tab_id_ = -1;
+  SessionID tab_id_ = SessionID::InvalidValue();
   CastRemotingConnector* connector_ = nullptr;
 
   base::WeakPtrFactory<FakeMediaRouter> weak_factory_;
diff --git a/chrome/browser/media/router/media_router.h b/chrome/browser/media/router/media_router.h
index 7413fd2a..85b9f720 100644
--- a/chrome/browser/media/router/media_router.h
+++ b/chrome/browser/media/router/media_router.h
@@ -21,6 +21,7 @@
 #include "chrome/common/media_router/media_sink.h"
 #include "chrome/common/media_router/media_source.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/sessions/core/session_id.h"
 #include "content/public/browser/media_controller.h"
 #include "content/public/browser/presentation_service_delegate.h"
 
@@ -205,9 +206,9 @@
   // given |tab_id|, only one CastRemotingConnector can be registered. The
   // registered CastRemotingConnector should be removed before it is destroyed.
   virtual void RegisterRemotingSource(
-      int32_t tab_id,
+      SessionID tab_id,
       CastRemotingConnector* remoting_source) = 0;
-  virtual void UnregisterRemotingSource(int32_t tab_id) = 0;
+  virtual void UnregisterRemotingSource(SessionID tab_id) = 0;
 
   // Returns media router state as a JSON string represented by base::Vaule.
   // Used by media-router-internals page.
diff --git a/chrome/browser/media/router/media_router_base.cc b/chrome/browser/media/router/media_router_base.cc
index 66554a0ef..ec72ea79 100644
--- a/chrome/browser/media/router/media_router_base.cc
+++ b/chrome/browser/media/router/media_router_base.cc
@@ -174,7 +174,7 @@
 #endif  // !defined(OS_ANDROID)
 
 void MediaRouterBase::RegisterRemotingSource(
-    int32_t tab_id,
+    SessionID tab_id,
     CastRemotingConnector* remoting_source) {
   auto it = remoting_sources_.find(tab_id);
   if (it != remoting_sources_.end()) {
@@ -184,7 +184,7 @@
   remoting_sources_.emplace(tab_id, remoting_source);
 }
 
-void MediaRouterBase::UnregisterRemotingSource(int32_t tab_id) {
+void MediaRouterBase::UnregisterRemotingSource(SessionID tab_id) {
   auto it = remoting_sources_.find(tab_id);
   DCHECK(it != remoting_sources_.end());
   remoting_sources_.erase(it);
diff --git a/chrome/browser/media/router/media_router_base.h b/chrome/browser/media/router/media_router_base.h
index 9827a37..6859a2f 100644
--- a/chrome/browser/media/router/media_router_base.h
+++ b/chrome/browser/media/router/media_router_base.h
@@ -40,9 +40,9 @@
   scoped_refptr<MediaRouteController> GetRouteController(
       const MediaRoute::Id& route_id) override;
 #endif  // !defined(OS_ANDROID)
-  void RegisterRemotingSource(int32_t tab_id,
+  void RegisterRemotingSource(SessionID tab_id,
                               CastRemotingConnector* remoting_source) override;
-  void UnregisterRemotingSource(int32_t tab_id) override;
+  void UnregisterRemotingSource(SessionID tab_id) override;
   base::Value GetState() const override;
 
  protected:
@@ -85,7 +85,8 @@
   // Stores CastRemotingConnectors that can be connected to the MediaRemoter
   // for media remoting when MediaRemoter is started. The map uses the tab ID
   // as the key.
-  std::unordered_map<int32_t, CastRemotingConnector*> remoting_sources_;
+  std::unordered_map<SessionID, CastRemotingConnector*, SessionID::Hasher>
+      remoting_sources_;
 
  private:
   friend class MediaRouterBaseTest;
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index 3432134..74f6187 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -213,7 +213,7 @@
     provider_id = MediaRouteProviderId::EXTENSION;
   }
 
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   std::string presentation_id = MediaRouterBase::CreatePresentationId();
   auto callback = base::BindOnce(
       &MediaRouterMojoImpl::RouteResponseReceived, weak_factory_.GetWeakPtr(),
@@ -246,7 +246,7 @@
     return;
   }
 
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   auto callback = base::BindOnce(
       &MediaRouterMojoImpl::RouteResponseReceived, weak_factory_.GetWeakPtr(),
       presentation_id, *provider_id, incognito, std::move(callbacks), true);
@@ -273,7 +273,7 @@
     return;
   }
 
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   std::string presentation_id = MediaRouterBase::CreatePresentationId();
   auto callback = base::BindOnce(
       &MediaRouterMojoImpl::RouteResponseReceived, weak_factory_.GetWeakPtr(),
@@ -901,7 +901,7 @@
     media::mojom::MirrorServiceRemotingSourceRequest source_request) {
   DVLOG_WITH_INSTANCE(1) << __func__ << ": tab_id = " << tab_id;
 
-  auto it = remoting_sources_.find(tab_id);
+  auto it = remoting_sources_.find(SessionID::FromSerializedValue(tab_id));
   if (it == remoting_sources_.end()) {
     LOG(WARNING) << __func__
                  << ": No registered remoting source for tab_id = " << tab_id;
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index da3c439..3d106b1 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -587,7 +587,11 @@
 
   if (IsMetricsReportingForceEnabled() ||
       base::FeatureList::IsEnabled(ukm::kUkmFeature)) {
-    ukm_service_.reset(new ukm::UkmService(local_state, this));
+    // We only need to restrict to whitelisted Entries if metrics reporting
+    // is not forced.
+    bool restrict_to_whitelist_entries = !IsMetricsReportingForceEnabled();
+    ukm_service_.reset(
+        new ukm::UkmService(local_state, this, restrict_to_whitelist_entries));
     ukm_service_->SetIsWebstoreExtensionCallback(
         base::BindRepeating(&IsWebstoreExtension));
     RegisterUKMProviders();
diff --git a/chrome/browser/net/trial_comparison_cert_verifier.h b/chrome/browser/net/trial_comparison_cert_verifier.h
index f7a05a7..7d3f2f83 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier.h
+++ b/chrome/browser/net/trial_comparison_cert_verifier.h
@@ -17,7 +17,7 @@
 class CertVerifyProc;
 }
 
-class NET_EXPORT TrialComparisonCertVerifier : public net::CertVerifier {
+class TrialComparisonCertVerifier : public net::CertVerifier {
  public:
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc b/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
index 6c80be0..da25185 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
@@ -181,22 +181,17 @@
         net::X509Certificate::FORMAT_AUTO);
     ASSERT_TRUE(cert_chain_2_);
 
-    reporting_service_test_helper_.SetUpInterceptor();
+    reporting_service_test_helper_ =
+        base::MakeRefCounted<CertificateReportingServiceTestHelper>();
     CertificateReportingServiceFactory::GetInstance()
         ->SetReportEncryptionParamsForTesting(
             reporting_service_test_helper()->server_public_key(),
             reporting_service_test_helper()->server_public_key_version());
+    CertificateReportingServiceFactory::GetInstance()
+        ->SetURLLoaderFactoryForTesting(reporting_service_test_helper_);
     reporting_service_test_helper()->SetFailureMode(
         certificate_reporting_test_utils::REPORTS_SUCCESSFUL);
 
-    url_request_context_getter_ =
-        base::MakeRefCounted<net::TestURLRequestContextGetter>(
-            (content::BrowserThread::GetTaskRunnerForThread(
-                content::BrowserThread::IO)));
-
-    TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(
-        url_request_context_getter_.get());
-
     sb_service_ = base::MakeRefCounted<safe_browsing::TestSafeBrowsingService>(
         // Doesn't matter, just need to choose one.
         safe_browsing::V4FeatureList::V4UsageStatus::V4_DISABLED);
@@ -236,7 +231,7 @@
   }
 
   CertificateReportingServiceTestHelper* reporting_service_test_helper() {
-    return &reporting_service_test_helper_;
+    return reporting_service_test_helper_.get();
   }
 
   CertificateReportingService* service() const {
@@ -252,12 +247,12 @@
 
  private:
   content::TestBrowserThreadBundle thread_bundle_;
-  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
   scoped_refptr<safe_browsing::SafeBrowsingService> sb_service_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   TestingProfile* profile_;
 
-  CertificateReportingServiceTestHelper reporting_service_test_helper_;
+  scoped_refptr<CertificateReportingServiceTestHelper>
+      reporting_service_test_helper_;
 };
 
 TEST_F(TrialComparisonCertVerifierTest, NotOptedIn) {
diff --git a/chrome/browser/notifications/chrome_ash_message_center_client.cc b/chrome/browser/notifications/chrome_ash_message_center_client.cc
index 9d77aa0..1fae3b1 100644
--- a/chrome/browser/notifications/chrome_ash_message_center_client.cc
+++ b/chrome/browser/notifications/chrome_ash_message_center_client.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/interfaces/constants.mojom.h"
 #include "base/i18n/string_compare.h"
+#include "base/stl_util.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/notifications/arc_application_notifier_controller_chromeos.h"
 #include "chrome/browser/notifications/extension_notifier_controller.h"
@@ -20,6 +21,9 @@
 
 namespace {
 
+// The singleton instance, which is tracked to allow access from tests.
+ChromeAshMessageCenterClient* g_chrome_ash_message_center_client = nullptr;
+
 // All notifier actions are performed on the notifiers for the currently active
 // profile, so this just returns the active profile.
 Profile* GetProfileForNotifiers() {
@@ -51,6 +55,9 @@
 ChromeAshMessageCenterClient::ChromeAshMessageCenterClient(
     NotificationPlatformBridgeDelegate* delegate)
     : delegate_(delegate), binding_(this) {
+  DCHECK(!g_chrome_ash_message_center_client);
+  g_chrome_ash_message_center_client = this;
+
   // May be null in unit tests.
   auto* connection = content::ServiceManagerConnection::GetForProcess();
   if (connection) {
@@ -77,7 +84,10 @@
           new arc::ArcApplicationNotifierControllerChromeOS(this))));
 }
 
-ChromeAshMessageCenterClient::~ChromeAshMessageCenterClient() {}
+ChromeAshMessageCenterClient::~ChromeAshMessageCenterClient() {
+  DCHECK_EQ(this, g_chrome_ash_message_center_client);
+  g_chrome_ash_message_center_client = nullptr;
+}
 
 // The unused variables here will not be a part of the future
 // NotificationPlatformBridge interface.
@@ -86,7 +96,18 @@
     Profile* /* profile */,
     const message_center::Notification& notification,
     std::unique_ptr<NotificationCommon::Metadata> metadata) {
-  controller_->ShowClientNotification(notification);
+  // Remove any previous mapping to |notification.id()| before inserting a new
+  // one.
+  base::EraseIf(
+      displayed_notifications_,
+      [notification](
+          const std::pair<base::UnguessableToken, std::string>& pair) {
+        return pair.second == notification.id();
+      });
+
+  base::UnguessableToken token = base::UnguessableToken::Create();
+  displayed_notifications_[token] = notification.id();
+  controller_->ShowClientNotification(notification, token);
 }
 
 // The unused variable here will not be a part of the future
@@ -113,9 +134,13 @@
 }
 
 void ChromeAshMessageCenterClient::HandleNotificationClosed(
-    const std::string& id,
+    const base::UnguessableToken& display_token,
     bool by_user) {
-  delegate_->HandleNotificationClosed(id, by_user);
+  auto entry = displayed_notifications_.find(display_token);
+  if (entry != displayed_notifications_.end()) {
+    delegate_->HandleNotificationClosed(entry->second, by_user);
+    displayed_notifications_.erase(entry);
+  }
 }
 
 void ChromeAshMessageCenterClient::HandleNotificationClicked(
@@ -180,3 +205,8 @@
   if (controller_)
     controller_->NotifierEnabledChanged(notifier_id, enabled);
 }
+
+// static
+void ChromeAshMessageCenterClient::FlushForTesting() {
+  g_chrome_ash_message_center_client->binding_.FlushForTesting();
+}
diff --git a/chrome/browser/notifications/chrome_ash_message_center_client.h b/chrome/browser/notifications/chrome_ash_message_center_client.h
index 96cadb7d..8e953179 100644
--- a/chrome/browser/notifications/chrome_ash_message_center_client.h
+++ b/chrome/browser/notifications/chrome_ash_message_center_client.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_NOTIFICATIONS_CHROME_ASH_MESSAGE_CENTER_CLIENT_H_
 
 #include "ash/public/interfaces/ash_message_center_controller.mojom.h"
+#include "base/unguessable_token.h"
 #include "chrome/browser/notifications/notification_platform_bridge.h"
 #include "chrome/browser/notifications/notification_platform_bridge_chromeos.h"
 #include "chrome/browser/notifications/notifier_controller.h"
@@ -37,7 +38,8 @@
   void SetReadyCallback(NotificationBridgeReadyCallback callback) override;
 
   // ash::mojom::AshMessageCenterClient:
-  void HandleNotificationClosed(const std::string& id, bool by_user) override;
+  void HandleNotificationClosed(const base::UnguessableToken& display_token,
+                                bool by_user) override;
   void HandleNotificationClicked(const std::string& id) override;
   void HandleNotificationButtonClicked(
       const std::string& id,
@@ -55,9 +57,20 @@
   void OnNotifierEnabledChanged(const message_center::NotifierId& notifier_id,
                                 bool enabled) override;
 
+  // Flushs |binding_|.
+  static void FlushForTesting();
+
  private:
   NotificationPlatformBridgeDelegate* delegate_;
 
+  // A mapping from display token to notification ID. The display token is
+  // generated each time a notification is shown (even if a notification is
+  // displayed more than once). This allows |this| to drop out-of-order
+  // HandleNotificationClosed() calls (i.e. those that arrive after the
+  // notification has already been re-displayed/updated and refer to an earlier
+  // notification).
+  std::map<base::UnguessableToken, std::string> displayed_notifications_;
+
   // Notifier source for each notifier type.
   std::map<message_center::NotifierId::NotifierType,
            std::unique_ptr<NotifierController>>
diff --git a/chrome/browser/notifications/chrome_ash_message_center_client_browsertest.cc b/chrome/browser/notifications/chrome_ash_message_center_client_browsertest.cc
new file mode 100644
index 0000000..cec2feb3f
--- /dev/null
+++ b/chrome/browser/notifications/chrome_ash_message_center_client_browsertest.cc
@@ -0,0 +1,89 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/chrome_ash_message_center_client.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/notifications/notification_display_service.h"
+#include "chrome/browser/notifications/notification_test_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
+
+namespace {
+
+class ChromeAshMessageCenterClientBrowserTest : public InProcessBrowserTest {
+ public:
+  ChromeAshMessageCenterClientBrowserTest() = default;
+  ~ChromeAshMessageCenterClientBrowserTest() override = default;
+
+  // InProcessBrowserTest overrides.
+  void SetUpInProcessBrowserTestFixture() override {
+    scoped_feature_list_.InitWithFeatures({features::kNativeNotifications}, {});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeAshMessageCenterClientBrowserTest);
+};
+
+class TestNotificationDelegate : public message_center::NotificationDelegate {
+ public:
+  TestNotificationDelegate() {}
+
+  void Wait() { run_loop_.Run(); }
+
+  void Close(bool by_user) override {
+    close_count_++;
+    run_loop_.Quit();
+  }
+
+  int close_count() const { return close_count_; }
+
+ private:
+  ~TestNotificationDelegate() override {}
+
+  base::RunLoop run_loop_;
+  int close_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TestNotificationDelegate);
+};
+
+// Regression test for https://crbug.com/825141 that verifies out-of-order
+// Display/Close pairs are handled correctly.
+IN_PROC_BROWSER_TEST_F(ChromeAshMessageCenterClientBrowserTest,
+                       DisplayCloseOrdering) {
+  auto delegate = base::MakeRefCounted<TestNotificationDelegate>();
+  const std::string id("notification_identifier");
+  message_center::Notification notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE, id, base::string16(),
+      base::string16(), gfx::Image(), base::ASCIIToUTF16("display_source"),
+      GURL(),
+      message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
+                                 "notifier_id"),
+      {}, delegate);
+
+  auto* display_service =
+      NotificationDisplayService::GetForProfile(browser()->profile());
+  display_service->Display(NotificationHandler::Type::TRANSIENT, notification);
+  display_service->Close(NotificationHandler::Type::TRANSIENT,
+                         notification.id());
+  // The Close callback should be fired asynchronously, so there is no close
+  // yet.
+  EXPECT_EQ(0, delegate->close_count());
+
+  display_service->Display(NotificationHandler::Type::TRANSIENT, notification);
+  display_service->Close(NotificationHandler::Type::TRANSIENT,
+                         notification.id());
+  ChromeAshMessageCenterClient::FlushForTesting();
+
+  // Only one close logged because Display was called again before the first
+  // close arrived.
+  EXPECT_EQ(1, delegate->close_count());
+}
+
+}  // namespace
diff --git a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
index d7418430..adc31c79 100644
--- a/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_chromeos.cc
@@ -48,7 +48,12 @@
 void NotificationPlatformBridgeChromeOs::Close(
     Profile* profile,
     const std::string& notification_id) {
-  impl_->Close(profile, notification_id);
+  // TODO(estade): we need |is_incognito| here.
+  const std::string profile_notification_id =
+      ProfileNotification::GetProfileNotificationId(
+          notification_id, NotificationUIManager::GetProfileID(profile));
+
+  impl_->Close(nullptr, profile_notification_id);
 }
 
 void NotificationPlatformBridgeChromeOs::GetDisplayed(
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 3e80ef3..6bb4818 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -88,7 +88,7 @@
 
   MOCK_METHOD3(FillReferrerChain,
                void(const GURL&,
-                    int,
+                    SessionID,
                     safe_browsing::LoginReputationClientRequest::Frame*));
   MOCK_METHOD0(IsExtendedReporting, bool());
   MOCK_METHOD0(IsIncognito, bool());
diff --git a/chrome/browser/predictors/loading_data_collector_unittest.cc b/chrome/browser/predictors/loading_data_collector_unittest.cc
index de49e26..e8bc28b 100644
--- a/chrome/browser/predictors/loading_data_collector_unittest.cc
+++ b/chrome/browser/predictors/loading_data_collector_unittest.cc
@@ -74,7 +74,7 @@
   EXPECT_FALSE(summary.always_revalidate);
 
   // Navigation_id elements should be unset by default.
-  EXPECT_EQ(-1, summary.navigation_id.tab_id);
+  EXPECT_FALSE(summary.navigation_id.tab_id.is_valid());
   EXPECT_EQ(GURL(), summary.navigation_id.main_frame_url);
 }
 
@@ -358,51 +358,52 @@
 // Single navigation that will be recorded. Will check for duplicate
 // resources and also for number of resources saved.
 TEST_F(LoadingDataCollectorTest, SimpleNavigation) {
+  const SessionID kTabId = SessionID::FromSerializedValue(1);
   URLRequestSummary main_frame =
-      CreateURLRequestSummary(1, "http://www.google.com");
+      CreateURLRequestSummary(kTabId, "http://www.google.com");
   collector_->RecordURLRequest(main_frame);
   collector_->RecordURLResponse(main_frame);
   EXPECT_EQ(1U, collector_->inflight_navigations_.size());
 
   std::vector<URLRequestSummary> resources;
   resources.push_back(CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/style1.css",
+      kTabId, "http://www.google.com", "http://google.com/style1.css",
       content::RESOURCE_TYPE_STYLESHEET));
   collector_->RecordURLResponse(resources.back());
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script1.js",
                                               content::RESOURCE_TYPE_SCRIPT));
   collector_->RecordURLResponse(resources.back());
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script2.js",
                                               content::RESOURCE_TYPE_SCRIPT));
   collector_->RecordURLResponse(resources.back());
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script1.js",
                                               content::RESOURCE_TYPE_SCRIPT));
   collector_->RecordURLResponse(resources.back());
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/image1.png",
                                               content::RESOURCE_TYPE_IMAGE));
   collector_->RecordURLResponse(resources.back());
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/image2.png",
                                               content::RESOURCE_TYPE_IMAGE));
   collector_->RecordURLResponse(resources.back());
   resources.push_back(CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/style2.css",
+      kTabId, "http://www.google.com", "http://google.com/style2.css",
       content::RESOURCE_TYPE_STYLESHEET));
   collector_->RecordURLResponse(resources.back());
 
   auto no_store =
-      CreateURLRequestSummary(1, "http://www.google.com",
+      CreateURLRequestSummary(kTabId, "http://www.google.com",
                               "http://static.google.com/style2-no-store.css",
                               content::RESOURCE_TYPE_STYLESHEET);
   no_store.is_no_store = true;
   collector_->RecordURLResponse(no_store);
 
   auto redirected = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://reader.google.com/style.css",
+      kTabId, "http://www.google.com", "http://reader.google.com/style.css",
       content::RESOURCE_TYPE_STYLESHEET);
   redirected.redirect_url = GURL("http://dev.null.google.com/style.css");
   collector_->RecordURLRedirect(redirected);
@@ -425,18 +426,20 @@
 }
 
 TEST_F(LoadingDataCollectorTest, SimpleRedirect) {
-  URLRequestSummary fb1 = CreateURLRequestSummary(1, "http://fb.com/google");
+  const SessionID kTabId = SessionID::FromSerializedValue(1);
+  URLRequestSummary fb1 =
+      CreateURLRequestSummary(kTabId, "http://fb.com/google");
   collector_->RecordURLRequest(fb1);
   EXPECT_EQ(1U, collector_->inflight_navigations_.size());
 
   URLRequestSummary fb2 = CreateRedirectRequestSummary(
-      1, "http://fb.com/google", "http://facebook.com/google");
+      kTabId, "http://fb.com/google", "http://facebook.com/google");
   collector_->RecordURLRedirect(fb2);
   URLRequestSummary fb3 = CreateRedirectRequestSummary(
-      1, "http://facebook.com/google", "https://facebook.com/google");
+      kTabId, "http://facebook.com/google", "https://facebook.com/google");
   collector_->RecordURLRedirect(fb3);
   URLRequestSummary fb4 =
-      CreateURLRequestSummary(1, "https://facebook.com/google");
+      CreateURLRequestSummary(kTabId, "https://facebook.com/google");
   collector_->RecordURLResponse(fb4);
 
   EXPECT_CALL(
@@ -449,15 +452,20 @@
 }
 
 TEST_F(LoadingDataCollectorTest, OnMainFrameRequest) {
+  const SessionID kTabId1 = SessionID::FromSerializedValue(1);
+  const SessionID kTabId2 = SessionID::FromSerializedValue(2);
+  const SessionID kTabId3 = SessionID::FromSerializedValue(3);
+  const SessionID kTabId4 = SessionID::FromSerializedValue(4);
+
   URLRequestSummary summary1 = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://www.google.com",
+      kTabId1, "http://www.google.com", "http://www.google.com",
       content::RESOURCE_TYPE_MAIN_FRAME);
   URLRequestSummary summary2 = CreateURLRequestSummary(
-      2, "http://www.google.com", "http://www.google.com",
+      kTabId2, "http://www.google.com", "http://www.google.com",
       content::RESOURCE_TYPE_MAIN_FRAME);
-  URLRequestSummary summary3 =
-      CreateURLRequestSummary(3, "http://www.yahoo.com", "http://www.yahoo.com",
-                              content::RESOURCE_TYPE_MAIN_FRAME);
+  URLRequestSummary summary3 = CreateURLRequestSummary(
+      kTabId3, "http://www.yahoo.com", "http://www.yahoo.com",
+      content::RESOURCE_TYPE_MAIN_FRAME);
 
   collector_->RecordURLRequest(summary1);
   EXPECT_EQ(1U, collector_->inflight_navigations_.size());
@@ -467,11 +475,11 @@
   EXPECT_EQ(3U, collector_->inflight_navigations_.size());
 
   // Insert another with same navigation id. It should replace.
-  URLRequestSummary summary4 =
-      CreateURLRequestSummary(1, "http://www.nike.com", "http://www.nike.com",
-                              content::RESOURCE_TYPE_MAIN_FRAME);
+  URLRequestSummary summary4 = CreateURLRequestSummary(
+      kTabId1, "http://www.nike.com", "http://www.nike.com",
+      content::RESOURCE_TYPE_MAIN_FRAME);
   URLRequestSummary summary5 = CreateURLRequestSummary(
-      2, "http://www.google.com", "http://www.google.com",
+      kTabId2, "http://www.google.com", "http://www.google.com",
       content::RESOURCE_TYPE_MAIN_FRAME);
 
   collector_->RecordURLRequest(summary4);
@@ -483,9 +491,9 @@
   collector_->RecordURLRequest(summary5);
   EXPECT_EQ(3U, collector_->inflight_navigations_.size());
 
-  URLRequestSummary summary6 =
-      CreateURLRequestSummary(4, "http://www.shoes.com", "http://www.shoes.com",
-                              content::RESOURCE_TYPE_MAIN_FRAME);
+  URLRequestSummary summary6 = CreateURLRequestSummary(
+      kTabId4, "http://www.shoes.com", "http://www.shoes.com",
+      content::RESOURCE_TYPE_MAIN_FRAME);
   collector_->RecordURLRequest(summary6);
   EXPECT_EQ(3U, collector_->inflight_navigations_.size());
 
@@ -498,35 +506,43 @@
 }
 
 TEST_F(LoadingDataCollectorTest, OnMainFrameRedirect) {
-  URLRequestSummary yahoo = CreateURLRequestSummary(1, "http://yahoo.com");
+  const SessionID kTabId1 = SessionID::FromSerializedValue(1);
+  const SessionID kTabId2 = SessionID::FromSerializedValue(2);
+  const SessionID kTabId3 = SessionID::FromSerializedValue(3);
+  const SessionID kTabId4 = SessionID::FromSerializedValue(4);
+  const SessionID kTabId5 = SessionID::FromSerializedValue(5);
 
-  URLRequestSummary bbc1 = CreateURLRequestSummary(2, "http://bbc.com");
-  URLRequestSummary bbc2 =
-      CreateRedirectRequestSummary(2, "http://bbc.com", "https://www.bbc.com");
-  NavigationID bbc_end = CreateNavigationID(2, "https://www.bbc.com");
+  URLRequestSummary yahoo =
+      CreateURLRequestSummary(kTabId1, "http://yahoo.com");
 
-  URLRequestSummary youtube1 = CreateURLRequestSummary(3, "http://youtube.com");
+  URLRequestSummary bbc1 = CreateURLRequestSummary(kTabId2, "http://bbc.com");
+  URLRequestSummary bbc2 = CreateRedirectRequestSummary(
+      kTabId2, "http://bbc.com", "https://www.bbc.com");
+  NavigationID bbc_end = CreateNavigationID(kTabId2, "https://www.bbc.com");
+
+  URLRequestSummary youtube1 =
+      CreateURLRequestSummary(kTabId3, "http://youtube.com");
   URLRequestSummary youtube2 = CreateRedirectRequestSummary(
-      3, "http://youtube.com", "https://youtube.com");
-  NavigationID youtube_end = CreateNavigationID(3, "https://youtube.com");
+      kTabId3, "http://youtube.com", "https://youtube.com");
+  NavigationID youtube_end = CreateNavigationID(kTabId3, "https://youtube.com");
 
-  URLRequestSummary nyt1 = CreateURLRequestSummary(4, "http://nyt.com");
-  URLRequestSummary nyt2 =
-      CreateRedirectRequestSummary(4, "http://nyt.com", "http://nytimes.com");
-  URLRequestSummary nyt3 = CreateRedirectRequestSummary(4, "http://nytimes.com",
-                                                        "http://m.nytimes.com");
-  NavigationID nyt_end = CreateNavigationID(4, "http://m.nytimes.com");
+  URLRequestSummary nyt1 = CreateURLRequestSummary(kTabId4, "http://nyt.com");
+  URLRequestSummary nyt2 = CreateRedirectRequestSummary(
+      kTabId4, "http://nyt.com", "http://nytimes.com");
+  URLRequestSummary nyt3 = CreateRedirectRequestSummary(
+      kTabId4, "http://nytimes.com", "http://m.nytimes.com");
+  NavigationID nyt_end = CreateNavigationID(kTabId4, "http://m.nytimes.com");
 
-  URLRequestSummary fb1 = CreateURLRequestSummary(5, "http://fb.com");
-  URLRequestSummary fb2 =
-      CreateRedirectRequestSummary(5, "http://fb.com", "http://facebook.com");
-  URLRequestSummary fb3 = CreateRedirectRequestSummary(5, "http://facebook.com",
-                                                       "https://facebook.com");
+  URLRequestSummary fb1 = CreateURLRequestSummary(kTabId5, "http://fb.com");
+  URLRequestSummary fb2 = CreateRedirectRequestSummary(kTabId5, "http://fb.com",
+                                                       "http://facebook.com");
+  URLRequestSummary fb3 = CreateRedirectRequestSummary(
+      kTabId5, "http://facebook.com", "https://facebook.com");
   URLRequestSummary fb4 = CreateRedirectRequestSummary(
-      5, "https://facebook.com",
+      kTabId5, "https://facebook.com",
       "https://m.facebook.com/?refsrc=https%3A%2F%2Fwww.facebook.com%2F&_rdr");
   NavigationID fb_end = CreateNavigationID(
-      5,
+      kTabId5,
       "https://m.facebook.com/?refsrc=https%3A%2F%2Fwww.facebook.com%2F&_rdr");
 
   // Redirect with empty redirect_url will be deleted.
@@ -572,26 +588,27 @@
 }
 
 TEST_F(LoadingDataCollectorTest, OnSubresourceResponse) {
+  const SessionID kTabId = SessionID::FromSerializedValue(1);
   // If there is no inflight navigation, nothing happens.
   URLRequestSummary resource1 = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/style1.css",
+      kTabId, "http://www.google.com", "http://google.com/style1.css",
       content::RESOURCE_TYPE_STYLESHEET);
   collector_->RecordURLResponse(resource1);
   EXPECT_TRUE(collector_->inflight_navigations_.empty());
 
   // Add an inflight navigation.
   URLRequestSummary main_frame1 = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://www.google.com",
+      kTabId, "http://www.google.com", "http://www.google.com",
       content::RESOURCE_TYPE_MAIN_FRAME);
   collector_->RecordURLRequest(main_frame1);
   EXPECT_EQ(1U, collector_->inflight_navigations_.size());
 
   // Now add a few subresources.
   URLRequestSummary resource2 = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/script1.js",
+      kTabId, "http://www.google.com", "http://google.com/script1.js",
       content::RESOURCE_TYPE_SCRIPT);
   URLRequestSummary resource3 = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/script2.js",
+      kTabId, "http://www.google.com", "http://google.com/script2.js",
       content::RESOURCE_TYPE_SCRIPT);
   collector_->RecordURLResponse(resource1);
   collector_->RecordURLResponse(resource2);
diff --git a/chrome/browser/predictors/loading_predictor_unittest.cc b/chrome/browser/predictors/loading_predictor_unittest.cc
index 2836ecb..c6965fd8 100644
--- a/chrome/browser/predictors/loading_predictor_unittest.cc
+++ b/chrome/browser/predictors/loading_predictor_unittest.cc
@@ -176,7 +176,8 @@
   predictor_->PrepareForPageLoad(url, HintOrigin::EXTERNAL);
   EXPECT_EQ(1UL, predictor_->active_hints_.size());
 
-  auto summary = CreateURLRequestSummary(12, url.spec());
+  auto summary =
+      CreateURLRequestSummary(SessionID::FromSerializedValue(12), url.spec());
   predictor_->OnMainFrameResponse(summary);
   EXPECT_TRUE(predictor_->active_hints_.empty());
 }
@@ -184,7 +185,7 @@
 TEST_F(LoadingPredictorTest, TestMainFrameRequestCancelsStaleNavigations) {
   const std::string url = kUrl;
   const std::string url2 = kUrl2;
-  const int tab_id = 12;
+  const SessionID tab_id = SessionID::FromSerializedValue(12);
   const auto& active_navigations = predictor_->active_navigations_;
   const auto& active_hints = predictor_->active_hints_;
 
@@ -207,7 +208,7 @@
 TEST_F(LoadingPredictorTest, TestMainFrameResponseClearsNavigations) {
   const std::string url = kUrl;
   const std::string redirected = kUrl2;
-  const int tab_id = 12;
+  const SessionID tab_id = SessionID::FromSerializedValue(12);
   const auto& active_navigations = predictor_->active_navigations_;
   const auto& active_hints = predictor_->active_hints_;
 
@@ -240,7 +241,7 @@
 
 TEST_F(LoadingPredictorTest, TestMainFrameRequestDoesntCancelExternalHint) {
   const GURL url = GURL(kUrl);
-  const int tab_id = 12;
+  const SessionID tab_id = SessionID::FromSerializedValue(12);
   const auto& active_navigations = predictor_->active_navigations_;
   auto& active_hints = predictor_->active_hints_;
 
diff --git a/chrome/browser/predictors/loading_stats_collector_unittest.cc b/chrome/browser/predictors/loading_stats_collector_unittest.cc
index 6bb6794a..4508b12 100644
--- a/chrome/browser/predictors/loading_stats_collector_unittest.cc
+++ b/chrome/browser/predictors/loading_stats_collector_unittest.cc
@@ -81,8 +81,9 @@
       .WillOnce(DoAll(SetArgPointee<1>(prediction), Return(true)));
 
   // Navigation simulation.
-  URLRequestSummary script = CreateURLRequestSummary(
-      1, navigation_url, script_url, content::RESOURCE_TYPE_SCRIPT);
+  URLRequestSummary script =
+      CreateURLRequestSummary(SessionID::FromSerializedValue(1), navigation_url,
+                              script_url, content::RESOURCE_TYPE_SCRIPT);
   PageRequestSummary summary =
       CreatePageRequestSummary(navigation_url, initial_url, {script});
 
@@ -113,10 +114,12 @@
 
   // Simulate a page load with 2 resources, one we know, one we don't, plus we
   // know the main frame origin.
-  URLRequestSummary script = CreateURLRequestSummary(
-      1, main_frame_url, gen(1), content::RESOURCE_TYPE_SCRIPT);
-  URLRequestSummary new_script = CreateURLRequestSummary(
-      1, main_frame_url, gen(100), content::RESOURCE_TYPE_SCRIPT);
+  URLRequestSummary script =
+      CreateURLRequestSummary(SessionID::FromSerializedValue(1), main_frame_url,
+                              gen(1), content::RESOURCE_TYPE_SCRIPT);
+  URLRequestSummary new_script =
+      CreateURLRequestSummary(SessionID::FromSerializedValue(1), main_frame_url,
+                              gen(100), content::RESOURCE_TYPE_SCRIPT);
   PageRequestSummary summary = CreatePageRequestSummary(
       main_frame_url, main_frame_url, {script, new_script});
 
@@ -158,6 +161,7 @@
 
 TEST_F(LoadingStatsCollectorTest, TestPreconnectHistograms) {
   const std::string main_frame_url("http://google.com/?query=cats");
+  const SessionID kTabId = SessionID::FromSerializedValue(1);
   auto gen = [](int index) {
     return base::StringPrintf("http://cdn%d.google.com/script.js", index);
   };
@@ -184,11 +188,11 @@
   {
     // Simulate a page load with 3 origins.
     URLRequestSummary script1 = CreateURLRequestSummary(
-        1, main_frame_url, gen(1), content::RESOURCE_TYPE_SCRIPT);
+        kTabId, main_frame_url, gen(1), content::RESOURCE_TYPE_SCRIPT);
     URLRequestSummary script2 = CreateURLRequestSummary(
-        1, main_frame_url, gen(2), content::RESOURCE_TYPE_SCRIPT);
+        kTabId, main_frame_url, gen(2), content::RESOURCE_TYPE_SCRIPT);
     URLRequestSummary script100 = CreateURLRequestSummary(
-        1, main_frame_url, gen(100), content::RESOURCE_TYPE_SCRIPT);
+        kTabId, main_frame_url, gen(100), content::RESOURCE_TYPE_SCRIPT);
     PageRequestSummary summary = CreatePageRequestSummary(
         main_frame_url, main_frame_url, {script1, script2, script100});
 
@@ -209,6 +213,7 @@
 // empty.
 TEST_F(LoadingStatsCollectorTest, TestPreconnectHistogramsEmpty) {
   const std::string main_frame_url = "http://google.com";
+  const SessionID kTabId = SessionID::FromSerializedValue(1);
   auto stats = std::make_unique<PreconnectStats>(GURL(main_frame_url));
   stats_collector_->RecordPreconnectStats(std::move(stats));
 
@@ -217,7 +222,7 @@
       .WillOnce(Return(false));
 
   URLRequestSummary script = CreateURLRequestSummary(
-      1, main_frame_url, "http://cdn.google.com/script.js",
+      kTabId, main_frame_url, "http://cdn.google.com/script.js",
       content::RESOURCE_TYPE_SCRIPT);
   PageRequestSummary summary =
       CreatePageRequestSummary(main_frame_url, main_frame_url, {script});
@@ -238,6 +243,7 @@
 // preresolve attempts only.
 TEST_F(LoadingStatsCollectorTest, TestPreconnectHistogramsPreresolvesOnly) {
   const std::string main_frame_url("http://google.com/?query=cats");
+  const SessionID kTabId = SessionID::FromSerializedValue(1);
   auto gen = [](int index) {
     return base::StringPrintf("http://cdn%d.google.com/script.js", index);
   };
@@ -264,11 +270,11 @@
   {
     // Simulate a page load with 3 origins.
     URLRequestSummary script1 = CreateURLRequestSummary(
-        1, main_frame_url, gen(1), content::RESOURCE_TYPE_SCRIPT);
+        kTabId, main_frame_url, gen(1), content::RESOURCE_TYPE_SCRIPT);
     URLRequestSummary script2 = CreateURLRequestSummary(
-        1, main_frame_url, gen(2), content::RESOURCE_TYPE_SCRIPT);
+        kTabId, main_frame_url, gen(2), content::RESOURCE_TYPE_SCRIPT);
     URLRequestSummary script100 = CreateURLRequestSummary(
-        1, main_frame_url, gen(100), content::RESOURCE_TYPE_SCRIPT);
+        kTabId, main_frame_url, gen(100), content::RESOURCE_TYPE_SCRIPT);
     PageRequestSummary summary = CreatePageRequestSummary(
         main_frame_url, main_frame_url, {script1, script2, script100});
 
diff --git a/chrome/browser/predictors/loading_test_util.cc b/chrome/browser/predictors/loading_test_util.cc
index 1c20740..057d5016 100644
--- a/chrome/browser/predictors/loading_test_util.cc
+++ b/chrome/browser/predictors/loading_test_util.cc
@@ -79,7 +79,7 @@
   return data;
 }
 
-NavigationID CreateNavigationID(SessionID::id_type tab_id,
+NavigationID CreateNavigationID(SessionID tab_id,
                                 const std::string& main_frame_url) {
   NavigationID navigation_id;
   navigation_id.tab_id = tab_id;
@@ -95,13 +95,14 @@
   GURL main_frame_gurl(main_frame_url);
   PageRequestSummary summary(main_frame_gurl);
   summary.initial_url = GURL(initial_url);
-  summary.UpdateOrAddToOrigins(CreateURLRequestSummary(1, main_frame_url));
+  summary.UpdateOrAddToOrigins(CreateURLRequestSummary(
+      SessionID::FromSerializedValue(1), main_frame_url));
   for (auto& request_summary : subresource_requests)
     summary.UpdateOrAddToOrigins(request_summary);
   return summary;
 }
 
-URLRequestSummary CreateURLRequestSummary(SessionID::id_type tab_id,
+URLRequestSummary CreateURLRequestSummary(SessionID tab_id,
                                           const std::string& main_frame_url,
                                           const std::string& request_url,
                                           content::ResourceType resource_type,
@@ -121,7 +122,7 @@
 }
 
 URLRequestSummary CreateRedirectRequestSummary(
-    SessionID::id_type session_id,
+    SessionID session_id,
     const std::string& main_frame_url,
     const std::string& redirect_url) {
   URLRequestSummary summary =
diff --git a/chrome/browser/predictors/loading_test_util.h b/chrome/browser/predictors/loading_test_util.h
index 62d4a903..ce5fab8 100644
--- a/chrome/browser/predictors/loading_test_util.h
+++ b/chrome/browser/predictors/loading_test_util.h
@@ -59,7 +59,7 @@
 OriginData CreateOriginData(const std::string& host,
                             uint64_t last_visit_time = 0);
 
-NavigationID CreateNavigationID(SessionID::id_type tab_id,
+NavigationID CreateNavigationID(SessionID tab_id,
                                 const std::string& main_frame_url);
 
 PageRequestSummary CreatePageRequestSummary(
@@ -68,7 +68,7 @@
     const std::vector<URLRequestSummary>& subresource_requests);
 
 URLRequestSummary CreateURLRequestSummary(
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     const std::string& main_frame_url,
     const std::string& request_url = std::string(),
     content::ResourceType resource_type = content::RESOURCE_TYPE_MAIN_FRAME,
@@ -76,7 +76,7 @@
     bool always_revalidate = false);
 
 URLRequestSummary CreateRedirectRequestSummary(
-    SessionID::id_type session_id,
+    SessionID session_id,
     const std::string& main_frame_url,
     const std::string& redirect_url);
 
diff --git a/chrome/browser/predictors/resource_prefetch_common.cc b/chrome/browser/predictors/resource_prefetch_common.cc
index 0dffc83..5f3020ac 100644
--- a/chrome/browser/predictors/resource_prefetch_common.cc
+++ b/chrome/browser/predictors/resource_prefetch_common.cc
@@ -18,7 +18,7 @@
 
 namespace predictors {
 
-NavigationID::NavigationID() : tab_id(-1) {}
+NavigationID::NavigationID() : tab_id(SessionID::InvalidValue()) {}
 
 NavigationID::NavigationID(const NavigationID& other)
     : tab_id(other.tab_id),
@@ -38,7 +38,7 @@
       creation_time(creation_time) {}
 
 bool NavigationID::is_valid() const {
-  return tab_id != -1 && !main_frame_url.is_empty();
+  return tab_id.is_valid() && !main_frame_url.is_empty();
 }
 
 bool NavigationID::operator<(const NavigationID& rhs) const {
diff --git a/chrome/browser/predictors/resource_prefetch_common.h b/chrome/browser/predictors/resource_prefetch_common.h
index 7d72939..8edd401b 100644
--- a/chrome/browser/predictors/resource_prefetch_common.h
+++ b/chrome/browser/predictors/resource_prefetch_common.h
@@ -33,7 +33,7 @@
   // Returns true iff the tab_id is valid and the Main frame URL is set.
   bool is_valid() const;
 
-  SessionID::id_type tab_id;
+  SessionID tab_id;
   GURL main_frame_url;
 
   // NOTE: Even though we store the creation time here, it is not used during
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
index bf8e16f..39a369b0 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_unittest.cc
@@ -34,10 +34,13 @@
 using testing::UnorderedElementsAre;
 
 namespace predictors {
+namespace {
 
 using RedirectDataMap = std::map<std::string, RedirectData>;
 using OriginDataMap = std::map<std::string, OriginData>;
 
+constexpr SessionID kTabId = SessionID::FromSerializedValue(1);
+
 template <typename T>
 class FakeGlowplugKeyValueTable : public GlowplugKeyValueTable<T> {
  public:
@@ -100,6 +103,8 @@
   MOCK_METHOD1(OnNavigationLearned, void(const PageRequestSummary& summary));
 };
 
+}  // namespace
+
 class ResourcePrefetchPredictorTest : public testing::Test {
  public:
   ResourcePrefetchPredictorTest();
@@ -248,39 +253,39 @@
 // resources and also for number of resources saved.
 TEST_F(ResourcePrefetchPredictorTest, NavigationUrlNotInDB) {
   URLRequestSummary main_frame =
-      CreateURLRequestSummary(1, "http://www.google.com");
+      CreateURLRequestSummary(kTabId, "http://www.google.com");
 
   std::vector<URLRequestSummary> resources;
   resources.push_back(CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/style1.css",
+      kTabId, "http://www.google.com", "http://google.com/style1.css",
       content::RESOURCE_TYPE_STYLESHEET));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script1.js",
                                               content::RESOURCE_TYPE_SCRIPT));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script2.js",
                                               content::RESOURCE_TYPE_SCRIPT));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script1.js",
                                               content::RESOURCE_TYPE_SCRIPT));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/image1.png",
                                               content::RESOURCE_TYPE_IMAGE));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/image2.png",
                                               content::RESOURCE_TYPE_IMAGE));
   resources.push_back(CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/style2.css",
+      kTabId, "http://www.google.com", "http://google.com/style2.css",
       content::RESOURCE_TYPE_STYLESHEET));
 
   auto no_store =
-      CreateURLRequestSummary(1, "http://www.google.com",
+      CreateURLRequestSummary(kTabId, "http://www.google.com",
                               "http://static.google.com/style2-no-store.css",
                               content::RESOURCE_TYPE_STYLESHEET);
   no_store.is_no_store = true;
 
   auto redirected = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://reader.google.com/style.css",
+      kTabId, "http://www.google.com", "http://reader.google.com/style.css",
       content::RESOURCE_TYPE_STYLESHEET);
   redirected.redirect_url = GURL("http://dev.null.google.com/style.css");
 
@@ -330,33 +335,33 @@
   InitializePredictor();
 
   URLRequestSummary main_frame = CreateURLRequestSummary(
-      1, "http://www.google.com", "http://www.google.com",
+      kTabId, "http://www.google.com", "http://www.google.com",
       content::RESOURCE_TYPE_MAIN_FRAME);
 
   std::vector<URLRequestSummary> resources;
   resources.push_back(CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/style1.css",
+      kTabId, "http://www.google.com", "http://google.com/style1.css",
       content::RESOURCE_TYPE_STYLESHEET));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script1.js",
                                               content::RESOURCE_TYPE_SCRIPT));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script2.js",
                                               content::RESOURCE_TYPE_SCRIPT));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/script1.js",
                                               content::RESOURCE_TYPE_SCRIPT));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/image1.png",
                                               content::RESOURCE_TYPE_IMAGE));
-  resources.push_back(CreateURLRequestSummary(1, "http://www.google.com",
+  resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
                                               "http://google.com/image2.png",
                                               content::RESOURCE_TYPE_IMAGE));
   resources.push_back(CreateURLRequestSummary(
-      1, "http://www.google.com", "http://google.com/style2.css",
+      kTabId, "http://www.google.com", "http://google.com/style2.css",
       content::RESOURCE_TYPE_STYLESHEET));
   auto no_store =
-      CreateURLRequestSummary(1, "http://www.google.com",
+      CreateURLRequestSummary(kTabId, "http://www.google.com",
                               "http://static.google.com/style2-no-store.css",
                               content::RESOURCE_TYPE_STYLESHEET);
   no_store.is_no_store = true;
@@ -397,15 +402,15 @@
   ResetPredictor();
   InitializePredictor();
 
-  URLRequestSummary main_frame =
-      CreateURLRequestSummary(1, "http://www.nike.com", "http://www.nike.com",
-                              content::RESOURCE_TYPE_MAIN_FRAME);
+  URLRequestSummary main_frame = CreateURLRequestSummary(
+      kTabId, "http://www.nike.com", "http://www.nike.com",
+      content::RESOURCE_TYPE_MAIN_FRAME);
 
   URLRequestSummary resource1 = CreateURLRequestSummary(
-      1, "http://www.nike.com", "http://nike.com/style1.css",
+      kTabId, "http://www.nike.com", "http://nike.com/style1.css",
       content::RESOURCE_TYPE_STYLESHEET);
   URLRequestSummary resource2 = CreateURLRequestSummary(
-      1, "http://www.nike.com", "http://nike.com/image2.png",
+      kTabId, "http://www.nike.com", "http://nike.com/image2.png",
       content::RESOURCE_TYPE_IMAGE);
 
   auto page_summary = CreatePageRequestSummary(
@@ -439,7 +444,7 @@
 TEST_F(ResourcePrefetchPredictorTest,
        NavigationManyResourcesWithDifferentOrigins) {
   URLRequestSummary main_frame =
-      CreateURLRequestSummary(1, "http://www.google.com");
+      CreateURLRequestSummary(kTabId, "http://www.google.com");
 
   auto gen = [](int i) {
     return base::StringPrintf("http://cdn%d.google.com/script.js", i);
@@ -447,8 +452,9 @@
   std::vector<URLRequestSummary> resources;
   const int num_resources = predictor_->config_.max_origins_per_entry + 10;
   for (int i = 1; i <= num_resources; ++i) {
-    resources.push_back(CreateURLRequestSummary(
-        1, "http://www.google.com", gen(i), content::RESOURCE_TYPE_SCRIPT));
+    resources.push_back(CreateURLRequestSummary(kTabId, "http://www.google.com",
+                                                gen(i),
+                                                content::RESOURCE_TYPE_SCRIPT));
   }
 
   auto page_summary = CreatePageRequestSummary(
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index 2803c562..ef60fd9 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -17,6 +17,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -301,6 +302,12 @@
 // Shows notifications which correspond to PersistentPrefStore's reading errors.
 void HandleReadError(const base::FilePath& pref_filename,
                      PersistentPrefStore::PrefReadError error) {
+  // The error callback is always invoked back on the main thread (which is
+  // BrowserThread::UI unless called during early initialization before the main
+  // thread is promoted to BrowserThread::UI).
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+         !BrowserThread::IsThreadInitialized(BrowserThread::UI));
+
   // Sample the histogram also for the successful case in order to get a
   // baseline on the success rate in addition to the error distribution.
   UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error,
@@ -319,18 +326,14 @@
     }
 
     if (message_id) {
-      auto show_profile_error_dialog = base::BindOnce(
-          &ShowProfileErrorDialog, ProfileErrorType::PREFERENCES, message_id,
-          sql::GetCorruptFileDiagnosticsInfo(pref_filename));
-      if (BrowserThread::IsThreadInitialized(BrowserThread::UI)) {
-        BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                                std::move(show_profile_error_dialog));
-      } else {
-        // In early startup (e.g. on error loading Local State before most
-        // browser pieces are up), the only option is to show the dialog
-        // synchronously.
-        std::move(show_profile_error_dialog).Run();
-      }
+      // Note: ThreadTaskRunnerHandle() is usually BrowserThread::UI but during
+      // early startup it can be ChromeBrowserMainParts::DeferringTaskRunner
+      // which will forward to BrowserThread::UI when it's initialized.
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          base::BindOnce(&ShowProfileErrorDialog, ProfileErrorType::PREFERENCES,
+                         message_id,
+                         sql::GetCorruptFileDiagnosticsInfo(pref_filename)));
     }
 #else
     // On ChromeOS error screen with message about broken local state
@@ -397,7 +400,8 @@
   factory->set_command_line_prefs(
       base::MakeRefCounted<ChromeCommandLinePrefStore>(
           base::CommandLine::ForCurrentProcess()));
-  factory->set_read_error_callback(base::Bind(&HandleReadError, pref_filename));
+  factory->set_read_error_callback(
+      base::BindRepeating(&HandleReadError, pref_filename));
   factory->set_user_prefs(std::move(user_pref_store));
   factory->SetPrefModelAssociatorClient(
       ChromePrefModelAssociatorClient::GetInstance());
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index 0bed292..685ddba2 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -260,11 +260,6 @@
   return sorted_lifecycle_units;
 }
 
-bool TabManager::IsTabDiscarded(content::WebContents* contents) const {
-  auto* lifecycle_unit = TabLifecycleUnitExternal::FromWebContents(contents);
-  return lifecycle_unit && lifecycle_unit->IsDiscarded();
-}
-
 void TabManager::DiscardTab(DiscardReason reason) {
   if (reason == DiscardReason::kUrgent)
     stats_collector_->RecordWillDiscardUrgently(GetNumAliveTabs());
@@ -329,11 +324,6 @@
   TabLifecycleUnitExternal::RemoveTabLifecycleObserver(observer);
 }
 
-bool TabManager::IsTabAutoDiscardable(content::WebContents* contents) const {
-  auto* lifecycle_unit = TabLifecycleUnitExternal::FromWebContents(contents);
-  return !lifecycle_unit || lifecycle_unit->IsAutoDiscardable();
-}
-
 void TabManager::SetTabAutoDiscardableState(int32_t tab_id, bool state) {
   for (LifecycleUnit* lifecycle_unit : lifecycle_units_) {
     if (lifecycle_unit->GetID() == tab_id) {
@@ -477,7 +467,7 @@
 bool TabManager::ShouldPurgeNow(content::WebContents* content) const {
   if (GetWebContentsData(content)->is_purged())
     return false;
-  if (IsTabDiscarded(content))
+  if (TabLifecycleUnitExternal::FromWebContents(content)->IsDiscarded())
     return false;
 
   base::TimeDelta time_passed =
@@ -880,7 +870,7 @@
     TabStripModel* tab_strip_model = browser->tab_strip_model();
     for (int index = 0; index < tab_strip_model->count(); ++index) {
       content::WebContents* contents = tab_strip_model->GetWebContentsAt(index);
-      if (!IsTabDiscarded(contents))
+      if (!TabLifecycleUnitExternal::FromWebContents(contents)->IsDiscarded())
         ++tab_count;
     }
   }
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index 594b2ea..fa423ab 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -98,9 +98,6 @@
   // vector after a LifecycleUnit has been destroyed.
   LifecycleUnitVector GetSortedLifecycleUnits();
 
-  // Returns true if |contents| is currently discarded.
-  bool IsTabDiscarded(content::WebContents* contents) const;
-
   // Discards a tab to free the memory occupied by its renderer. The tab still
   // exists in the tab-strip; clicking on it will reload it. If the |reason| is
   // urgent, an aggressive fast-kill will be attempted if the sudden termination
@@ -138,13 +135,6 @@
   void AddObserver(TabLifecycleObserver* observer);
   void RemoveObserver(TabLifecycleObserver* observer);
 
-  // Returns the auto-discardable state of the tab. When true, the tab is
-  // eligible to be automatically discarded when critical memory pressure hits,
-  // otherwise the tab is ignored and will never be automatically discarded.
-  // Note that this property doesn't block the discarding of the tab via other
-  // methods (about:discards for instance).
-  bool IsTabAutoDiscardable(content::WebContents* contents) const;
-
   // Sets/clears the auto-discardable state of the tab.
   void SetTabAutoDiscardableState(int32_t tab_id, bool state);
   void SetTabAutoDiscardableState(content::WebContents* contents, bool state);
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index 960fd945..a6da556 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -110,6 +110,10 @@
              ->entry->GetURL() == expected_url;
 }
 
+bool IsTabDiscarded(content::WebContents* web_contents) {
+  return TabLifecycleUnitExternal::FromWebContents(web_contents)->IsDiscarded();
+}
+
 }  // namespace
 
 IN_PROC_BROWSER_TEST_F(TabManagerTest, TabManagerBasics) {
@@ -180,32 +184,32 @@
   // and was not selected.
   EXPECT_TRUE(tab_manager->DiscardTabImpl(DiscardReason::kProactive));
   EXPECT_EQ(3, tsm->count());
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(2)));
 
   // Run discard again, make sure it kills the second tab.
   EXPECT_TRUE(tab_manager->DiscardTabImpl(DiscardReason::kProactive));
   EXPECT_EQ(3, tsm->count());
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(2)));
 
   // Kill the third tab. It should not kill the last tab, since it is active
   // tab.
   EXPECT_FALSE(tab_manager->DiscardTabImpl(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(2)));
 
   // Kill the third tab after making second tab active.
   tsm->ActivateTabAt(1, true);
 
   EXPECT_EQ(1, tsm->active_index());
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
   FastForwardAfterDiscardProtectionTime();
   tab_manager->DiscardTabImpl(DiscardReason::kProactive);
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(2)));
 
   // Force creation of the FindBarController.
   browser()->GetFindBarController();
@@ -221,9 +225,9 @@
   EXPECT_EQ(browser()->GetFindBarController()->web_contents(),
             tsm->GetActiveWebContents());
   EXPECT_EQ(0, tsm->active_index());
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(2)));
 
   // Select the third tab. It should reload.
   WindowedNotificationObserver reload2(
@@ -232,9 +236,9 @@
   chrome::SelectNumberedTab(browser(), 2);
   reload2.Wait();
   EXPECT_EQ(2, tsm->active_index());
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(2)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(2)));
 
   // Navigate the third tab back twice.  We used to crash here due to
   // crbug.com/121373.
@@ -262,7 +266,6 @@
 // Test that the MemoryPressureListener event is properly triggering a tab
 // discard upon |MEMORY_PRESSURE_LEVEL_CRITICAL| event.
 IN_PROC_BROWSER_TEST_F(TabManagerTest, OomPressureListener) {
-  TabManager* tab_manager = g_browser_process->GetTabManager();
   TabStripModel* tsm = browser()->tab_strip_model();
 
   // Get two tabs open.
@@ -287,14 +290,14 @@
   ASSERT_EQ(tsm->count(), 2);
   FastForwardAfterDiscardProtectionTime();
 
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
 
   // Nothing should happen with a moderate memory pressure event.
   base::MemoryPressureListener::NotifyMemoryPressure(
       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
 
   // A critical memory pressure event should discard a tab.
   base::MemoryPressureListener::NotifyMemoryPressure(
@@ -308,11 +311,11 @@
     base::PlatformThread::Sleep(
         base::TimeDelta::FromMilliseconds(kIntervalTimeInMS));
     base::RunLoop().RunUntilIdle();
-    if (tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)))
+    if (IsTabDiscarded(tsm->GetWebContentsAt(0)))
       break;
   }
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tsm->GetWebContentsAt(1)));
 }
 
 #endif
@@ -536,7 +539,7 @@
 
   // Now it should be able to discard the tab.
   EXPECT_TRUE(tab_manager->DiscardTabImpl(DiscardReason::kProactive));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
+  EXPECT_TRUE(IsTabDiscarded(tsm->GetWebContentsAt(0)));
 }
 
 IN_PROC_BROWSER_TEST_F(TabManagerTest, PurgeBackgroundRenderer) {
@@ -1090,34 +1093,28 @@
 // On ChromeOS, active tabs are discarded if their window is non-visible. On
 // other platforms, they are never discarded.
 #if defined(OS_CHROMEOS)
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(
-      browser1->tab_strip_model()->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(
-      browser2->tab_strip_model()->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(
-      browser3->tab_strip_model()->GetWebContentsAt(0)));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(
-      browser4->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_TRUE(IsTabDiscarded(browser1->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_FALSE(
+      IsTabDiscarded(browser2->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_FALSE(
+      IsTabDiscarded(browser3->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_TRUE(IsTabDiscarded(browser4->tab_strip_model()->GetWebContentsAt(0)));
 #else
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(
-      browser1->tab_strip_model()->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(
-      browser2->tab_strip_model()->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(
-      browser3->tab_strip_model()->GetWebContentsAt(0)));
-  EXPECT_FALSE(tab_manager->IsTabDiscarded(
-      browser4->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_FALSE(
+      IsTabDiscarded(browser1->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_FALSE(
+      IsTabDiscarded(browser2->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_FALSE(
+      IsTabDiscarded(browser3->tab_strip_model()->GetWebContentsAt(0)));
+  EXPECT_FALSE(
+      IsTabDiscarded(browser4->tab_strip_model()->GetWebContentsAt(0)));
 #endif
 
   // Non-active tabs can be discarded on all platforms.
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(
-      browser1->tab_strip_model()->GetWebContentsAt(1)));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(
-      browser2->tab_strip_model()->GetWebContentsAt(1)));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(
-      browser3->tab_strip_model()->GetWebContentsAt(1)));
-  EXPECT_TRUE(tab_manager->IsTabDiscarded(
-      browser4->tab_strip_model()->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(browser1->tab_strip_model()->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(browser2->tab_strip_model()->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(browser3->tab_strip_model()->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(browser4->tab_strip_model()->GetWebContentsAt(1)));
 }
 
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index fcecd96..e4c17164 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/background_tab_navigation_throttle.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/browser/resource_coordinator/tab_manager_resource_coordinator_signal_observer.h"
 #include "chrome/browser/resource_coordinator/tab_manager_stats_collector.h"
@@ -130,6 +131,10 @@
   kInternalPage,
 };
 
+bool IsTabDiscarded(content::WebContents* web_contents) {
+  return TabLifecycleUnitExternal::FromWebContents(web_contents)->IsDiscarded();
+}
+
 }  // namespace
 
 class TabManagerTest : public ChromeRenderViewHostTestHarness {
@@ -434,20 +439,20 @@
     tab_manager_->DiscardTab(DiscardReason::kProactive);
 
   // Active tab in a visible window should not be discarded.
-  EXPECT_FALSE(tab_manager_->IsTabDiscarded(tab_strip1->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tab_strip1->GetWebContentsAt(0)));
 
   // Non-active tabs should be discarded.
-  EXPECT_TRUE(tab_manager_->IsTabDiscarded(tab_strip1->GetWebContentsAt(1)));
-  EXPECT_TRUE(tab_manager_->IsTabDiscarded(tab_strip2->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(tab_strip1->GetWebContentsAt(1)));
+  EXPECT_TRUE(IsTabDiscarded(tab_strip2->GetWebContentsAt(1)));
 
 #if defined(OS_CHROMEOS)
   // On ChromeOS, a non-visible tab should be discarded even if it's active in
   // its tab strip.
-  EXPECT_TRUE(tab_manager_->IsTabDiscarded(tab_strip2->GetWebContentsAt(0)));
+  EXPECT_TRUE(IsTabDiscarded(tab_strip2->GetWebContentsAt(0)));
 #else
   // On other platforms, an active tab is never discarded, even if it's not
   // visible.
-  EXPECT_FALSE(tab_manager_->IsTabDiscarded(tab_strip2->GetWebContentsAt(0)));
+  EXPECT_FALSE(IsTabDiscarded(tab_strip2->GetWebContentsAt(0)));
 #endif  // defined(OS_CHROMEOS)
 
   // Tabs with a committed URL must be closed explicitly to avoid DCHECK errors.
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger.cc b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
index c7ab1dc5..9e990ec 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
@@ -15,7 +15,7 @@
 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -236,10 +236,6 @@
   // audio indicator in the tab strip.
   entry.SetWasRecentlyAudible(web_contents->WasRecentlyAudible());
 
-  resource_coordinator::TabManager* tab_manager =
-      g_browser_process->GetTabManager();
-  DCHECK(tab_manager);
-
   PopulatePageTransitionMetrics(&entry, tab_metrics.page_transition);
   entry
       .SetHasBeforeUnloadHandler(
@@ -247,9 +243,10 @@
               blink::kBeforeUnloadHandler))
       .SetHasFormEntry(
           web_contents->GetPageImportanceSignals().had_form_interaction)
-      // TODO(michaelpg): This dependency should be reversed during the
-      // resource_coordinator refactor: https://crbug.com/775644.
-      .SetIsExtensionProtected(!tab_manager->IsTabAutoDiscardable(web_contents))
+      .SetIsExtensionProtected(
+          !resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
+               web_contents)
+               ->IsAutoDiscardable())
       .SetIsPinned(tab_strip_model->IsTabPinned(index))
       .SetNavigationEntryCount(web_contents->GetController().GetEntryCount())
       .SetSequenceId(++sequence_id_)
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service.cc b/chrome/browser/safe_browsing/certificate_reporting_service.cc
index 9b4dd6b2..9bb9997 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service.cc
@@ -14,7 +14,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "content/public/browser/browser_thread.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
 
@@ -44,11 +44,6 @@
       CertificateReportingService::ReportOutcome::EVENT_COUNT);
 }
 
-void CleanupOnIOThread(
-    std::unique_ptr<CertificateReportingService::Reporter> reporter) {
-  reporter.reset();
-}
-
 }  // namespace
 
 const char CertificateReportingService::kReportEventHistogram[] =
@@ -110,12 +105,10 @@
 
 void CertificateReportingService::Reporter::Send(
     const std::string& serialized_report) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   SendInternal(Report(current_report_id_++, clock_->Now(), serialized_report));
 }
 
 void CertificateReportingService::Reporter::SendPending() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   if (!retries_enabled_) {
     return;
   }
@@ -153,25 +146,27 @@
   return retry_list_.get();
 }
 
+void CertificateReportingService::Reporter::
+    SetClosureWhenNoInflightReportsForTesting(const base::Closure& closure) {
+  no_in_flight_reports_ = closure;
+}
+
 void CertificateReportingService::Reporter::SendInternal(
     const CertificateReportingService::Report& report) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   inflight_reports_.insert(std::make_pair(report.report_id, report));
   RecordUMAEvent(ReportOutcome::SUBMITTED);
   error_reporter_->SendExtendedReportingReport(
       report.serialized_report,
-      base::Bind(&CertificateReportingService::Reporter::SuccessCallback,
-                 weak_factory_.GetWeakPtr(), report.report_id),
-      base::Bind(&CertificateReportingService::Reporter::ErrorCallback,
-                 weak_factory_.GetWeakPtr(), report.report_id));
+      base::BindOnce(&CertificateReportingService::Reporter::SuccessCallback,
+                     weak_factory_.GetWeakPtr(), report.report_id),
+      base::BindOnce(&CertificateReportingService::Reporter::ErrorCallback,
+                     weak_factory_.GetWeakPtr(), report.report_id));
 }
 
 void CertificateReportingService::Reporter::ErrorCallback(
     int report_id,
-    const GURL& url,
     int net_error,
     int http_response_code) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   RecordUMAOnFailure(net_error);
   RecordUMAEvent(ReportOutcome::FAILED);
   if (retries_enabled_) {
@@ -180,17 +175,20 @@
     retry_list_->Add(it->second);
   }
   CHECK_GT(inflight_reports_.erase(report_id), 0u);
+  if (inflight_reports_.empty() && no_in_flight_reports_)
+    no_in_flight_reports_.Run();
 }
 
 void CertificateReportingService::Reporter::SuccessCallback(int report_id) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   RecordUMAEvent(ReportOutcome::SUCCESSFUL);
   CHECK_GT(inflight_reports_.erase(report_id), 0u);
+  if (inflight_reports_.empty() && no_in_flight_reports_)
+    no_in_flight_reports_.Run();
 }
 
 CertificateReportingService::CertificateReportingService(
     safe_browsing::SafeBrowsingService* safe_browsing_service,
-    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     Profile* profile,
     uint8_t server_public_key[/* 32 */],
     uint32_t server_public_key_version,
@@ -199,7 +197,7 @@
     base::Clock* clock,
     const base::Callback<void()>& reset_callback)
     : pref_service_(*profile->GetPrefs()),
-      url_request_context_(nullptr),
+      url_loader_factory_(url_loader_factory),
       max_queued_report_count_(max_queued_report_count),
       max_report_age_(max_report_age),
       clock_(clock),
@@ -208,10 +206,6 @@
       server_public_key_version_(server_public_key_version) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(clock_);
-  // Subscribe to SafeBrowsing shutdown notifications.
-  safe_browsing_service_shutdown_subscription_ =
-      safe_browsing_service->RegisterShutdownCallback(base::Bind(
-          &CertificateReportingService::Shutdown, base::Unretained(this)));
 
   // Subscribe to SafeBrowsing preference change notifications.
   safe_browsing_state_subscription_ =
@@ -219,13 +213,8 @@
           base::Bind(&CertificateReportingService::OnPreferenceChanged,
                      base::Unretained(this)));
 
-  content::BrowserThread::PostTaskAndReply(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CertificateReportingService::InitializeOnIOThread,
-                     base::Unretained(this), true, url_request_context_getter,
-                     max_queued_report_count_, max_report_age_, clock_,
-                     server_public_key_, server_public_key_version_),
-      reset_callback_);
+  Reset(true);
+  reset_callback_.Run();
 }
 
 CertificateReportingService::~CertificateReportingService() {
@@ -233,65 +222,25 @@
 }
 
 void CertificateReportingService::Shutdown() {
-  // Shutdown will be called twice: Once after SafeBrowsing shuts down, and once
-  // when all KeyedServices shut down. All calls after the first one are no-op.
-  url_request_context_ = nullptr;
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CleanupOnIOThread, std::move(reporter_)));
+  reporter_.reset();
 }
 
 void CertificateReportingService::Send(const std::string& serialized_report) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!reporter_) {
-    return;
-  }
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CertificateReportingService::Reporter::Send,
-                     base::Unretained(reporter_.get()), serialized_report));
+  if (reporter_)
+    reporter_->Send(serialized_report);
 }
 
 void CertificateReportingService::SendPending() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!reporter_) {
-    return;
-  }
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CertificateReportingService::Reporter::SendPending,
-                     base::Unretained(reporter_.get())));
-}
-
-void CertificateReportingService::InitializeOnIOThread(
-    bool enabled,
-    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
-    size_t max_queued_report_count,
-    base::TimeDelta max_report_age,
-    base::Clock* clock,
-    uint8_t* server_public_key,
-    uint32_t server_public_key_version) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  DCHECK(!url_request_context_);
-  url_request_context_ = url_request_context_getter->GetURLRequestContext();
-  ResetOnIOThread(enabled, url_request_context_, max_queued_report_count,
-                  max_report_age, clock, server_public_key,
-                  server_public_key_version);
+  if (reporter_)
+    reporter_->SendPending();
 }
 
 void CertificateReportingService::SetEnabled(bool enabled) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  // Don't reset if the service is already shut down.
-  if (!url_request_context_)
-    return;
-
-  content::BrowserThread::PostTaskAndReply(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CertificateReportingService::ResetOnIOThread,
-                     base::Unretained(this), enabled, url_request_context_,
-                     max_queued_report_count_, max_report_age_, clock_,
-                     server_public_key_, server_public_key_version_),
-      reset_callback_);
+  Reset(enabled);
+  reset_callback_.Run();
 }
 
 CertificateReportingService::Reporter*
@@ -304,57 +253,26 @@
   return GURL(kExtendedReportingUploadUrl);
 }
 
-void CertificateReportingService::ResetOnIOThread(
-    bool enabled,
-    net::URLRequestContext* url_request_context,
-    size_t max_queued_report_count,
-    base::TimeDelta max_report_age,
-    base::Clock* clock,
-    uint8_t* const server_public_key,
-    uint32_t server_public_key_version) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  // url_request_context_ is null during shutdown.
-  if (!enabled || !url_request_context) {
+void CertificateReportingService::Reset(bool enabled) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!enabled) {
     reporter_.reset();
     return;
   }
   std::unique_ptr<CertificateErrorReporter> error_reporter;
-  if (server_public_key) {
-    // Only used in tests.
-    net::NetworkTrafficAnnotationTag traffic_annotation =
-        net::DefineNetworkTrafficAnnotation(
-            "certificate_reporting_service_test", R"(
-        semantics {
-          sender: "Certificate Reporting Service Test"
-          description:
-            "This request is used for testing certificate reporting service."
-          trigger: "Upon request from testing API."
-          data:
-            "No user data."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "This feature is only used for testing."
-          policy_exception_justification:
-            "This feature is only used for testing."
-        }
-    )");
-    std::unique_ptr<net::ReportSender> report_sender(
-        new net::ReportSender(url_request_context, traffic_annotation));
+  if (server_public_key_) {
     error_reporter.reset(new CertificateErrorReporter(
-        GURL(kExtendedReportingUploadUrl), server_public_key,
-        server_public_key_version, std::move(report_sender)));
+        url_loader_factory_, GURL(kExtendedReportingUploadUrl),
+        server_public_key_, server_public_key_version_));
   } else {
     error_reporter.reset(new CertificateErrorReporter(
-        url_request_context, GURL(kExtendedReportingUploadUrl)));
+        url_loader_factory_, GURL(kExtendedReportingUploadUrl)));
   }
   reporter_.reset(
       new Reporter(std::move(error_reporter),
                    std::unique_ptr<BoundedReportList>(
-                       new BoundedReportList(max_queued_report_count)),
-                   clock, max_report_age, true /* retries_enabled */));
+                       new BoundedReportList(max_queued_report_count_)),
+                   clock_, max_report_age_, true /* retries_enabled */));
 }
 
 void CertificateReportingService::OnPreferenceChanged() {
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service.h b/chrome/browser/safe_browsing/certificate_reporting_service.h
index c2dc0494..dba0255 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service.h
+++ b/chrome/browser/safe_browsing/certificate_reporting_service.h
@@ -17,7 +17,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/ssl/certificate_error_reporter.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 class PrefService;
 class Profile;
@@ -26,8 +26,8 @@
 class Clock;
 }
 
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
 }
 
 namespace safe_browsing {
@@ -41,16 +41,11 @@
 //
 // Lifetime and dependencies:
 //
-// CertificateReportingService uses the url request context from SafeBrowsing
+// CertificateReportingService uses the URLLoaderFactory from SafeBrowsing
 // service. SafeBrowsingService is created before CertificateReportingService,
-// but is also shut down before any KeyedService is shut down. This means that
-// CertificateReportingService cannot depend on SafeBrowsing's url request being
-// available at all times, and it should know when SafeBrowsing shuts down. It
-// does this by subscribing to SafeBrowsingService shut downs when it's
-// created. When SafeBrowsingService shuts down, CertificateReportingService
-// also shuts down.
+// but is also shut down before any KeyedService is shut down.
 //
-// This class also observes SafeBrowsing preference changes to enable/disable
+// This class observes SafeBrowsing preference changes to enable/disable
 // reporting. It does this by subscribing to changes in SafeBrowsing and
 // extended reporting preferences.
 class CertificateReportingService : public KeyedService {
@@ -142,6 +137,9 @@
     // Getter and setters for testing:
     size_t inflight_report_count_for_testing() const;
     BoundedReportList* GetQueueForTesting() const;
+    // Sets a closure that is called when there are no more inflight reports.
+    void SetClosureWhenNoInflightReportsForTesting(
+        const base::Closure& closure);
 
    private:
     void SendInternal(const Report& report);
@@ -149,7 +147,6 @@
     // non-HTTP 200 response code. See
     // TransportSecurityState::ReportSenderInterface for parameters.
     void ErrorCallback(int report_id,
-                       const GURL& url,
                        int net_error,
                        int http_response_code);
     // Called when a report upload is successful.
@@ -167,6 +164,8 @@
 
     std::map<int, Report> inflight_reports_;
 
+    base::Closure no_in_flight_reports_;
+
     base::WeakPtrFactory<Reporter> weak_factory_;
 
     DISALLOW_COPY_AND_ASSIGN(Reporter);
@@ -177,7 +176,7 @@
 
   CertificateReportingService(
       safe_browsing::SafeBrowsingService* safe_browsing_service,
-      scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       Profile* profile,
       uint8_t server_public_key[/* 32 */],
       uint32_t server_public_key_version,
@@ -208,40 +207,18 @@
   static GURL GetReportingURLForTesting();
 
  private:
-  void InitializeOnIOThread(
-      bool enabled,
-      scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
-      size_t max_queued_report_count,
-      base::TimeDelta max_report_age,
-      base::Clock* const clock,
-      uint8_t* server_public_key,
-      uint32_t server_public_key_version);
-
-  // Resets the reporter on the IO thread. Changes in SafeBrowsing or extended
-  // reporting enabled states cause the reporter to be reset.
-  // If |enabled| is false or |url_request_context_getter| is null, report is
-  // set to null, effectively cancelling all in flight uploads and clearing the
+  // Resets the reporter. Changes in SafeBrowsing or extended reporting enabled
+  // states cause the reporter to be reset. If |enabled| is false, report is set
+  // to null, effectively cancelling all in flight uploads and clearing the
   // pending reports queue.
-  void ResetOnIOThread(bool enabled,
-                       net::URLRequestContext* url_request_context,
-                       size_t max_queued_report_count,
-                       base::TimeDelta max_report_age,
-                       base::Clock* const clock,
-                       uint8_t* server_public_key,
-                       uint32_t server_public_key_version);
+  void Reset(bool enabled);
 
   void OnPreferenceChanged();
 
   const PrefService& pref_service_;
-  net::URLRequestContext* url_request_context_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   std::unique_ptr<Reporter> reporter_;
 
-  // Subscription for url request context shutdowns. When this subscription is
-  // notified, it means SafeBrowsingService is shutting down, and this service
-  // must also shut down.
-  std::unique_ptr<base::CallbackList<void(void)>::Subscription>
-      safe_browsing_service_shutdown_subscription_;
-
   // Subscription for state changes. When this subscription is notified, it
   // means SafeBrowsingService is enabled/disabled or one of the preferences
   // related to it is changed.
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
index f6755fc..86198277 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -82,7 +82,8 @@
     https_server_.ServeFilesFromSourceDirectory("chrome/test/data");
     ASSERT_TRUE(https_server_.Start());
 
-    test_helper()->SetUpInterceptor();
+    test_helper_ =
+        base::MakeRefCounted<CertificateReportingServiceTestHelper>();
 
     CertificateReportingServiceFactory::GetInstance()
         ->SetReportEncryptionParamsForTesting(
@@ -92,6 +93,8 @@
         ->SetServiceResetCallbackForTesting(
             base::Bind(&CertificateReportingServiceObserver::OnServiceReset,
                        base::Unretained(&service_observer_)));
+    CertificateReportingServiceFactory::GetInstance()
+        ->SetURLLoaderFactoryForTesting(test_helper_);
 
     event_histogram_tester_.reset(new EventHistogramTester());
     InProcessBrowserTest::SetUpOnMainThread();
@@ -126,7 +129,23 @@
     }
   }
 
-  CertificateReportingServiceTestHelper* test_helper() { return &test_helper_; }
+  CertificateReportingServiceTestHelper* test_helper() {
+    return test_helper_.get();
+  }
+
+  void WaitForNoReports() {
+    if (!service()->GetReporterForTesting() ||
+        !service()
+             ->GetReporterForTesting()
+             ->inflight_report_count_for_testing())
+      return;
+
+    base::RunLoop run_loop;
+    service()
+        ->GetReporterForTesting()
+        ->SetClosureWhenNoInflightReportsForTesting(run_loop.QuitClosure());
+    run_loop.Run();
+  }
 
  protected:
   CertificateReportingServiceFactory* factory() {
@@ -157,7 +176,10 @@
       content::WaitForInterstitialDetach(contents);
   }
 
-  void SendPendingReports() { service()->SendPending(); }
+  void SendPendingReports() {
+    WaitForNoReports();
+    service()->SendPending();
+  }
 
   // Changes opt-in status and waits for the cert reporting service to reset.
   // Can only be used after the service is initialized. When changing the
@@ -216,7 +238,7 @@
 
   int num_expected_failed_report_ = -1;
 
-  CertificateReportingServiceTestHelper test_helper_;
+  scoped_refptr<CertificateReportingServiceTestHelper> test_helper_;
 
   CertificateReportingServiceObserver service_observer_;
 
@@ -434,6 +456,8 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Successful({{"report1", RetryStatus::RETRIED}}));
 
+  WaitForNoReports();
+
   // report0 was submitted once, failed once, then cleared.
   // report1 was submitted twice, failed once, succeeded once.
   event_histogram_tester()->SetExpectedValues(
@@ -512,6 +536,8 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Successful({{"report3", RetryStatus::NOT_RETRIED}}));
 
+  WaitForNoReports();
+
   // report0 was submitted once, failed once, dropped once.
   // report1 was submitted twice, failed twice, dropped once.
   // report2 was submitted twice, failed twice, dropped once.
@@ -593,6 +619,8 @@
   test_helper()->WaitForRequestsDestroyed(ReportExpectation::Successful(
       {{"report2", RetryStatus::RETRIED}, {"report3", RetryStatus::RETRIED}}));
 
+  WaitForNoReports();
+
   // report0 was submitted once, failed once, dropped once.
   // report1 was submitted twice, failed twice, dropped once.
   // report2 was submitted thrice, failed twice, succeeded once.
@@ -623,6 +651,8 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Delayed({{"report0", RetryStatus::NOT_RETRIED}}));
 
+  WaitForNoReports();
+
   // report0 was submitted once and succeeded once.
   event_histogram_tester()->SetExpectedValues(
       1 /* submitted */, 0 /* failed */, 1 /* successful */, 0 /* dropped */);
@@ -674,6 +704,11 @@
 
   // Disable SafeBrowsing. This should clear all pending reports.
   ToggleSafeBrowsingAndWaitForServiceReset(false);
+
+  // In production, the request would have already went out to the network. For
+  // this test, we manually resume it which will cause it to be cancelled.
+  test_helper()->ResumeDelayedRequest();
+
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Delayed({{"report0", RetryStatus::NOT_RETRIED}}));
 
@@ -695,6 +730,8 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Delayed({{"report1", RetryStatus::NOT_RETRIED}}));
 
+  WaitForNoReports();
+
   // report0 was submitted once and delayed, then cleared.
   // report1 was submitted once and delayed, then succeeded.
   event_histogram_tester()->SetExpectedValues(
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc b/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc
index a69205d9..5a4607e 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_factory.cc
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
+
 #include "base/time/default_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service.h"
-#include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
 
@@ -65,6 +67,11 @@
   service_reset_callback_ = service_reset_callback;
 }
 
+void CertificateReportingServiceFactory::SetURLLoaderFactoryForTesting(
+    scoped_refptr<network::SharedURLLoaderFactory> factory) {
+  url_loader_factory_ = factory;
+}
+
 CertificateReportingServiceFactory::CertificateReportingServiceFactory()
     : BrowserContextKeyedServiceFactory(
           "cert_reporting::Factory",
@@ -83,7 +90,9 @@
   safe_browsing::SafeBrowsingService* safe_browsing_service =
       g_browser_process->safe_browsing_service();
   return new CertificateReportingService(
-      safe_browsing_service, safe_browsing_service->url_request_context(),
+      safe_browsing_service,
+      url_loader_factory_.get() ? url_loader_factory_
+                                : safe_browsing_service->GetURLLoaderFactory(),
       static_cast<Profile*>(profile), server_public_key_,
       server_public_key_version_, max_queued_report_count_, queued_report_ttl_,
       clock_, service_reset_callback_);
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_factory.h b/chrome/browser/safe_browsing/certificate_reporting_service_factory.h
index b898bbe1..5fb3e1ce 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_factory.h
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_factory.h
@@ -7,6 +7,7 @@
 
 #include "base/memory/singleton.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace base {
 class Clock;
@@ -36,6 +37,8 @@
   void SetMaxQueuedReportCountForTesting(size_t max_report_count);
   void SetServiceResetCallbackForTesting(
       const base::Callback<void()>& service_reset_callback);
+  void SetURLLoaderFactoryForTesting(
+      scoped_refptr<network::SharedURLLoaderFactory> factory);
 
  private:
   friend struct base::DefaultSingletonTraits<
@@ -58,6 +61,7 @@
   base::TimeDelta queued_report_ttl_;
   size_t max_queued_report_count_;
   base::Callback<void()> service_reset_callback_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CertificateReportingServiceFactory);
 };
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc
index 0b6d2af..8b1ccf2 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.cc
@@ -23,26 +23,16 @@
 static const char kHkdfLabel[] = "certificate report";
 const uint32_t kServerPublicKeyTestVersion = 16;
 
-void SetUpURLHandlersOnIOThread(
-    std::unique_ptr<net::URLRequestInterceptor> url_request_interceptor) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
-  filter->AddUrlInterceptor(
-      CertificateReportingService::GetReportingURLForTesting(),
-      std::move(url_request_interceptor));
+std::string GetUploadData(const network::ResourceRequest& request) {
+  auto body = request.request_body;
+  CHECK(body.get());
+  CHECK_EQ(1u, body->elements()->size());
+  const auto& element = body->elements()->at(0);
+  CHECK_EQ(network::DataElement::TYPE_BYTES, element.type());
+  return std::string(element.bytes(), element.length());
 }
 
-std::string GetUploadData(net::URLRequest* request) {
-  const net::UploadDataStream* stream = request->get_upload();
-  EXPECT_TRUE(stream);
-  EXPECT_TRUE(stream->GetElementReaders());
-  EXPECT_EQ(1u, stream->GetElementReaders()->size());
-  const net::UploadBytesElementReader* reader =
-      (*stream->GetElementReaders())[0]->AsBytesReader();
-  return std::string(reader->bytes(), reader->length());
-}
-
-std::string GetReportContents(net::URLRequest* request,
+std::string GetReportContents(const network::ResourceRequest& request,
                               const uint8_t* server_private_key) {
   std::string serialized_report(GetUploadData(request));
   encrypted_messages::EncryptedMessage encrypted_message;
@@ -235,108 +225,6 @@
                      weak_factory_.GetWeakPtr()));
 }
 
-CertReportJobInterceptor::CertReportJobInterceptor(
-    ReportSendingResult expected_report_result,
-    const uint8_t* server_private_key)
-    : expected_report_result_(expected_report_result),
-      server_private_key_(server_private_key) {}
-
-CertReportJobInterceptor::~CertReportJobInterceptor() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-}
-
-net::URLRequestJob* CertReportJobInterceptor::MaybeInterceptRequest(
-    net::URLRequest* request,
-    net::NetworkDelegate* network_delegate) const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  const std::string serialized_report =
-      GetReportContents(request, server_private_key_);
-  RequestCreated(serialized_report, expected_report_result_);
-
-  if (expected_report_result_ == REPORTS_FAIL) {
-    return new DelayableCertReportURLRequestJob(
-        false, true, request, network_delegate,
-        base::Bind(&CertReportJobInterceptor::RequestDestructed,
-                   base::Unretained(this), serialized_report,
-                   expected_report_result_));
-
-  } else if (expected_report_result_ == REPORTS_DELAY) {
-    DCHECK(!delayed_request_) << "Supports only one delayed request at a time";
-    DelayableCertReportURLRequestJob* job =
-        new DelayableCertReportURLRequestJob(
-            true, false, request, network_delegate,
-            base::Bind(&CertReportJobInterceptor::RequestDestructed,
-                       base::Unretained(this), serialized_report,
-                       expected_report_result_));
-    delayed_request_ = job->GetWeakPtr();
-    return job;
-  }
-  // Successful url request job.
-  return new DelayableCertReportURLRequestJob(
-      false, false, request, network_delegate,
-      base::Bind(&CertReportJobInterceptor::RequestDestructed,
-                 base::Unretained(this), serialized_report,
-                 expected_report_result_));
-}
-
-void CertReportJobInterceptor::SetFailureMode(
-    ReportSendingResult expected_report_result) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CertReportJobInterceptor::SetFailureModeOnIOThread,
-                     base::Unretained(this), expected_report_result));
-}
-
-void CertReportJobInterceptor::Resume() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CertReportJobInterceptor::ResumeOnIOThread,
-                     base::Unretained(this)));
-}
-
-RequestObserver* CertReportJobInterceptor::request_created_observer() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return &request_created_observer_;
-}
-
-RequestObserver* CertReportJobInterceptor::request_destroyed_observer() const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return &request_destroyed_observer_;
-}
-
-void CertReportJobInterceptor::SetFailureModeOnIOThread(
-    ReportSendingResult expected_report_result) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  expected_report_result_ = expected_report_result;
-}
-
-void CertReportJobInterceptor::ResumeOnIOThread() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  EXPECT_EQ(REPORTS_DELAY, expected_report_result_);
-  if (delayed_request_)
-    delayed_request_->Resume();
-}
-
-void CertReportJobInterceptor::RequestCreated(
-    const std::string& serialized_report,
-    ReportSendingResult expected_report_result) const {
-  content::BrowserThread::PostTask(
-      content::BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&RequestObserver::OnRequest,
-                     base::Unretained(&request_created_observer_),
-                     serialized_report, expected_report_result));
-}
-
-void CertReportJobInterceptor::RequestDestructed(
-    const std::string& serialized_report,
-    ReportSendingResult expected_report_result) const {
-  request_destroyed_observer_.OnRequest(serialized_report,
-                                        expected_report_result);
-}
-
 ReportExpectation::ReportExpectation() {}
 
 ReportExpectation::ReportExpectation(const ReportExpectation& other) = default;
@@ -393,7 +281,8 @@
     run_loop_->Quit();
 }
 
-CertificateReportingServiceTestHelper::CertificateReportingServiceTestHelper() {
+CertificateReportingServiceTestHelper::CertificateReportingServiceTestHelper()
+    : expected_report_result_(REPORTS_FAIL) {
   memset(server_private_key_, 1, sizeof(server_private_key_));
   X25519_public_from_private(server_public_key_, server_private_key_);
 }
@@ -401,26 +290,19 @@
 CertificateReportingServiceTestHelper::
     ~CertificateReportingServiceTestHelper() {}
 
-void CertificateReportingServiceTestHelper::SetUpInterceptor() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  url_request_interceptor_ =
-      new CertReportJobInterceptor(REPORTS_FAIL, server_private_key_);
-  content::BrowserThread::PostTask(
-      content::BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&SetUpURLHandlersOnIOThread,
-                     std::unique_ptr<net::URLRequestInterceptor>(
-                         url_request_interceptor_)));
-}
-
 void CertificateReportingServiceTestHelper::SetFailureMode(
     ReportSendingResult expected_report_result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  url_request_interceptor_->SetFailureMode(expected_report_result);
+  expected_report_result_ = expected_report_result;
 }
 
 void CertificateReportingServiceTestHelper::ResumeDelayedRequest() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  url_request_interceptor_->Resume();
+  EXPECT_EQ(REPORTS_DELAY, expected_report_result_);
+  if (delayed_client_) {
+    SendResponse(std::move(delayed_client_), delayed_result_ == REPORTS_FAIL);
+    request_destroyed_observer_.OnRequest(delayed_report_, delayed_result_);
+  }
 }
 
 uint8_t* CertificateReportingServiceTestHelper::server_public_key() {
@@ -434,41 +316,33 @@
 
 void CertificateReportingServiceTestHelper::WaitForRequestsCreated(
     const ReportExpectation& expectation) {
-  WaitReports(interceptor()->request_created_observer(), expectation, nullptr);
+  WaitReports(&request_created_observer_, expectation, nullptr);
 }
 
 void CertificateReportingServiceTestHelper::WaitForRequestsCreated(
     const ReportExpectation& expectation,
     std::vector<std::string>* full_reports) {
-  WaitReports(interceptor()->request_created_observer(), expectation,
-              full_reports);
+  WaitReports(&request_created_observer_, expectation, full_reports);
 }
 
 void CertificateReportingServiceTestHelper::WaitForRequestsDestroyed(
     const ReportExpectation& expectation) {
-  WaitReports(interceptor()->request_destroyed_observer(), expectation,
-              nullptr);
+  WaitReports(&request_destroyed_observer_, expectation, nullptr);
 }
 
 void CertificateReportingServiceTestHelper::WaitForRequestsDestroyed(
     const ReportExpectation& expectation,
     std::vector<std::string>* full_reports) {
-  WaitReports(interceptor()->request_destroyed_observer(), expectation,
-              full_reports);
+  WaitReports(&request_destroyed_observer_, expectation, full_reports);
 }
 
 void CertificateReportingServiceTestHelper::ExpectNoRequests(
     CertificateReportingService* service) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // Check that all requests have been destroyed.
-  EXPECT_TRUE(interceptor()
-                  ->request_destroyed_observer()
-                  ->successful_reports()
-                  .empty());
-  EXPECT_TRUE(
-      interceptor()->request_destroyed_observer()->failed_reports().empty());
-  EXPECT_TRUE(
-      interceptor()->request_destroyed_observer()->delayed_reports().empty());
+  EXPECT_TRUE(request_destroyed_observer_.successful_reports().empty());
+  EXPECT_TRUE(request_destroyed_observer_.failed_reports().empty());
+  EXPECT_TRUE(request_destroyed_observer_.delayed_reports().empty());
 
   if (service->GetReporterForTesting()) {
     // Reporter can be null if reporting is disabled.
@@ -478,6 +352,62 @@
   }
 }
 
+void CertificateReportingServiceTestHelper::SendResponse(
+    network::mojom::URLLoaderClientPtr client,
+    bool fail) {
+  if (fail) {
+    client->OnComplete(
+        network::URLLoaderCompletionStatus(net::ERR_SSL_PROTOCOL_ERROR));
+    return;
+  }
+
+  network::ResourceResponseHead head;
+  head.headers = new net::HttpResponseHeaders(
+      "HTTP/1.1 200 OK\nContent-type: text/html\n\n");
+  head.mime_type = "text/html";
+  client->OnReceiveResponse(head, nullptr);
+  client->OnComplete(network::URLLoaderCompletionStatus());
+}
+
+std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+CertificateReportingServiceTestHelper::Clone() {
+  NOTREACHED();
+  return nullptr;
+}
+
+void CertificateReportingServiceTestHelper::CreateLoaderAndStart(
+    network::mojom::URLLoaderRequest request,
+    int32_t routing_id,
+    int32_t request_id,
+    uint32_t options,
+    const network::ResourceRequest& url_request,
+    network::mojom::URLLoaderClientPtr client,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  const std::string serialized_report =
+      GetReportContents(url_request, server_private_key_);
+  request_created_observer_.OnRequest(serialized_report,
+                                      expected_report_result_);
+
+  if (expected_report_result_ == REPORTS_FAIL) {
+    SendResponse(std::move(client), true);
+    request_destroyed_observer_.OnRequest(serialized_report,
+                                          expected_report_result_);
+    return;
+  }
+
+  if (expected_report_result_ == REPORTS_DELAY) {
+    DCHECK(!delayed_client_) << "Supports only one delayed request at a time";
+    delayed_client_ = std::move(client);
+    delayed_report_ = serialized_report;
+    delayed_result_ = expected_report_result_;
+    return;
+  }
+
+  SendResponse(std::move(client), false);
+  request_destroyed_observer_.OnRequest(serialized_report,
+                                        expected_report_result_);
+}
+
 EventHistogramTester::EventHistogramTester() {}
 
 EventHistogramTester::~EventHistogramTester() {
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
index 41a8631..c061afa1 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
@@ -17,6 +17,7 @@
 #include "net/base/network_delegate_impl.h"
 #include "net/url_request/url_request_interceptor.h"
 #include "net/url_request/url_request_job.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace net {
 class NetworkDelegate;
@@ -139,51 +140,6 @@
   DISALLOW_COPY_AND_ASSIGN(DelayableCertReportURLRequestJob);
 };
 
-// A job interceptor that returns a failed, succesful or delayed request job.
-// Used to simulate report uploads that fail, succeed or hang.
-// The caller is responsible for guaranteeing that |this| is kept alive for
-// all posted tasks and URLRequestJob objects.
-class CertReportJobInterceptor : public net::URLRequestInterceptor {
- public:
-  CertReportJobInterceptor(ReportSendingResult expected_report_result,
-                           const uint8_t* server_private_key);
-  ~CertReportJobInterceptor() override;
-
-  // net::URLRequestInterceptor method:
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override;
-
-  // Sets the failure mode for reports. Must be called on the UI thread.
-  void SetFailureMode(ReportSendingResult expected_report_result);
-  // Resumes any hanging URL request and runs callback when the request
-  // is resumed (i.e. response starts). Must be called on the UI thread.
-  void Resume();
-
-  RequestObserver* request_created_observer() const;
-  RequestObserver* request_destroyed_observer() const;
-
- private:
-  void SetFailureModeOnIOThread(ReportSendingResult expected_report_result);
-  void ResumeOnIOThread();
-  void RequestCreated(const std::string& serialized_report,
-                      ReportSendingResult expected_report_result) const;
-  void RequestDestructed(const std::string& serialized_report,
-                         ReportSendingResult expected_report_result) const;
-
-  ReportSendingResult expected_report_result_;
-
-  // Private key to decrypt certificate reports.
-  const uint8_t* server_private_key_;
-
-  mutable RequestObserver request_created_observer_;
-  mutable RequestObserver request_destroyed_observer_;
-
-  mutable base::WeakPtr<DelayableCertReportURLRequestJob> delayed_request_;
-
-  DISALLOW_COPY_AND_ASSIGN(CertReportJobInterceptor);
-};
-
 // Class to wait for the CertificateReportingService to reset.
 class CertificateReportingServiceObserver {
  public:
@@ -204,14 +160,11 @@
   std::unique_ptr<base::RunLoop> run_loop_;
 };
 
-// Base class for CertificateReportingService tests. Sets up an interceptor to
-// keep track of reports that are being sent.
-class CertificateReportingServiceTestHelper {
+// Base class for CertificateReportingService tests.
+class CertificateReportingServiceTestHelper
+    : public network::SharedURLLoaderFactory {
  public:
   CertificateReportingServiceTestHelper();
-  ~CertificateReportingServiceTestHelper();
-
-  void SetUpInterceptor();
 
   // Changes the behavior of report uploads to fail, succeed or hang.
   void SetFailureMode(ReportSendingResult expected_report_result);
@@ -235,10 +188,30 @@
   uint32_t server_public_key_version() const;
 
  private:
-  CertReportJobInterceptor* interceptor() { return url_request_interceptor_; }
-  void SetUpInterceptorOnIOThread();
+  friend class base::RefCounted<CertificateReportingServiceTestHelper>;
+  ~CertificateReportingServiceTestHelper() override;
 
-  CertReportJobInterceptor* url_request_interceptor_;
+  void SendResponse(network::mojom::URLLoaderClientPtr client, bool fail);
+
+  // network::SharedURLLoaderFactory
+  std::unique_ptr<network::SharedURLLoaderFactoryInfo> Clone() override;
+  void CreateLoaderAndStart(network::mojom::URLLoaderRequest request,
+                            int32_t routing_id,
+                            int32_t request_id,
+                            uint32_t options,
+                            const network::ResourceRequest& url_request,
+                            network::mojom::URLLoaderClientPtr client,
+                            const net::MutableNetworkTrafficAnnotationTag&
+                                traffic_annotation) override;
+
+  ReportSendingResult expected_report_result_;
+
+  network::mojom::URLLoaderClientPtr delayed_client_;
+  std::string delayed_report_;
+  ReportSendingResult delayed_result_;
+
+  RequestObserver request_created_observer_;
+  RequestObserver request_destroyed_observer_;
 
   uint8_t server_public_key_[32];
   uint8_t server_private_key_[32];
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc
index c4f6b19c..30cbfef7 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ssl/certificate_error_report.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/weak_wrapper_shared_url_loader_factory.h"
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "crypto/rsa_private_key.h"
@@ -33,6 +34,7 @@
 #include "net/test/url_request/url_request_mock_data_job.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using certificate_reporting_test_utils::CertificateReportingServiceTestHelper;
@@ -90,10 +92,6 @@
   EXPECT_EQ(expected_creation_time, report.creation_time);
 }
 
-void ClearURLHandlers() {
-  net::URLRequestFilter::GetInstance()->ClearHandlers();
-}
-
 // Class for histogram testing. The failed report histogram is checked once
 // after teardown to ensure all in flight requests have completed.
 class ReportHistogramTestHelper {
@@ -167,19 +165,15 @@
     : public ::testing::Test {
  public:
   void SetUp() override {
-    message_loop_.reset(new base::MessageLoopForIO());
-    io_thread_.reset(new content::TestBrowserThread(content::BrowserThread::IO,
-                                                    message_loop_.get()));
-    url_request_context_getter_ =
-        new net::TestURLRequestContextGetter(message_loop_->task_runner());
-    net::URLRequestFailedJob::AddUrlHandler();
-    net::URLRequestMockDataJob::AddUrlHandler();
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<content::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
 
+    message_loop_.reset(new base::MessageLoopForIO());
     event_histogram_tester_.reset(new EventHistogramTester());
   }
 
   void TearDown() override {
-    ClearURLHandlers();
     // Check histograms as the last thing. This makes sure no in-flight report
     // is missed.
     histogram_test_helper_.CheckHistogram();
@@ -187,8 +181,11 @@
   }
 
  protected:
-  net::URLRequestContextGetter* url_request_context_getter() {
-    return url_request_context_getter_.get();
+  network::TestURLLoaderFactory* test_url_loader_factory() {
+    return &test_url_loader_factory_;
+  }
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory() {
+    return test_shared_loader_factory_;
   }
 
   void SetExpectedFailedReportCountOnTearDown(unsigned int count) {
@@ -203,7 +200,8 @@
   std::unique_ptr<base::MessageLoopForIO> message_loop_;
   std::unique_ptr<content::TestBrowserThread> io_thread_;
 
-  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   ReportHistogramTestHelper histogram_test_helper_;
   // Histogram tester for reporting events. This is a member instead of a local
   // so that we can check the histogram after the test teardown. At that point
@@ -220,11 +218,14 @@
   const base::Time reference_time = base::Time::Now();
   clock->SetNow(reference_time);
 
-  const GURL kFailureURL =
-      net::URLRequestFailedJob::GetMockHttpsUrl(net::ERR_SSL_PROTOCOL_ERROR);
+  const GURL kFailureURL("https://www.foo.com/");
+
+  test_url_loader_factory()->AddResponse(
+      kFailureURL, network::ResourceResponseHead(), std::string(),
+      network::URLLoaderCompletionStatus(net::ERR_SSL_PROTOCOL_ERROR));
+
   CertificateErrorReporter* certificate_error_reporter =
-      new CertificateErrorReporter(
-          url_request_context_getter()->GetURLRequestContext(), kFailureURL);
+      new CertificateErrorReporter(test_shared_loader_factory(), kFailureURL);
 
   CertificateReportingService::BoundedReportList* list =
       new CertificateReportingService::BoundedReportList(2);
@@ -290,8 +291,10 @@
 
   // Send pending reports again, this time successfully. There should be no
   // pending reports left.
-  const GURL kSuccessURL =
-      net::URLRequestMockDataJob::GetMockHttpsUrl("dummy data", 1);
+  const GURL kSuccessURL("https://www.bar.com/");
+
+  test_url_loader_factory()->AddResponse(kSuccessURL.spec(), "dummy data");
+
   certificate_error_reporter->set_upload_url_for_testing(kSuccessURL);
   clock->Advance(base::TimeDelta::FromSeconds(1));
   reporter.SendPending();
@@ -315,11 +318,14 @@
   base::Time reference_time = base::Time::Now();
   clock->SetNow(reference_time);
 
-  const GURL kFailureURL =
-      net::URLRequestFailedJob::GetMockHttpsUrl(net::ERR_SSL_PROTOCOL_ERROR);
+  const GURL kFailureURL("https://www.foo.com/");
+
+  test_url_loader_factory()->AddResponse(
+      kFailureURL, network::ResourceResponseHead(), std::string(),
+      network::URLLoaderCompletionStatus(net::ERR_SSL_PROTOCOL_ERROR));
+
   CertificateErrorReporter* certificate_error_reporter =
-      new CertificateErrorReporter(
-          url_request_context_getter()->GetURLRequestContext(), kFailureURL);
+      new CertificateErrorReporter(test_shared_loader_factory(), kFailureURL);
 
   CertificateReportingService::BoundedReportList* list =
       new CertificateReportingService::BoundedReportList(2);
@@ -369,24 +375,18 @@
 
   void SetUp() override {
     service_observer_.Clear();
-    test_helper_.SetUpInterceptor();
-    WaitForIOThread();
-
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::BindOnce(
-            &CertificateReportingServiceTest::SetUpURLRequestContextOnIOThread,
-            base::Unretained(this)));
-    WaitForIOThread();
 
     safe_browsing::SafeBrowsingService::RegisterFactory(&sb_service_factory);
     sb_service_ = sb_service_factory.CreateSafeBrowsingService();
 
+    test_helper_ =
+        base::MakeRefCounted<CertificateReportingServiceTestHelper>();
+
     clock_.reset(new base::SimpleTestClock());
     service_.reset(new CertificateReportingService(
-        sb_service_.get(), url_request_context_getter(), &profile_,
-        test_helper_.server_public_key(),
-        test_helper_.server_public_key_version(), kMaxReportCountInQueue,
+        sb_service_.get(), test_helper_, &profile_,
+        test_helper_->server_public_key(),
+        test_helper_->server_public_key_version(), kMaxReportCountInQueue,
         base::TimeDelta::FromHours(24), clock_.get(),
         base::Bind(&CertificateReportingServiceObserver::OnServiceReset,
                    base::Unretained(&service_observer_))));
@@ -396,36 +396,16 @@
   }
 
   void TearDown() override {
-    WaitForIOThread();
     test_helper()->ExpectNoRequests(service());
 
     service_->Shutdown();
-    WaitForIOThread();
     service_.reset(nullptr);
 
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::BindOnce(&CertificateReportingServiceTest::TearDownOnIOThread,
-                       base::Unretained(this)));
-    content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
-                                     base::BindOnce(&ClearURLHandlers));
-    WaitForIOThread();
-
     histogram_test_helper_.CheckHistogram();
     event_histogram_tester_.reset();
   }
 
  protected:
-  net::URLRequestContextGetter* url_request_context_getter() {
-    return url_request_context_getter_.get();
-  }
-
-  void WaitForIOThread() {
-    scoped_refptr<base::ThreadTestHelper> io_helper(
-        new base::ThreadTestHelper(io_task_runner_));
-    ASSERT_TRUE(io_helper->Run());
-  }
-
   CertificateReportingService* service() { return service_.get(); }
 
   // Sets service enabled state and waits for a reset event.
@@ -435,42 +415,33 @@
     service_observer_.WaitForReset();
   }
 
-  void AdvanceClock(base::TimeDelta delta) {
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::BindOnce(&base::SimpleTestClock::Advance,
-                       base::Unretained(clock_.get()), delta));
-  }
+  void AdvanceClock(base::TimeDelta delta) { clock_->Advance(delta); }
 
-  void SetNow(base::Time now) {
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO, FROM_HERE,
-        base::BindOnce(&base::SimpleTestClock::SetNow,
-                       base::Unretained(clock_.get()), now));
-  }
+  void SetNow(base::Time now) { clock_->SetNow(now); }
 
   void SetExpectedFailedReportCountOnTearDown(unsigned int count) {
     histogram_test_helper_.SetExpectedFailedReportCount(count);
   }
 
-  CertificateReportingServiceTestHelper* test_helper() { return &test_helper_; }
+  CertificateReportingServiceTestHelper* test_helper() {
+    return test_helper_.get();
+  }
 
   EventHistogramTester* event_histogram_tester() {
     return event_histogram_tester_.get();
   }
 
+  void WaitForNoReports() {
+    if (!service_->GetReporterForTesting()->inflight_report_count_for_testing())
+      return;
+
+    base::RunLoop run_loop;
+    service_->GetReporterForTesting()
+        ->SetClosureWhenNoInflightReportsForTesting(run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
  private:
-  void SetUpURLRequestContextOnIOThread() {
-    std::unique_ptr<net::TestURLRequestContext> url_request_context(
-        new net::TestURLRequestContext(false));
-    url_request_context_getter_ = new net::TestURLRequestContextGetter(
-        io_task_runner_, std::move(url_request_context));
-  }
-
-  void TearDownOnIOThread() {
-    url_request_context_getter_ = nullptr;
-  }
-
   // Must be initialized before url_request_context_getter_
   content::TestBrowserThreadBundle thread_bundle_;
 
@@ -484,7 +455,7 @@
   safe_browsing::TestSafeBrowsingServiceFactory sb_service_factory;
   TestingProfile profile_;
 
-  CertificateReportingServiceTestHelper test_helper_;
+  scoped_refptr<CertificateReportingServiceTestHelper> test_helper_;
   ReportHistogramTestHelper histogram_test_helper_;
   CertificateReportingServiceObserver service_observer_;
 
@@ -504,6 +475,7 @@
       ReportExpectation::Successful({{"report0", RetryStatus::NOT_RETRIED},
                                      {"report1", RetryStatus::NOT_RETRIED}}));
 
+  WaitForNoReports();
   // report0 and report1 were both submitted once, succeeded once.
   event_histogram_tester()->SetExpectedValues(
       2 /* submitted */, 0 /* failed */, 2 /* successful */, 0 /* dropped */);
@@ -523,6 +495,9 @@
       ReportExpectation::Failed({{"report0", RetryStatus::NOT_RETRIED},
                                  {"report1", RetryStatus::NOT_RETRIED}}));
 
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Send pending reports. Previously queued reports should be queued again.
   service()->SendPending();
   test_helper()->WaitForRequestsDestroyed(ReportExpectation::Failed(
@@ -537,12 +512,16 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Successful({{"report2", RetryStatus::NOT_RETRIED}}));
 
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Send pending reports. Previously failed and queued two reports should be
   // observed.
   service()->SendPending();
   test_helper()->WaitForRequestsDestroyed(ReportExpectation::Successful(
       {{"report0", RetryStatus::RETRIED}, {"report1", RetryStatus::RETRIED}}));
 
+  WaitForNoReports();
   // report0 and report1 were both submitted thrice, failed twice, succeeded
   // once. report2 was submitted once, succeeded once.
   event_histogram_tester()->SetExpectedValues(
@@ -570,6 +549,7 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Successful({{"report1", RetryStatus::NOT_RETRIED}}));
 
+  WaitForNoReports();
   // report0 was never sent. report1 was submitted once, succeeded once.
   event_histogram_tester()->SetExpectedValues(
       1 /* submitted */, 0 /* failed */, 1 /* successful */, 0 /* dropped */);
@@ -586,6 +566,9 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Failed({{"report0", RetryStatus::NOT_RETRIED}}));
 
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Disable the service.
   SetServiceEnabledAndWait(false);
 
@@ -599,6 +582,7 @@
   // Sending with empty queue has no effect.
   service()->SendPending();
 
+  WaitForNoReports();
   // report0 was submitted once, failed once.
   event_histogram_tester()->SetExpectedValues(
       1 /* submitted */, 1 /* failed */, 0 /* successful */, 0 /* dropped */);
@@ -624,6 +608,10 @@
   // time. This makes the report0 older than max age (24 hours). The report1 is
   // now 20 hours old.
   AdvanceClock(base::TimeDelta::FromHours(20));
+
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Send pending reports. report0 should be discarded since it's too old.
   // report1 should be queued again.
   service()->SendPending();
@@ -637,6 +625,10 @@
 
   // Advance the clock 5 hours. The report1 will now be 25 hours old.
   AdvanceClock(base::TimeDelta::FromHours(5));
+
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Send pending reports. report1 should be discarded since it's too old.
   // report2 should be queued again.
   service()->SendPending();
@@ -646,10 +638,15 @@
   // Advance the clock 20 hours again so that report2 is 25 hours old and is
   // older than max age (24 hours)
   AdvanceClock(base::TimeDelta::FromHours(20));
+
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Send pending reports. report2 should be discarded since it's too old. No
   // other reports remain.
   service()->SendPending();
 
+  WaitForNoReports();
   // report0 was submitted once, failed once, dropped once.
   // report1 was submitted twice, failed twice, dropped once.
   // report2 was submitted twice, failed twice, dropped once.
@@ -687,6 +684,9 @@
                                  {"report2", RetryStatus::NOT_RETRIED},
                                  {"report3", RetryStatus::NOT_RETRIED}}));
 
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Send pending reports. Four reports were generated above, but the service
   // only queues three reports, so the very first one should be dropped since
   // it's the oldest.
@@ -706,6 +706,10 @@
   // report2 is 20 hours old.
   // report3 is 15 hours old.
   AdvanceClock(base::TimeDelta::FromHours(15));
+
+  // Need this to ensure the pending reports are queued.
+  WaitForNoReports();
+
   // Send pending reports. Only report2 and report3 should be sent, report1
   // should be ignored because it's too old.
   service()->SendPending();
@@ -715,6 +719,7 @@
   // Do a final send. No reports should be sent.
   service()->SendPending();
 
+  WaitForNoReports();
   // report0 was submitted once, failed once, dropped once.
   // report1 was submitted twice, failed twice, dropped once.
   // report2 was submitted thrice, failed twice, succeeded once.
@@ -740,6 +745,7 @@
   test_helper()->WaitForRequestsDestroyed(
       ReportExpectation::Delayed({{"report0", RetryStatus::NOT_RETRIED}}));
 
+  WaitForNoReports();
   // report0 was submitted once, succeeded once.
   event_histogram_tester()->SetExpectedValues(
       1 /* submitted */, 0 /* failed */, 1 /* successful */, 0 /* dropped */);
@@ -779,6 +785,7 @@
       ReportExpectation::Delayed({{"report0", RetryStatus::NOT_RETRIED},
                                   {"report1", RetryStatus::NOT_RETRIED}}));
 
+  WaitForNoReports();
   // report0 was submitted once, but neither failed nor succeeded because the
   // report queue was cleared. report1 was submitted once, succeeded once.
   event_histogram_tester()->SetExpectedValues(
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index e60e885..e906e44 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -229,7 +229,7 @@
 
 void ChromePasswordProtectionService::FillReferrerChain(
     const GURL& event_url,
-    int event_tab_id,
+    SessionID event_tab_id,
     LoginReputationClientRequest::Frame* frame) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   SafeBrowsingNavigationObserverManager::AttributionResult result =
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.h b/chrome/browser/safe_browsing/chrome_password_protection_service.h
index 9ff7dfa..745d372 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.h
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.h
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "components/safe_browsing/password_protection/password_protection_service.h"
 #include "components/safe_browsing/triggers/trigger_manager.h"
+#include "components/sessions/core/session_id.h"
 #include "components/sync/protocol/user_event_specifics.pb.h"
 #include "ui/base/ui_features.h"
 #include "url/origin.h"
@@ -153,7 +154,7 @@
   // Obtains referrer chain of |event_url| and |event_tab_id| and add this
   // info into |frame|.
   void FillReferrerChain(const GURL& event_url,
-                         int event_tab_id,
+                         SessionID event_tab_id,
                          LoginReputationClientRequest::Frame* frame) override;
 
   bool IsExtendedReporting() override;
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index 59ab62af..67e32ec 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -375,10 +375,10 @@
   content::WebContents* web_contents =
       content::DownloadItemUtils::GetWebContents(
           const_cast<download::DownloadItem*>(&item));
-  int download_tab_id = SessionTabHelper::IdForTab(web_contents);
+  SessionID download_tab_id = SessionTabHelper::IdForTab(web_contents);
   UMA_HISTOGRAM_BOOLEAN(
       "SafeBrowsing.ReferrerHasInvalidTabID.DownloadAttribution",
-      download_tab_id == -1);
+      !download_tab_id.is_valid());
   // We look for the referrer chain that leads to the download url first.
   SafeBrowsingNavigationObserverManager::AttributionResult result =
       navigation_observer_manager_->IdentifyReferrerChainByEventURL(
@@ -423,14 +423,15 @@
 void DownloadProtectionService::AddReferrerChainToPPAPIClientDownloadRequest(
     const GURL& initiating_frame_url,
     const GURL& initiating_main_frame_url,
-    int tab_id,
+    SessionID tab_id,
     bool has_user_gesture,
     ClientDownloadRequest* out_request) {
   if (!navigation_observer_manager_)
     return;
 
   UMA_HISTOGRAM_BOOLEAN(
-      "SafeBrowsing.ReferrerHasInvalidTabID.DownloadAttribution", tab_id == -1);
+      "SafeBrowsing.ReferrerHasInvalidTabID.DownloadAttribution",
+      !tab_id.is_valid());
   SafeBrowsingNavigationObserverManager::AttributionResult result =
       navigation_observer_manager_->IdentifyReferrerChainByHostingPage(
           initiating_frame_url, initiating_main_frame_url, tab_id,
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.h b/chrome/browser/safe_browsing/download_protection/download_protection_service.h
index f0e7d07..0736e8e 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.h
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.h
@@ -27,6 +27,7 @@
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "components/safe_browsing/db/database_manager.h"
+#include "components/sessions/core/session_id.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -242,7 +243,7 @@
   void AddReferrerChainToPPAPIClientDownloadRequest(
       const GURL& initiating_frame_url,
       const GURL& initiating_main_frame_url,
-      int tab_id,
+      SessionID tab_id,
       bool has_user_gesture,
       ClientDownloadRequest* out_request);
 
diff --git a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.h b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.h
index afdd6345..81eaabb 100644
--- a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.h
+++ b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.h
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
+#include "components/sessions/core/session_id.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -134,7 +135,7 @@
 
   // Tab id that associated with the PPAPI plugin, computed by
   // SessionTabHelper::IdForTab().
-  int tab_id_;
+  SessionID tab_id_;
 
   // If the user interacted with this PPAPI plugin to trigger the download.
   bool has_user_gesture_;
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
index 009fa96..d2ec7d2 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
@@ -35,8 +35,8 @@
     : source_url(),
       source_main_frame_url(),
       original_request_url(),
-      source_tab_id(-1),
-      target_tab_id(-1),
+      source_tab_id(SessionID::InvalidValue()),
+      target_tab_id(SessionID::InvalidValue()),
       frame_id(-1),
       last_updated(base::Time::Now()),
       navigation_initiation(ReferrerChainEntry::UNDEFINED),
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
index 0d95c5c5..67d5047 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
@@ -10,6 +10,7 @@
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/safe_browsing/proto/csd.pb.h"
+#include "components/sessions/core/session_id.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "url/gurl.h"
 
@@ -48,10 +49,10 @@
   // Which tab contains the frame with source_url. Tab ID is returned by
   // SessionTabHelper::IdForTab. This ID is immutable for a given tab and unique
   // across Chrome within the current session.
-  int source_tab_id;
+  SessionID source_tab_id;
 
   // Which tab this request url is targeting to.
-  int target_tab_id;
+  SessionID target_tab_id;
 
   // Frame tree node ID of the frame where this navigation takes place.
   int frame_id;
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
index 548f908d..8f0da3f 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -84,7 +84,6 @@
 const char kPageBeforeLandingReferrerURL[] =
     "/safe_browsing/download_protection/navigation_observer/"
     "page_before_landing_referrer.html";
-const char kTestExeURL[] = "/temporary/test.exe";
 
 class DownloadItemCreatedObserver : public DownloadManager::Observer {
  public:
@@ -364,7 +363,7 @@
   void IdentifyReferrerChainForDownload(
       DownloadItem* download,
       ReferrerChain* referrer_chain) {
-    int download_tab_id = SessionTabHelper::IdForTab(
+    SessionID download_tab_id = SessionTabHelper::IdForTab(
         content::DownloadItemUtils::GetWebContents(download));
     auto result = observer_manager_->IdentifyReferrerChainByEventURL(
         download->GetURL(), download_tab_id,
@@ -393,7 +392,7 @@
       const GURL& initiating_frame_url,
       content::WebContents* web_contents,
       ReferrerChain* referrer_chain) {
-    int tab_id = SessionTabHelper::IdForTab(web_contents);
+    SessionID tab_id = SessionTabHelper::IdForTab(web_contents);
     bool has_user_gesture = observer_manager_->HasUserGesture(web_contents);
     observer_manager_->OnUserGestureConsumed(web_contents, base::Time::Now());
     EXPECT_LE(observer_manager_->IdentifyReferrerChainByHostingPage(
@@ -454,7 +453,7 @@
                                            const GURL& target_url) {
     NavigationEvent* nav_event =
         observer_manager_->navigation_event_list()->FindNavigationEvent(
-            target_url, GURL(), -1);
+            target_url, GURL(), SessionID::InvalidValue());
     if (nav_event) {
       observer_manager_->AddToReferrerChain(referrer_chain, nav_event, GURL(),
                                             ReferrerChainEntry::EVENT_URL);
@@ -2124,8 +2123,6 @@
   GURL hosting_url = embedded_test_server()->GetURL(kSingleFrameTestURL);
   TriggerDownloadViaHtml5FileApi();
   std::string test_server_ip(embedded_test_server()->host_port_pair().host());
-  GURL filesystem_url(std::string(url::kFileSystemScheme) + ":" +
-                      embedded_test_server()->GetURL(kTestExeURL).spec());
   auto* nav_list = navigation_event_list();
   ASSERT_TRUE(nav_list);
   ASSERT_EQ(1U, nav_list->Size());
@@ -2140,19 +2137,7 @@
   VerifyHostToIpMap();
   ReferrerChain referrer_chain;
   IdentifyReferrerChainForDownload(GetDownload(), &referrer_chain);
-  ASSERT_EQ(2, referrer_chain.size());
-
-  VerifyReferrerChainEntry(
-      filesystem_url,                 // url
-      GURL(),                         // main_frame_url
-      ReferrerChainEntry::EVENT_URL,  // type
-      std::string(),                  // ip_address
-      hosting_url,                    // referrer_url
-      GURL(),                         // referrer_main_frame_url
-      false,                          // is_retargeting
-      std::vector<GURL>(),            // server redirects
-      ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE,
-      referrer_chain.Get(0));
+  ASSERT_EQ(1, referrer_chain.size());
 
   VerifyReferrerChainEntry(hosting_url,  // url
                            GURL(),       // main_frame_url
@@ -2163,7 +2148,7 @@
                            false,                // is_retargeting
                            std::vector<GURL>(),  // server redirects
                            ReferrerChainEntry::BROWSER_INITIATED,
-                           referrer_chain.Get(1));
+                           referrer_chain.Get(0));
 }
 
 // Verify referrer chain when there are URL fragments.
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
index a2d7d56d..25b6590 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
@@ -117,7 +117,7 @@
 NavigationEvent* NavigationEventList::FindNavigationEvent(
     const GURL& target_url,
     const GURL& target_main_frame_url,
-    int target_tab_id) {
+    SessionID target_tab_id) {
   if (target_url.is_empty() && target_main_frame_url.is_empty())
     return nullptr;
 
@@ -132,7 +132,8 @@
     auto* nav_event = rit->get();
     // If tab id is not valid, we only compare url, otherwise we compare both.
     if (nav_event->GetDestinationUrl() == search_url &&
-        (target_tab_id == -1 || nav_event->target_tab_id == target_tab_id)) {
+        (!target_tab_id.is_valid() ||
+         nav_event->target_tab_id == target_tab_id)) {
       // If both source_url and source_main_frame_url are empty, and this
       // navigation is not triggered by user, a retargeting navigation probably
       // causes this navigation. In this case, we skip this navigation event and
@@ -165,7 +166,7 @@
 
 NavigationEvent* NavigationEventList::FindRetargetingNavigationEvent(
     const GURL& target_url,
-    int target_tab_id) {
+    SessionID target_tab_id) {
   if (target_url.is_empty())
     return nullptr;
 
@@ -347,7 +348,7 @@
 SafeBrowsingNavigationObserverManager::AttributionResult
 SafeBrowsingNavigationObserverManager::IdentifyReferrerChainByEventURL(
     const GURL& event_url,
-    int event_tab_id,
+    SessionID event_tab_id,
     int user_gesture_count_limit,
     ReferrerChain* out_referrer_chain) {
   if (!event_url.is_valid())
@@ -383,7 +384,7 @@
   if (!last_committed_url.is_valid())
     return INVALID_URL;
   bool has_user_gesture = HasUserGesture(web_contents);
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents);
   return IdentifyReferrerChainByHostingPage(
       ClearURLRef(last_committed_url), GURL(), tab_id, has_user_gesture,
       user_gesture_count_limit, out_referrer_chain);
@@ -393,7 +394,7 @@
 SafeBrowsingNavigationObserverManager::IdentifyReferrerChainByHostingPage(
     const GURL& initiating_frame_url,
     const GURL& initiating_main_frame_url,
-    int tab_id,
+    SessionID tab_id,
     bool has_user_gesture,
     int user_gesture_count_limit,
     ReferrerChain* out_referrer_chain) {
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 562c1d04..cde1f5fee 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
@@ -9,6 +9,7 @@
 #include "base/feature_list.h"
 #include "base/supports_user_data.h"
 #include "components/safe_browsing/proto/csd.pb.h"
+#include "components/sessions/core/session_id.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 #include "url/gurl.h"
@@ -66,7 +67,7 @@
   // |target_main_frame_url| are the same.
   // If |target_url| is empty, we use its main frame url (a.k.a.
   // |target_main_frame_url|) to search for navigation events.
-  // If |target_tab_id| is not available (-1), we look for all tabs for the most
+  // If |target_tab_id| is invalid, we look for all tabs for the most
   // recent navigation to |target_url| or |target_main_frame_url|.
   // For some cases, the most recent navigation to |target_url| may not be
   // relevant.
@@ -83,12 +84,12 @@
   // of all these navigations.
   NavigationEvent* FindNavigationEvent(const GURL& target_url,
                                        const GURL& target_main_frame_url,
-                                       int target_tab_id);
+                                       SessionID target_tab_id);
 
   // Finds the most recent retargeting NavigationEvent that satisfies
   // |target_url|, and |target_tab_id|.
   NavigationEvent* FindRetargetingNavigationEvent(const GURL& target_url,
-                                                  int target_tab_id);
+                                                  SessionID target_tab_id);
 
   void RecordNavigationEvent(std::unique_ptr<NavigationEvent> nav_event);
 
@@ -175,7 +176,7 @@
   // |out_referrer_chain|.
   AttributionResult IdentifyReferrerChainByEventURL(
       const GURL& event_url,
-      int event_tab_id,  // -1 if tab id is unknown or not available
+      SessionID event_tab_id,  // Invalid if tab id is unknown or not available.
       int user_gesture_count_limit,
       ReferrerChain* out_referrer_chain);
 
@@ -201,7 +202,7 @@
   AttributionResult IdentifyReferrerChainByHostingPage(
       const GURL& initiating_frame_url,
       const GURL& initiating_main_frame_url,
-      int tab_id,
+      SessionID tab_id,
       bool has_user_gesture,
       int user_gesture_count_limit,
       ReferrerChain* out_referrer_chain);
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc
index 6baf731..9241ef41 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_unittest.cc
@@ -49,8 +49,8 @@
       const GURL& expected_source_main_frame_url,
       const GURL& expected_original_request_url,
       const GURL& expected_destination_url,
-      int expected_source_tab,
-      int expected_target_tab,
+      SessionID expected_source_tab,
+      SessionID expected_target_tab,
       ReferrerChainEntry::NavigationInitiation expected_nav_initiation,
       bool expected_has_committed,
       bool expected_has_server_redirect,
@@ -119,7 +119,8 @@
   NavigationEventList events(3);
 
   EXPECT_EQ(nullptr,
-            events.FindNavigationEvent(GURL("http://invalid.com"), GURL(), -1));
+            events.FindNavigationEvent(GURL("http://invalid.com"), GURL(),
+                                       SessionID::InvalidValue()));
   EXPECT_EQ(0U, events.CleanUpNavigationEvents());
   EXPECT_EQ(0U, events.Size());
 
@@ -133,9 +134,10 @@
       CreateNavigationEventUniquePtr(GURL("http://foo1.com"), now));
   EXPECT_EQ(2U, events.Size());
   // FindNavigationEvent should return the latest matching event.
-  EXPECT_EQ(now,
-            events.FindNavigationEvent(GURL("http://foo1.com"), GURL(), -1)
-                ->last_updated);
+  EXPECT_EQ(now, events
+                     .FindNavigationEvent(GURL("http://foo1.com"), GURL(),
+                                          SessionID::InvalidValue())
+                     ->last_updated);
   // One event should get removed.
   EXPECT_EQ(1U, events.CleanUpNavigationEvents());
   EXPECT_EQ(1U, events.Size());
@@ -164,7 +166,7 @@
                              WindowOpenDisposition::CURRENT_TAB,
                              ui::PAGE_TRANSITION_AUTO_BOOKMARK, false));
   CommitPendingLoad(controller);
-  int tab_id = SessionTabHelper::IdForTab(controller->GetWebContents());
+  SessionID tab_id = SessionTabHelper::IdForTab(controller->GetWebContents());
   auto* nav_list = navigation_event_list();
   ASSERT_EQ(1U, nav_list->Size());
   VerifyNavigationEvent(GURL(),                // source_url
@@ -186,7 +188,7 @@
   navigation->Start();
   navigation->Redirect(GURL("http://redirect/1"));
   navigation->Commit();
-  int tab_id = SessionTabHelper::IdForTab(
+  SessionID tab_id = SessionTabHelper::IdForTab(
       browser()->tab_strip_model()->GetWebContentsAt(0));
   auto* nav_list = navigation_event_list();
   ASSERT_EQ(1U, nav_list->Size());
@@ -238,8 +240,8 @@
 
   // Verifies all stale and invalid navigation events are removed.
   ASSERT_EQ(2U, navigation_event_list()->Size());
-  EXPECT_EQ(nullptr,
-            navigation_event_list()->FindNavigationEvent(url_1, GURL(), -1));
+  EXPECT_EQ(nullptr, navigation_event_list()->FindNavigationEvent(
+                         url_1, GURL(), SessionID::InvalidValue()));
   EXPECT_THAT(histograms.GetAllSamples(kNavigationEventCleanUpHistogramName),
               testing::ElementsAre(base::Bucket(4, 1)));
 }
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index da80777f..e3e4877 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -176,7 +176,6 @@
 
 void SafeBrowsingService::ShutDown() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  shutdown_callback_list_.Notify();
 
   // Remove Profile creation/destruction observers.
   profiles_registrar_.RemoveAll();
@@ -546,13 +545,6 @@
   return state_callback_list_.Add(callback);
 }
 
-std::unique_ptr<SafeBrowsingService::ShutdownSubscription>
-SafeBrowsingService::RegisterShutdownCallback(
-    const base::Callback<void(void)>& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return shutdown_callback_list_.Add(callback);
-}
-
 void SafeBrowsingService::RefreshState() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Check if any profile requires the service to be active.
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index ddc20fc..58c2dd4 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -210,12 +210,6 @@
   std::unique_ptr<StateSubscription> RegisterStateCallback(
       const base::Callback<void(void)>& callback);
 
-  // Adds a listener for when SafeBrowsingService starts shutting down.
-  // The callbacks run on the UI thread, and give the subscribers an opportunity
-  // to clean up any references they hold to SafeBrowsingService.
-  std::unique_ptr<ShutdownSubscription> RegisterShutdownCallback(
-      const base::Callback<void(void)>& callback);
-
   // Sends serialized download report to backend.
   virtual void SendSerializedDownloadReport(const std::string& report);
 
@@ -350,10 +344,6 @@
   // Should only be accessed on the UI thread.
   base::CallbackList<void(void)> state_callback_list_;
 
-  // Callbacks when SafeBrowsing service starts shutting down.
-  // Should only be accessed on the UI thread.
-  base::CallbackList<void(void)> shutdown_callback_list_;
-
   // The UI manager handles showing interstitials.  Accessed on both UI and IO
   // thread.
   scoped_refptr<SafeBrowsingUIManager> ui_manager_;
diff --git a/chrome/browser/sessions/chrome_tab_restore_service_client.cc b/chrome/browser/sessions/chrome_tab_restore_service_client.cc
index 50940c5..17b168f 100644
--- a/chrome/browser/sessions/chrome_tab_restore_service_client.cc
+++ b/chrome/browser/sessions/chrome_tab_restore_service_client.cc
@@ -80,9 +80,9 @@
 sessions::LiveTabContext*
 ChromeTabRestoreServiceClient::FindLiveTabContextWithID(SessionID desired_id) {
 #if defined(OS_ANDROID)
-  return AndroidLiveTabContext::FindContextWithID(desired_id.id());
+  return AndroidLiveTabContext::FindContextWithID(desired_id);
 #else
-  return BrowserLiveTabContext::FindContextWithID(desired_id.id());
+  return BrowserLiveTabContext::FindContextWithID(desired_id);
 #endif
 }
 
diff --git a/chrome/browser/sessions/session_tab_helper.cc b/chrome/browser/sessions/session_tab_helper.cc
index a5d23d8..a639716 100644
--- a/chrome/browser/sessions/session_tab_helper.cc
+++ b/chrome/browser/sessions/session_tab_helper.cc
@@ -18,8 +18,9 @@
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SessionTabHelper);
 
 SessionTabHelper::SessionTabHelper(content::WebContents* contents)
-    : content::WebContentsObserver(contents) {
-}
+    : content::WebContentsObserver(contents),
+      session_id_(SessionID::NewUnique()),
+      window_id_(SessionID::InvalidValue()) {}
 
 SessionTabHelper::~SessionTabHelper() {
 }
@@ -36,18 +37,20 @@
 }
 
 // static
-SessionID::id_type SessionTabHelper::IdForTab(const content::WebContents* tab) {
+SessionID SessionTabHelper::IdForTab(const content::WebContents* tab) {
   const SessionTabHelper* session_tab_helper =
       tab ? SessionTabHelper::FromWebContents(tab) : NULL;
-  return session_tab_helper ? session_tab_helper->session_id().id() : -1;
+  return session_tab_helper ? session_tab_helper->session_id()
+                            : SessionID::InvalidValue();
 }
 
 // static
-SessionID::id_type SessionTabHelper::IdForWindowContainingTab(
+SessionID SessionTabHelper::IdForWindowContainingTab(
     const content::WebContents* tab) {
   const SessionTabHelper* session_tab_helper =
       tab ? SessionTabHelper::FromWebContents(tab) : NULL;
-  return session_tab_helper ? session_tab_helper->window_id().id() : -1;
+  return session_tab_helper ? session_tab_helper->window_id()
+                            : SessionID::InvalidValue();
 }
 
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
diff --git a/chrome/browser/sessions/session_tab_helper.h b/chrome/browser/sessions/session_tab_helper.h
index 0345f97..a43d485 100644
--- a/chrome/browser/sessions/session_tab_helper.h
+++ b/chrome/browser/sessions/session_tab_helper.h
@@ -29,9 +29,10 @@
   // If the specified WebContents has a SessionTabHelper (probably because it
   // was used as the contents of a tab), returns a tab id. This value is
   // immutable for a given tab. It will be unique across Chrome within the
-  // current session, but may be re-used across sessions. Returns -1
-  // for a NULL WebContents or if the WebContents has no SessionTabHelper.
-  static SessionID::id_type IdForTab(const content::WebContents* tab);
+  // current session, but may be re-used across sessions. Returns
+  // SessionID::InvalidValue() for a NULL WebContents or if the WebContents has
+  // no SessionTabHelper.
+  static SessionID IdForTab(const content::WebContents* tab);
 
   // If the specified WebContents has a SessionTabHelper (probably because it
   // was used as the contents of a tab), and has ever been attached to a Browser
@@ -39,10 +40,9 @@
   // being dragged between Browser windows, returns the old window's id value.
   // If the WebContents has a SessionTabHelper but has never been attached to a
   // Browser window, returns an id value that is different from that of any
-  // Browser. Returns -1 for a NULL WebContents or if the WebContents has no
-  // SessionTabHelper.
-  static SessionID::id_type IdForWindowContainingTab(
-      const content::WebContents* tab);
+  // Browser. Returns SessionID::InvalidValue() for a NULL WebContents or if the
+  // WebContents has no SessionTabHelper.
+  static SessionID IdForWindowContainingTab(const content::WebContents* tab);
 
   // content::WebContentsObserver:
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
diff --git a/chrome/browser/ssl/DEPS b/chrome/browser/ssl/DEPS
index 82a694f..c99cacb 100644
--- a/chrome/browser/ssl/DEPS
+++ b/chrome/browser/ssl/DEPS
@@ -2,6 +2,7 @@
   "+components/captive_portal",
   "+components/certificate_transparency",
   "+components/wifi",
+  "+services/network/test",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/ssl/certificate_error_reporter.cc b/chrome/browser/ssl/certificate_error_reporter.cc
index f2621529..3a98874b 100644
--- a/chrome/browser/ssl/certificate_error_reporter.cc
+++ b/chrome/browser/ssl/certificate_error_reporter.cc
@@ -13,10 +13,12 @@
 
 #include "base/logging.h"
 #include "base/strings/string_piece.h"
+#include "chrome/browser/net/chrome_report_sender.h"
 #include "components/encrypted_messages/encrypted_message.pb.h"
 #include "components/encrypted_messages/message_encrypter.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/report_sender.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace {
 
@@ -72,26 +74,22 @@
 }  // namespace
 
 CertificateErrorReporter::CertificateErrorReporter(
-    net::URLRequestContext* request_context,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const GURL& upload_url)
-    : CertificateErrorReporter(
-          upload_url,
-          kServerPublicKey,
-          kServerPublicKeyVersion,
-          std::make_unique<net::ReportSender>(
-              request_context,
-              kSafeBrowsingCertificateErrorReportingTrafficAnnotation)) {}
+    : CertificateErrorReporter(url_loader_factory,
+                               upload_url,
+                               kServerPublicKey,
+                               kServerPublicKeyVersion) {}
 
 CertificateErrorReporter::CertificateErrorReporter(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const GURL& upload_url,
     const uint8_t server_public_key[/* 32 */],
-    const uint32_t server_public_key_version,
-    std::unique_ptr<net::ReportSender> certificate_report_sender)
-    : certificate_report_sender_(std::move(certificate_report_sender)),
+    const uint32_t server_public_key_version)
+    : url_loader_factory_(url_loader_factory),
       upload_url_(upload_url),
       server_public_key_(server_public_key),
       server_public_key_version_(server_public_key_version) {
-  DCHECK(certificate_report_sender_);
   DCHECK(!upload_url.is_empty());
 }
 
@@ -99,31 +97,32 @@
 
 void CertificateErrorReporter::SendExtendedReportingReport(
     const std::string& serialized_report,
-    const base::Callback<void()>& success_callback,
-    const base::Callback<void(const GURL&, int, int)>& error_callback) {
-  if (upload_url_.SchemeIsCryptographic()) {
-    certificate_report_sender_->Send(upload_url_, "application/octet-stream",
-                                     serialized_report, success_callback,
-                                     error_callback);
-    return;
-  }
-  encrypted_messages::EncryptedMessage encrypted_report;
-  // By mistake, the HKDF label here ends up with an extra null byte on
-  // the end, due to using sizeof(kHkdfLabel) in the StringPiece
-  // constructor instead of strlen(kHkdfLabel). This has since been changed
-  // to strlen() + 1, but will need to be fixed in future to just be strlen.
-  // TODO(estark): fix this...
-  //  https://crbug.com/517746
-  if (!encrypted_messages::EncryptSerializedMessage(
-          server_public_key_, server_public_key_version_,
-          base::StringPiece(kHkdfLabel, strlen(kHkdfLabel) + 1),
-          serialized_report, &encrypted_report)) {
-    LOG(ERROR) << "Failed to encrypt serialized report.";
-    return;
-  }
+    base::OnceCallback<void()> success_callback,
+    base::OnceCallback<void(int, int)> error_callback) {
   std::string serialized_encrypted_report;
-  encrypted_report.SerializeToString(&serialized_encrypted_report);
-  certificate_report_sender_->Send(upload_url_, "application/octet-stream",
-                                   serialized_encrypted_report,
-                                   success_callback, error_callback);
+  const std::string* string_to_send = &serialized_report;
+  if (!upload_url_.SchemeIsCryptographic()) {
+    encrypted_messages::EncryptedMessage encrypted_report;
+    // By mistake, the HKDF label here ends up with an extra null byte on
+    // the end, due to using sizeof(kHkdfLabel) in the StringPiece
+    // constructor instead of strlen(kHkdfLabel). This has since been changed
+    // to strlen() + 1, but will need to be fixed in future to just be strlen.
+    // TODO(estark): fix this...
+    //  https://crbug.com/517746
+    if (!encrypted_messages::EncryptSerializedMessage(
+            server_public_key_, server_public_key_version_,
+            base::StringPiece(kHkdfLabel, strlen(kHkdfLabel) + 1),
+            serialized_report, &encrypted_report)) {
+      LOG(ERROR) << "Failed to encrypt serialized report.";
+      return;
+    }
+
+    encrypted_report.SerializeToString(&serialized_encrypted_report);
+    string_to_send = &serialized_encrypted_report;
+  }
+
+  SendReport(url_loader_factory_,
+             kSafeBrowsingCertificateErrorReportingTrafficAnnotation,
+             upload_url_, "application/octet-stream", *string_to_send,
+             std::move(success_callback), std::move(error_callback));
 }
diff --git a/chrome/browser/ssl/certificate_error_reporter.h b/chrome/browser/ssl/certificate_error_reporter.h
index 9a06275..f9f8832 100644
--- a/chrome/browser/ssl/certificate_error_reporter.h
+++ b/chrome/browser/ssl/certificate_error_reporter.h
@@ -13,11 +13,11 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "net/url_request/report_sender.h"
+#include "base/memory/ref_counted.h"
 #include "url/gurl.h"
 
-namespace net {
-class URLRequestContext;
+namespace network {
+class SharedURLLoaderFactory;
 }
 
 // Provides functionality for sending reports about invalid SSL
@@ -25,19 +25,19 @@
 class CertificateErrorReporter {
  public:
   // Creates a certificate error reporter that will send certificate
-  // error reports to |upload_url|, using |request_context| as the
+  // error reports to |upload_url|, using |url_loader_factory| as the
   // context for the reports.
-  CertificateErrorReporter(net::URLRequestContext* request_context,
-                           const GURL& upload_url);
-
-  // Allows tests to use a server public key with known private key and
-  // a mock ReportSender. |server_public_key| must outlive
-  // the ErrorReporter.
   CertificateErrorReporter(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const GURL& upload_url);
+
+  // Allows tests to use a server public key with known private key.
+  // |server_public_key| must outlive the ErrorReporter.
+  CertificateErrorReporter(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const GURL& upload_url,
       const uint8_t server_public_key[/* 32 */],
-      const uint32_t server_public_key_version,
-      std::unique_ptr<net::ReportSender> certificate_report_sender);
+      const uint32_t server_public_key_version);
 
   virtual ~CertificateErrorReporter();
 
@@ -63,16 +63,14 @@
   // net error and HTTP response code parameters.
   virtual void SendExtendedReportingReport(
       const std::string& serialized_report,
-      const base::Callback<void()>& success_callback,
-      const base::Callback<void(const GURL&,
-                                int /* net_error */,
-                                int /* http_response_code */)>& error_callback);
+      base::OnceCallback<void()> success_callback,
+      base::OnceCallback<void(int /* net_error */,
+                              int /* http_response_code */)> error_callback);
 
   void set_upload_url_for_testing(const GURL& url) { upload_url_ = url; }
 
  private:
-  std::unique_ptr<net::ReportSender> certificate_report_sender_;
-
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   GURL upload_url_;
 
   const uint8_t* server_public_key_;
diff --git a/chrome/browser/ssl/certificate_error_reporter_unittest.cc b/chrome/browser/ssl/certificate_error_reporter_unittest.cc
index a0f9ba3..1cd8fe4 100644
--- a/chrome/browser/ssl/certificate_error_reporter_unittest.cc
+++ b/chrome/browser/ssl/certificate_error_reporter_unittest.cc
@@ -17,14 +17,14 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
+#include "base/test/bind_test_util.h"
 #include "components/encrypted_messages/encrypted_message.pb.h"
 #include "components/encrypted_messages/message_encrypter.h"
+#include "content/public/common/weak_wrapper_shared_url_loader_factory.h"
 #include "net/http/http_status_code.h"
-#include "net/test/url_request/url_request_failed_job.h"
-#include "net/test/url_request/url_request_mock_data_job.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/report_sender.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/boringssl/src/include/openssl/curve25519.h"
 
@@ -36,79 +36,12 @@
 const char kDummyReport[] = "a dummy report";
 const uint32_t kServerPublicKeyTestVersion = 16;
 
-void ErrorCallback(bool* called,
-                   const GURL& report_uri,
-                   int net_error,
-                   int http_response_code) {
-  EXPECT_NE(net::OK, net_error);
-  EXPECT_EQ(-1, http_response_code);
-  *called = true;
-}
-
-void SuccessCallback(bool* called) {
-  *called = true;
-}
-
-// A mock ReportSender that keeps track of the last report
-// sent.
-class MockCertificateReportSender : public net::ReportSender {
- public:
-  MockCertificateReportSender()
-      : net::ReportSender(nullptr, TRAFFIC_ANNOTATION_FOR_TESTS) {}
-  ~MockCertificateReportSender() override {}
-
-  void Send(const GURL& report_uri,
-            base::StringPiece content_type,
-            base::StringPiece report,
-            const base::Callback<void()>& success_callback,
-            const base::Callback<void(const GURL&, int, int)>& error_callback)
-      override {
-    latest_report_uri_ = report_uri;
-    report.CopyToString(&latest_report_);
-    content_type.CopyToString(&latest_content_type_);
-  }
-
-  const GURL& latest_report_uri() const { return latest_report_uri_; }
-
-  const std::string& latest_report() const { return latest_report_; }
-
-  const std::string& latest_content_type() const {
-    return latest_content_type_;
-  }
-
- private:
-  GURL latest_report_uri_;
-  std::string latest_report_;
-  std::string latest_content_type_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockCertificateReportSender);
-};
-
-// A test network delegate that allows the user to specify a callback to
-// be run whenever a net::URLRequest is destroyed.
-class TestCertificateReporterNetworkDelegate : public net::NetworkDelegateImpl {
- public:
-  TestCertificateReporterNetworkDelegate()
-      : url_request_destroyed_callback_(base::DoNothing()) {}
-
-  void set_url_request_destroyed_callback(const base::Closure& callback) {
-    url_request_destroyed_callback_ = callback;
-  }
-
-  // net::NetworkDelegateImpl:
-  void OnURLRequestDestroyed(net::URLRequest* request) override {
-    url_request_destroyed_callback_.Run();
-  }
-
- private:
-  base::Closure url_request_destroyed_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestCertificateReporterNetworkDelegate);
-};
-
 class ErrorReporterTest : public ::testing::Test {
  public:
-  ErrorReporterTest() {
+  ErrorReporterTest()
+      : test_shared_loader_factory_(
+            base::MakeRefCounted<content::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)) {
     memset(server_private_key_, 1, sizeof(server_private_key_));
     X25519_public_from_private(server_public_key_, server_private_key_);
   }
@@ -120,44 +53,57 @@
   uint8_t server_public_key_[32];
   uint8_t server_private_key_[32];
 
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ErrorReporterTest);
 };
 
 // Test that ErrorReporter::SendExtendedReportingReport sends
 // an encrypted or plaintext extended reporting report as appropriate.
 TEST_F(ErrorReporterTest, ExtendedReportingSendReport) {
+  GURL latest_report_uri;
+  std::string latest_report;
+  std::string latest_content_type;
+
+  test_url_loader_factory_.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        latest_report_uri = request.url;
+        request.headers.GetHeader(net::HttpRequestHeaders::kContentType,
+                                  &latest_content_type);
+        auto body = request.request_body;
+        CHECK_EQ(1u, body->elements()->size());
+        auto& element = body->elements()->at(0);
+        CHECK_EQ(network::DataElement::TYPE_BYTES, element.type());
+        latest_report = std::string(element.bytes(), element.length());
+      }));
+
   // Data should not be encrypted when sent to an HTTPS URL.
-  MockCertificateReportSender* mock_report_sender =
-      new MockCertificateReportSender();
-  GURL https_url(kDummyHttpsReportUri);
-  CertificateErrorReporter https_reporter(https_url, server_public_key_,
-                                          kServerPublicKeyTestVersion,
-                                          base::WrapUnique(mock_report_sender));
+  const GURL https_url(kDummyHttpsReportUri);
+  CertificateErrorReporter https_reporter(test_shared_loader_factory_,
+                                          https_url, server_public_key_,
+                                          kServerPublicKeyTestVersion);
   https_reporter.SendExtendedReportingReport(
-      kDummyReport, base::Callback<void()>(),
-      base::Callback<void(const GURL&, int, int)>());
-  EXPECT_EQ(mock_report_sender->latest_report_uri(), https_url);
-  EXPECT_EQ(mock_report_sender->latest_report(), kDummyReport);
+      kDummyReport, base::OnceCallback<void()>(),
+      base::OnceCallback<void(int, int)>());
+  EXPECT_EQ(latest_report_uri, https_url);
+  EXPECT_EQ(latest_report, kDummyReport);
 
   // Data should be encrypted when sent to an HTTP URL.
-  MockCertificateReportSender* http_mock_report_sender =
-      new MockCertificateReportSender();
   const GURL http_url(kDummyHttpReportUri);
-  CertificateErrorReporter http_reporter(
-      http_url, server_public_key_, kServerPublicKeyTestVersion,
-      base::WrapUnique(http_mock_report_sender));
+  CertificateErrorReporter http_reporter(test_shared_loader_factory_, http_url,
+                                         server_public_key_,
+                                         kServerPublicKeyTestVersion);
   http_reporter.SendExtendedReportingReport(
-      kDummyReport, base::Callback<void()>(),
-      base::Callback<void(const GURL&, int, int)>());
+      kDummyReport, base::OnceCallback<void()>(),
+      base::OnceCallback<void(int, int)>());
 
-  EXPECT_EQ(http_mock_report_sender->latest_report_uri(), http_url);
-  EXPECT_EQ("application/octet-stream",
-            http_mock_report_sender->latest_content_type());
+  EXPECT_EQ(latest_report_uri, http_url);
+  EXPECT_EQ("application/octet-stream", latest_content_type);
 
   std::string uploaded_report;
   encrypted_messages::EncryptedMessage encrypted_report;
-  ASSERT_TRUE(encrypted_report.ParseFromString(
-      http_mock_report_sender->latest_report()));
+  ASSERT_TRUE(encrypted_report.ParseFromString(latest_report));
   EXPECT_EQ(kServerPublicKeyTestVersion,
             encrypted_report.server_public_key_version());
   EXPECT_EQ(
@@ -177,54 +123,37 @@
 
 // Tests that an UMA histogram is recorded if a report fails to send.
 TEST_F(ErrorReporterTest, ErroredRequestCallsCallback) {
-  net::URLRequestFailedJob::AddUrlHandler();
-
   base::RunLoop run_loop;
-  net::TestURLRequestContext context(true);
-  TestCertificateReporterNetworkDelegate test_delegate;
-  test_delegate.set_url_request_destroyed_callback(run_loop.QuitClosure());
-  context.set_network_delegate(&test_delegate);
-  context.Init();
 
-  const GURL report_uri(
-      net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_FAILED));
-  CertificateErrorReporter reporter(&context, report_uri);
+  const GURL report_uri("http://foo.com/bar");
 
-  bool error_callback_called = false;
-  bool success_callback_called = false;
+  test_url_loader_factory_.AddResponse(
+      report_uri, network::ResourceResponseHead(), std::string(),
+      network::URLLoaderCompletionStatus(net::ERR_CONNECTION_FAILED));
+
+  CertificateErrorReporter reporter(test_shared_loader_factory_, report_uri);
+
   reporter.SendExtendedReportingReport(
-      kDummyReport, base::Bind(&SuccessCallback, &success_callback_called),
-      base::Bind(&ErrorCallback, &error_callback_called));
+      kDummyReport, base::BindLambdaForTesting([&]() { FAIL(); }),
+      base::BindLambdaForTesting(
+          [&](int net_error, int http_response_code) { run_loop.Quit(); }));
   run_loop.Run();
-
-  EXPECT_TRUE(error_callback_called);
-  EXPECT_FALSE(success_callback_called);
 }
 
 // Tests that an UMA histogram is recorded if a report is successfully sent.
 TEST_F(ErrorReporterTest, SuccessfulRequestCallsCallback) {
-  net::URLRequestMockDataJob::AddUrlHandler();
-
   base::RunLoop run_loop;
-  net::TestURLRequestContext context(true);
-  TestCertificateReporterNetworkDelegate test_delegate;
-  test_delegate.set_url_request_destroyed_callback(run_loop.QuitClosure());
-  context.set_network_delegate(&test_delegate);
-  context.Init();
 
-  const GURL report_uri(
-      net::URLRequestMockDataJob::GetMockHttpUrl("some data", 1));
-  CertificateErrorReporter reporter(&context, report_uri);
+  const GURL report_uri("http://foo.com/bar");
+  test_url_loader_factory_.AddResponse(report_uri.spec(), "some data");
 
-  bool error_callback_called = false;
-  bool success_callback_called = false;
+  CertificateErrorReporter reporter(test_shared_loader_factory_, report_uri);
+
   reporter.SendExtendedReportingReport(
-      kDummyReport, base::Bind(&SuccessCallback, &success_callback_called),
-      base::Bind(&ErrorCallback, &error_callback_called));
+      kDummyReport, base::BindLambdaForTesting([&]() { run_loop.Quit(); }),
+      base::BindLambdaForTesting(
+          [&](int net_error, int http_response_code) { FAIL(); }));
   run_loop.Run();
-
-  EXPECT_FALSE(error_callback_called);
-  EXPECT_TRUE(success_callback_called);
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/glue/synced_window_delegates_getter_android.cc b/chrome/browser/sync/glue/synced_window_delegates_getter_android.cc
index 18591fb..006c576 100644
--- a/chrome/browser/sync/glue/synced_window_delegates_getter_android.cc
+++ b/chrome/browser/sync/glue/synced_window_delegates_getter_android.cc
@@ -27,7 +27,7 @@
 
 const SyncedWindowDelegate* SyncedWindowDelegatesGetterAndroid::FindById(
     SessionID session_id) {
-  TabModel* tab_model = TabModelList::FindTabModelWithId(session_id.id());
+  TabModel* tab_model = TabModelList::FindTabModelWithId(session_id);
 
   // In case we don't find the browser (e.g. for Developer Tools).
   return tab_model ? tab_model->GetSyncedWindowDelegate() : nullptr;
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index b2b1764..ecdf5ab 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/signin/about_signin_internals_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/spellchecker/spellcheck_factory.h"
@@ -137,6 +138,7 @@
   DependsOn(ThemeServiceFactory::GetInstance());
 #endif  // !defined(OS_ANDROID)
   DependsOn(HistoryServiceFactory::GetInstance());
+  DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(invalidation::ProfileInvalidationProviderFactory::GetInstance());
   DependsOn(PasswordStoreFactory::GetInstance());
   DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
@@ -218,10 +220,6 @@
 #endif  // defined(OS_WIN)
 
   if (!local_sync_backend_enabled) {
-    SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile);
-    SigninClient* signin_client =
-        ChromeSigninClientFactory::GetForProfile(profile);
-
     // Always create the GCMProfileService instance such that we can listen to
     // the profile notifications and purge the GCM store when the profile is
     // being signed out.
@@ -232,13 +230,15 @@
     AboutSigninInternalsFactory::GetForProfile(profile);
 
     init_params.signin_wrapper =
-        std::make_unique<SupervisedUserSigninManagerWrapper>(profile, signin);
+        std::make_unique<SupervisedUserSigninManagerWrapper>(
+            profile, IdentityManagerFactory::GetForProfile(profile),
+            SigninManagerFactory::GetForProfile(profile));
     // Note: base::Unretained(signin_client) is safe because the SigninClient is
     // guaranteed to outlive the PSS, per a DependsOn() above (and because PSS
     // clears the callback in its Shutdown()).
-    init_params.signin_scoped_device_id_callback =
-        base::BindRepeating(&SigninClient::GetSigninScopedDeviceId,
-                            base::Unretained(signin_client));
+    init_params.signin_scoped_device_id_callback = base::BindRepeating(
+        &SigninClient::GetSigninScopedDeviceId,
+        base::Unretained(ChromeSigninClientFactory::GetForProfile(profile)));
     init_params.oauth2_token_service =
         ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
     init_params.gaia_cookie_manager_service =
diff --git a/chrome/browser/sync/profile_sync_test_util.cc b/chrome/browser/sync/profile_sync_test_util.cc
index d3930f21..fe02d73 100644
--- a/chrome/browser/sync/profile_sync_test_util.cc
+++ b/chrome/browser/sync/profile_sync_test_util.cc
@@ -11,6 +11,7 @@
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/chrome_sync_client.h"
@@ -45,6 +46,7 @@
   ProfileSyncService::InitParams init_params;
 
   init_params.signin_wrapper = std::make_unique<SigninManagerWrapper>(
+      IdentityManagerFactory::GetForProfile(profile),
       SigninManagerFactory::GetForProfile(profile));
   init_params.signin_scoped_device_id_callback =
       base::BindRepeating([]() { return std::string(); });
diff --git a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
index 62fc3f1..6836374 100644
--- a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
+++ b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
@@ -74,8 +74,7 @@
 
 void SyncSessionsRouterTabHelper::SetSourceTabIdForChild(
     content::WebContents* child_contents) {
-  SessionID source_tab_id = SessionID::FromSerializedValue(
-      SessionTabHelper::IdForTab(web_contents()));
+  SessionID source_tab_id = SessionTabHelper::IdForTab(web_contents());
   if (child_contents &&
       SyncSessionsRouterTabHelper::FromWebContents(child_contents) &&
       child_contents != web_contents() && source_tab_id.is_valid()) {
diff --git a/chrome/browser/sync/supervised_user_signin_manager_wrapper.cc b/chrome/browser/sync/supervised_user_signin_manager_wrapper.cc
index 1d1ac6e..1724f679 100644
--- a/chrome/browser/sync/supervised_user_signin_manager_wrapper.cc
+++ b/chrome/browser/sync/supervised_user_signin_manager_wrapper.cc
@@ -15,8 +15,10 @@
 
 SupervisedUserSigninManagerWrapper::SupervisedUserSigninManagerWrapper(
     Profile* profile,
-    SigninManagerBase* original)
-    : SigninManagerWrapper(original), profile_(profile) {}
+    identity::IdentityManager* identity_manager,
+    SigninManagerBase* signin_manager)
+    : SigninManagerWrapper(identity_manager, signin_manager),
+      profile_(profile) {}
 
 SupervisedUserSigninManagerWrapper::~SupervisedUserSigninManagerWrapper() {}
 
diff --git a/chrome/browser/sync/supervised_user_signin_manager_wrapper.h b/chrome/browser/sync/supervised_user_signin_manager_wrapper.h
index 6d49080..9233d75 100644
--- a/chrome/browser/sync/supervised_user_signin_manager_wrapper.h
+++ b/chrome/browser/sync/supervised_user_signin_manager_wrapper.h
@@ -13,6 +13,10 @@
 class Profile;
 class SigninManagerBase;
 
+namespace identity {
+class IdentityManager;
+}
+
 // Some chrome cloud services support supervised users as well as normally
 // authenticated users that sign in through SigninManager.  To facilitate
 // getting the "effective" username and account identifiers, services can
@@ -20,8 +24,10 @@
 // information when appropriate.
 class SupervisedUserSigninManagerWrapper : public SigninManagerWrapper {
  public:
-  SupervisedUserSigninManagerWrapper(Profile* profile,
-                                     SigninManagerBase* original);
+  SupervisedUserSigninManagerWrapper(
+      Profile* profile,
+      identity::IdentityManager* identity_manager,
+      SigninManagerBase* signin_manager);
   ~SupervisedUserSigninManagerWrapper() override;
 
   // SigninManagerWrapper implementation
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index 3872aeac..26ec0db4 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -370,12 +370,11 @@
   content::WebContents* new_tab_contents =
       GetBrowser(0)->tab_strip_model()->GetWebContentsAt(1);
 
-  SessionID::id_type source_tab_id =
-      SessionTabHelper::IdForTab(original_tab_contents);
+  SessionID source_tab_id = SessionTabHelper::IdForTab(original_tab_contents);
   sync_sessions::SyncSessionsRouterTabHelper* new_tab_helper =
       sync_sessions::SyncSessionsRouterTabHelper::FromWebContents(
           new_tab_contents);
-  EXPECT_EQ(new_tab_helper->source_tab_id().id(), source_tab_id);
+  EXPECT_EQ(new_tab_helper->source_tab_id(), source_tab_id);
 }
 
 void DumpSessionsOnServer(fake_server::FakeServer* fake_server) {
diff --git a/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc b/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc
index 9af40935..2bc257d 100644
--- a/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc
+++ b/chrome/browser/task_manager/providers/fallback_task_provider_unittest.cc
@@ -33,7 +33,7 @@
 
   const Task* GetParentTask() const override { return nullptr; }
 
-  int GetTabId() const override { return 0; }
+  SessionID GetTabId() const override { return SessionID::InvalidValue(); }
 
  private:
   Type type_;
diff --git a/chrome/browser/task_manager/providers/task.cc b/chrome/browser/task_manager/providers/task.cc
index d8905b8..9ff36e9c 100644
--- a/chrome/browser/task_manager/providers/task.cc
+++ b/chrome/browser/task_manager/providers/task.cc
@@ -147,8 +147,8 @@
   return base::string16();
 }
 
-int Task::GetTabId() const {
-  return -1;
+SessionID Task::GetTabId() const {
+  return SessionID::InvalidValue();
 }
 
 bool Task::HasParentTask() const {
diff --git a/chrome/browser/task_manager/providers/task.h b/chrome/browser/task_manager/providers/task.h
index 6e493bafc..e92eeee 100644
--- a/chrome/browser/task_manager/providers/task.h
+++ b/chrome/browser/task_manager/providers/task.h
@@ -14,6 +14,7 @@
 #include "base/process/process_handle.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "components/sessions/core/session_id.h"
 #include "third_party/WebKit/public/platform/WebCache.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -121,9 +122,9 @@
   virtual base::string16 GetProfileName() const;
 
   // Returns the unique ID of the tab if this task represents a renderer
-  // WebContents used for a tab. Returns -1 if this task does not represent
-  // a renderer, or a contents of a tab.
-  virtual int GetTabId() const;
+  // WebContents used for a tab. Returns SessionID::InvalidValue() if this task
+  // does not represent a renderer, or a contents of a tab.
+  virtual SessionID GetTabId() const;
 
   // For Tasks that represent a subactivity of some other task (e.g. a plugin
   // embedded in a page), this returns the Task representing the parent
diff --git a/chrome/browser/task_manager/providers/web_contents/renderer_task.cc b/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
index b8da268c..21849f8 100644
--- a/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
+++ b/chrome/browser/task_manager/providers/web_contents/renderer_task.cc
@@ -158,7 +158,7 @@
   return profile_name_;
 }
 
-int RendererTask::GetTabId() const {
+SessionID RendererTask::GetTabId() const {
   return SessionTabHelper::IdForTab(web_contents_);
 }
 
diff --git a/chrome/browser/task_manager/providers/web_contents/renderer_task.h b/chrome/browser/task_manager/providers/web_contents/renderer_task.h
index 2fbf6149..2ae37c46 100644
--- a/chrome/browser/task_manager/providers/web_contents/renderer_task.h
+++ b/chrome/browser/task_manager/providers/web_contents/renderer_task.h
@@ -62,7 +62,7 @@
   void GetTerminationStatus(base::TerminationStatus* out_status,
                             int* out_error_code) const override;
   base::string16 GetProfileName() const override;
-  int GetTabId() const override;
+  SessionID GetTabId() const override;
   int64_t GetV8MemoryAllocated() const override;
   int64_t GetV8MemoryUsed() const override;
   bool ReportsWebCacheStats() const override;
diff --git a/chrome/browser/task_manager/sampling/task_group_unittest.cc b/chrome/browser/task_manager/sampling/task_group_unittest.cc
index ac6e0d7..9f0fcbc 100644
--- a/chrome/browser/task_manager/sampling/task_group_unittest.cc
+++ b/chrome/browser/task_manager/sampling/task_group_unittest.cc
@@ -42,7 +42,7 @@
 
   const Task* GetParentTask() const override { return nullptr; }
 
-  int GetTabId() const override { return 0; }
+  SessionID GetTabId() const override { return SessionID::InvalidValue(); }
 
  private:
   Type type_;
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.cc b/chrome/browser/task_manager/sampling/task_manager_impl.cc
index c9315271..7b1c342 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.cc
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.cc
@@ -232,7 +232,7 @@
   return GetTaskByTaskId(task_id)->GetType();
 }
 
-int TaskManagerImpl::GetTabId(TaskId task_id) const {
+SessionID TaskManagerImpl::GetTabId(TaskId task_id) const {
   return GetTaskByTaskId(task_id)->GetTabId();
 }
 
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.h b/chrome/browser/task_manager/sampling/task_manager_impl.h
index 7e31f64..dd64db2 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl.h
+++ b/chrome/browser/task_manager/sampling/task_manager_impl.h
@@ -68,7 +68,7 @@
   const base::ProcessHandle& GetProcessHandle(TaskId task_id) const override;
   const base::ProcessId& GetProcessId(TaskId task_id) const override;
   Task::Type GetType(TaskId task_id) const override;
-  int GetTabId(TaskId task_id) const override;
+  SessionID GetTabId(TaskId task_id) const override;
   int GetChildProcessUniqueId(TaskId task_id) const override;
   void GetTerminationStatus(TaskId task_id,
                             base::TerminationStatus* out_status,
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc b/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc
index 52a2dee..e513926f 100644
--- a/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc
+++ b/chrome/browser/task_manager/sampling/task_manager_impl_unittest.cc
@@ -25,7 +25,7 @@
   FakeTask(base::ProcessId process_id,
            Type type,
            const std::string& title,
-           int tab_id)
+           SessionID tab_id)
       : Task(base::ASCIIToUTF16(title),
              "FakeTask",
              nullptr,
@@ -45,14 +45,14 @@
 
   const Task* GetParentTask() const override { return parent_; }
 
-  int GetTabId() const override { return tab_id_; }
+  SessionID GetTabId() const override { return tab_id_; }
 
   void SetParent(Task* parent) { parent_ = parent; }
 
  private:
   Type type_;
   Task* parent_;
-  int tab_id_;
+  SessionID tab_id_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeTask);
 };
@@ -74,7 +74,7 @@
   FakeTask* AddTask(int pid_offset,
                     Task::Type type,
                     const std::string& title,
-                    int tab_id) {
+                    SessionID tab_id) {
     // Offset based on the current process id, to avoid collisions with the
     // browser process task.
     base::ProcessId process_id = base::GetCurrentProcId() + pid_offset;
@@ -98,31 +98,38 @@
 };
 
 TEST_F(TaskManagerImplTest, SortingTypes) {
-  AddTask(100, Task::GPU, "Gpu Process", -1);
+  constexpr SessionID kTabId1 = SessionID::FromSerializedValue(10);
+  constexpr SessionID kTabId2 = SessionID::FromSerializedValue(20);
 
-  Task* tab1 = AddTask(200, Task::RENDERER, "Tab One", 10);
-  AddTask(400, Task::EXTENSION, "Extension Subframe: Tab One", 10)
+  AddTask(100, Task::GPU, "Gpu Process", /*tab_id=*/SessionID::InvalidValue());
+
+  Task* tab1 = AddTask(200, Task::RENDERER, "Tab One", kTabId1);
+  AddTask(400, Task::EXTENSION, "Extension Subframe: Tab One", kTabId1)
       ->SetParent(tab1);
-  AddTask(300, Task::RENDERER, "Subframe: Tab One", 10)->SetParent(tab1);
+  AddTask(300, Task::RENDERER, "Subframe: Tab One", kTabId1)->SetParent(tab1);
 
-  Task* tab2 =
-      AddTask(200, Task::RENDERER, "Tab Two: sharing process with Tab One", 20);
+  Task* tab2 = AddTask(200, Task::RENDERER,
+                       "Tab Two: sharing process with Tab One", kTabId2);
 
-  AddTask(301, Task::RENDERER, "Subframe: Tab Two", 20)->SetParent(tab2);
-  AddTask(400, Task::EXTENSION, "Extension Subframe: Tab Two", 20)
+  AddTask(301, Task::RENDERER, "Subframe: Tab Two", kTabId2)->SetParent(tab2);
+  AddTask(400, Task::EXTENSION, "Extension Subframe: Tab Two", kTabId2)
       ->SetParent(tab2);
 
-  AddTask(600, Task::ARC, "ARC", -1);
-  AddTask(800, Task::UTILITY, "Utility One", -1);
-  AddTask(700, Task::UTILITY, "Utility Two", -1);
-  AddTask(1000, Task::GUEST, "Guest", 20);
-  AddTask(900, Task::WORKER, "Worker", -1);
-  AddTask(500, Task::ZYGOTE, "Zygote", -1);
+  AddTask(600, Task::ARC, "ARC", /*tab_id=*/SessionID::InvalidValue());
+  AddTask(800, Task::UTILITY, "Utility One",
+          /*tab_id=*/SessionID::InvalidValue());
+  AddTask(700, Task::UTILITY, "Utility Two",
+          /*tab_id=*/SessionID::InvalidValue());
+  AddTask(1000, Task::GUEST, "Guest", kTabId2);
+  AddTask(900, Task::WORKER, "Worker", /*tab_id=*/SessionID::InvalidValue());
+  AddTask(500, Task::ZYGOTE, "Zygote", /*tab_id=*/SessionID::InvalidValue());
 
-  AddTask(300, Task::RENDERER, "Subframe: Tab One (2)", 10)->SetParent(tab1);
-  AddTask(300, Task::RENDERER, "Subframe: Tab One (third)", 10)
+  AddTask(300, Task::RENDERER, "Subframe: Tab One (2)", kTabId1)
       ->SetParent(tab1);
-  AddTask(300, Task::RENDERER, "Subframe: Tab One (4)", 10)->SetParent(tab1);
+  AddTask(300, Task::RENDERER, "Subframe: Tab One (third)", kTabId1)
+      ->SetParent(tab1);
+  AddTask(300, Task::RENDERER, "Subframe: Tab One (4)", kTabId1)
+      ->SetParent(tab1);
 
   EXPECT_EQ(
       "Browser\n"
@@ -146,49 +153,60 @@
 }
 
 TEST_F(TaskManagerImplTest, SortingCycles) {
+  constexpr SessionID kTabId1 = SessionID::FromSerializedValue(10);
+  constexpr SessionID kTabId2 = SessionID::FromSerializedValue(20);
+  constexpr SessionID kTabId3 = SessionID::FromSerializedValue(5);
+  constexpr SessionID kTabId4 = SessionID::FromSerializedValue(30);
+
   // Two tabs, with subframes in the other's process. This induces a cycle in
   // the TaskGroup dependencies, without being a cycle in the Tasks. This can
   // happen in practice.
-  Task* tab1 = AddTask(200, Task::RENDERER, "Tab 1: Process 200", 10);
-  AddTask(300, Task::RENDERER, "Subframe in Tab 1: Process 300", 10)
+  Task* tab1 = AddTask(200, Task::RENDERER, "Tab 1: Process 200", kTabId1);
+  AddTask(300, Task::RENDERER, "Subframe in Tab 1: Process 300", kTabId1)
       ->SetParent(tab1);
-  Task* tab2 = AddTask(300, Task::RENDERER, "Tab 2: Process 300", 20);
-  AddTask(200, Task::RENDERER, "Subframe in Tab 2: Process 200", 20)
+  Task* tab2 = AddTask(300, Task::RENDERER, "Tab 2: Process 300", kTabId2);
+  AddTask(200, Task::RENDERER, "Subframe in Tab 2: Process 200", kTabId2)
       ->SetParent(tab2);
 
   // Simulated GPU process.
-  AddTask(100, Task::GPU, "Gpu Process", -1);
+  AddTask(100, Task::GPU, "Gpu Process", /*tab_id=*/SessionID::InvalidValue());
 
   // Two subframes that list each other as a parent (a true cycle). This
   // shouldn't happen in practice, but we want the sorting code to handle it
   // gracefully.
-  FakeTask* cycle1 = AddTask(501, Task::SANDBOX_HELPER, "Cycle 1", -1);
-  FakeTask* cycle2 = AddTask(500, Task::ARC, "Cycle 2", -1);
+  FakeTask* cycle1 = AddTask(501, Task::SANDBOX_HELPER, "Cycle 1",
+                             /*tab_id=*/SessionID::InvalidValue());
+  FakeTask* cycle2 =
+      AddTask(500, Task::ARC, "Cycle 2", /*tab_id=*/SessionID::InvalidValue());
   cycle1->SetParent(cycle2);
   cycle2->SetParent(cycle1);
 
   // A cycle where both elements are in the same group.
-  FakeTask* cycle3 = AddTask(600, Task::SANDBOX_HELPER, "Cycle 3", -1);
-  FakeTask* cycle4 = AddTask(600, Task::ARC, "Cycle 4", -1);
+  FakeTask* cycle3 = AddTask(600, Task::SANDBOX_HELPER, "Cycle 3",
+                             /*tab_id=*/SessionID::InvalidValue());
+  FakeTask* cycle4 =
+      AddTask(600, Task::ARC, "Cycle 4", /*tab_id=*/SessionID::InvalidValue());
   cycle3->SetParent(cycle4);
   cycle4->SetParent(cycle3);
 
   // Tasks listing a cycle as their parent.
-  FakeTask* lollipop5 = AddTask(701, Task::EXTENSION, "Child of Cycle 3", -1);
+  FakeTask* lollipop5 = AddTask(701, Task::EXTENSION, "Child of Cycle 3",
+                                /*tab_id=*/SessionID::InvalidValue());
   lollipop5->SetParent(cycle3);
-  FakeTask* lollipop6 = AddTask(700, Task::PLUGIN, "Child of Cycle 4", -1);
+  FakeTask* lollipop6 = AddTask(700, Task::PLUGIN, "Child of Cycle 4",
+                                /*tab_id=*/SessionID::InvalidValue());
   lollipop6->SetParent(cycle4);
 
   // A task listing itself as parent.
-  FakeTask* self_cycle = AddTask(800, Task::RENDERER, "Self Cycle", 5);
+  FakeTask* self_cycle = AddTask(800, Task::RENDERER, "Self Cycle", kTabId3);
   self_cycle->SetParent(self_cycle);
 
   // Add a plugin child to tab1 and tab2.
-  AddTask(900, Task::PLUGIN, "Plugin: Tab 2", 20)->SetParent(tab1);
-  AddTask(901, Task::PLUGIN, "Plugin: Tab 1", 10)->SetParent(tab1);
+  AddTask(900, Task::PLUGIN, "Plugin: Tab 2", kTabId2)->SetParent(tab1);
+  AddTask(901, Task::PLUGIN, "Plugin: Tab 1", kTabId1)->SetParent(tab1);
 
   // Finish with a normal renderer task.
-  AddTask(903, Task::RENDERER, "Tab: Normal Renderer", 30);
+  AddTask(903, Task::RENDERER, "Tab: Normal Renderer", kTabId4);
 
   // Cycles should wind up on the bottom of the list.
   EXPECT_EQ(
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
index 9f237c33..6c6c754 100644
--- a/chrome/browser/task_manager/task_manager_browsertest.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -268,7 +268,7 @@
   // Killing the tab via task manager should remove the row.
   int tab = FindResourceIndex(MatchTab("title1.html"));
   ASSERT_NE(-1, tab);
-  ASSERT_NE(-1, model()->GetTabId(tab));
+  ASSERT_TRUE(model()->GetTabId(tab).is_valid());
   model()->Kill(tab);
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchTab("title1.html")));
   ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
@@ -365,11 +365,11 @@
 
   int extension_tab = FindResourceIndex(MatchExtension("Foobar"));
   ASSERT_NE(-1, extension_tab);
-  ASSERT_NE(-1, model()->GetTabId(extension_tab));
+  ASSERT_TRUE(model()->GetTabId(extension_tab).is_valid());
 
   int background_page = FindResourceIndex(MatchExtension("My extension 1"));
   ASSERT_NE(-1, background_page);
-  ASSERT_EQ(-1, model()->GetTabId(background_page));
+  ASSERT_FALSE(model()->GetTabId(background_page).is_valid());
 }
 
 IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeExtensionTab) {
@@ -395,11 +395,11 @@
 
   int extension_tab = FindResourceIndex(MatchExtension("Foobar"));
   ASSERT_NE(-1, extension_tab);
-  ASSERT_NE(-1, model()->GetTabId(extension_tab));
+  ASSERT_TRUE(model()->GetTabId(extension_tab).is_valid());
 
   int background_page = FindResourceIndex(MatchExtension("My extension 1"));
   ASSERT_NE(-1, background_page);
-  ASSERT_EQ(-1, model()->GetTabId(background_page));
+  ASSERT_FALSE(model()->GetTabId(background_page).is_valid());
 }
 
 IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeAppTabChanges) {
@@ -432,7 +432,7 @@
   // a tab contents and an extension.
   int app_tab = FindResourceIndex(MatchApp("Packaged App Test"));
   ASSERT_NE(-1, app_tab);
-  ASSERT_NE(-1, model()->GetTabId(app_tab));
+  ASSERT_TRUE(model()->GetTabId(app_tab).is_valid());
   ASSERT_EQ(2, browser()->tab_strip_model()->count());
 
   // Unload extension to make sure the tab goes away.
@@ -469,7 +469,7 @@
   // a tab contents and an extension.
   int app_tab = FindResourceIndex(MatchApp("Packaged App Test"));
   ASSERT_NE(-1, app_tab);
-  ASSERT_NE(-1, model()->GetTabId(app_tab));
+  ASSERT_TRUE(model()->GetTabId(app_tab).is_valid());
 }
 
 IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeHostedAppTabChanges) {
@@ -1011,7 +1011,7 @@
 
     int subframe_b = FindResourceIndex(MatchSubframe("http://b.com/"));
     ASSERT_NE(-1, subframe_b);
-    ASSERT_NE(-1, model()->GetTabId(subframe_b));
+    ASSERT_TRUE(model()->GetTabId(subframe_b).is_valid());
     model()->Kill(subframe_b);
 
     ASSERT_NO_FATAL_FAILURE(
diff --git a/chrome/browser/task_manager/task_manager_interface.h b/chrome/browser/task_manager/task_manager_interface.h
index 5893811..7aa902b 100644
--- a/chrome/browser/task_manager/task_manager_interface.h
+++ b/chrome/browser/task_manager/task_manager_interface.h
@@ -20,6 +20,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/task_manager/providers/task.h"
 #include "chrome/browser/task_manager/task_manager_observer.h"
+#include "components/sessions/core/session_id.h"
 #include "third_party/WebKit/public/platform/WebCache.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -159,7 +160,7 @@
 
   // Gets the unique ID of the tab if the task with |task_id| represents a
   // WebContents of a tab. Returns -1 otherwise.
-  virtual int GetTabId(TaskId task_id) const = 0;
+  virtual SessionID GetTabId(TaskId task_id) const = 0;
 
   // Returns the unique ID of the BrowserChildProcessHost/RenderProcessHost on
   // which the task with |task_id| is running. It is not the PID nor the handle
diff --git a/chrome/browser/task_manager/task_manager_tester.cc b/chrome/browser/task_manager/task_manager_tester.cc
index 4bd243ce..dfa95c6 100644
--- a/chrome/browser/task_manager/task_manager_tester.cc
+++ b/chrome/browser/task_manager/task_manager_tester.cc
@@ -172,7 +172,7 @@
   return value;
 }
 
-int32_t TaskManagerTester::GetTabId(int row) {
+SessionID TaskManagerTester::GetTabId(int row) {
   TaskId task_id = model_->tasks_[row];
   return task_manager()->GetTabId(task_id);
 }
diff --git a/chrome/browser/task_manager/task_manager_tester.h b/chrome/browser/task_manager/task_manager_tester.h
index d762beb..f1398c5 100644
--- a/chrome/browser/task_manager/task_manager_tester.h
+++ b/chrome/browser/task_manager/task_manager_tester.h
@@ -13,6 +13,7 @@
 #include "base/memory/memory_coordinator_client.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/task_manager/task_manager_browsertest_util.h"
+#include "components/sessions/core/session_id.h"
 
 namespace task_manager {
 
@@ -48,8 +49,8 @@
   int64_t GetColumnValue(ColumnSpecifier column, int row);
 
   // If |row| is associated with a WebContents, return its SessionID. Otherwise,
-  // return -1.
-  int32_t GetTabId(int row);
+  // return SessionID::InvalidValue().
+  SessionID GetTabId(int row);
 
   // Return the memory state of the process which is associated with |row|.
   base::MemoryState GetMemoryState(int row);
diff --git a/chrome/browser/task_manager/test_task_manager.cc b/chrome/browser/task_manager/test_task_manager.cc
index 4ff357b..b12d21f 100644
--- a/chrome/browser/task_manager/test_task_manager.cc
+++ b/chrome/browser/task_manager/test_task_manager.cc
@@ -112,8 +112,8 @@
   return Task::UNKNOWN;
 }
 
-int TestTaskManager::GetTabId(TaskId task_id) const {
-  return -1;
+SessionID TestTaskManager::GetTabId(TaskId task_id) const {
+  return SessionID::InvalidValue();
 }
 
 int TestTaskManager::GetChildProcessUniqueId(TaskId task_id) const {
diff --git a/chrome/browser/task_manager/test_task_manager.h b/chrome/browser/task_manager/test_task_manager.h
index f99892a..3bb9c01b 100644
--- a/chrome/browser/task_manager/test_task_manager.h
+++ b/chrome/browser/task_manager/test_task_manager.h
@@ -52,7 +52,7 @@
   const base::ProcessHandle& GetProcessHandle(TaskId task_id) const override;
   const base::ProcessId& GetProcessId(TaskId task_id) const override;
   Task::Type GetType(TaskId task_id) const override;
-  int GetTabId(TaskId task_id) const override;
+  SessionID GetTabId(TaskId task_id) const override;
   int GetChildProcessUniqueId(TaskId task_id) const override;
   void GetTerminationStatus(TaskId task_id,
                             base::TerminationStatus* out_status,
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 4c6ace2..4b92eba 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -347,6 +347,8 @@
       "cocoa/infobars/before_translate_infobar_controller.mm",
       "cocoa/infobars/confirm_infobar_controller.h",
       "cocoa/infobars/confirm_infobar_controller.mm",
+      "cocoa/infobars/infobar_background_view.h",
+      "cocoa/infobars/infobar_background_view.mm",
       "cocoa/infobars/infobar_cocoa.h",
       "cocoa/infobars/infobar_cocoa.mm",
       "cocoa/infobars/infobar_container_cocoa.h",
@@ -355,8 +357,6 @@
       "cocoa/infobars/infobar_container_controller.mm",
       "cocoa/infobars/infobar_controller.h",
       "cocoa/infobars/infobar_controller.mm",
-      "cocoa/infobars/infobar_gradient_view.h",
-      "cocoa/infobars/infobar_gradient_view.mm",
       "cocoa/infobars/infobar_utilities.h",
       "cocoa/infobars/infobar_utilities.mm",
       "cocoa/infobars/translate_infobar_base.h",
@@ -2865,6 +2865,8 @@
       "views/harmony/harmony_layout_provider.h",
       "views/harmony/harmony_typography_provider.cc",
       "views/harmony/harmony_typography_provider.h",
+      "views/harmony/material_refresh_layout_provider.cc",
+      "views/harmony/material_refresh_layout_provider.h",
       "views/harmony/textfield_layout.cc",
       "views/harmony/textfield_layout.h",
       "views/hover_button.cc",
@@ -3513,6 +3515,8 @@
       "app_list/search/arc/arc_playstore_search_result.h",
       "app_list/search/arc/icon_decode_request.cc",
       "app_list/search/arc/icon_decode_request.h",
+      "app_list/search/chrome_search_result.cc",
+      "app_list/search/chrome_search_result.h",
       "app_list/search/common/json_response_fetcher.cc",
       "app_list/search/common/json_response_fetcher.h",
       "app_list/search/common/url_icon_source.cc",
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
index fd30458..6afed5d7 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
@@ -127,15 +127,14 @@
   if (!tab_android)
     return nullptr;
 
-  TabModel* model = TabModelList::FindTabModelWithId(
-      tab_android->window_id().id());
+  TabModel* model = TabModelList::FindTabModelWithId(tab_android->window_id());
 
   return model ? model->GetLiveTabContext() : nullptr;
 }
 
 // static.
 sessions::LiveTabContext* AndroidLiveTabContext::FindContextWithID(
-    SessionID::id_type desired_id) {
+    SessionID desired_id) {
   // Find the model with desired id.
   TabModel* tab_model = TabModelList::FindTabModelWithId(desired_id);
 
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.h b/chrome/browser/ui/android/tab_model/android_live_tab_context.h
index 50d844e1..f5e12139 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.h
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.h
@@ -57,8 +57,7 @@
 
   static LiveTabContext* FindContextForWebContents(
       const content::WebContents* contents);
-  static sessions::LiveTabContext* FindContextWithID(
-      SessionID::id_type desired_id);
+  static sessions::LiveTabContext* FindContextWithID(SessionID desired_id);
 
  private:
   TabModel* tab_model_;
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
index 5bb7320c..3811f64 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
@@ -72,7 +72,7 @@
   // the window id.
   TabAndroid* tab = TabAndroid::GetNativeTab(env, jtab);
   if (tab)
-    tab->SetWindowSessionID(GetSessionId().id());
+    tab->SetWindowSessionID(GetSessionId());
 }
 
 int TabModelJniBridge::GetTabCount() const {
diff --git a/chrome/browser/ui/android/tab_model/tab_model_list.cc b/chrome/browser/ui/android/tab_model/tab_model_list.cc
index 7e02839..0f9f9e8 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_list.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_list.cc
@@ -79,11 +79,10 @@
   return NULL;
 }
 
-TabModel* TabModelList::FindTabModelWithId(
-    SessionID::id_type desired_id) {
+TabModel* TabModelList::FindTabModelWithId(SessionID desired_id) {
   for (TabModelList::const_iterator i = TabModelList::begin();
       i != TabModelList::end(); i++) {
-    if ((*i)->GetSessionId().id() == desired_id)
+    if ((*i)->GetSessionId() == desired_id)
       return *i;
   }
 
diff --git a/chrome/browser/ui/android/tab_model/tab_model_list.h b/chrome/browser/ui/android/tab_model/tab_model_list.h
index e599ffc..223763ba 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_list.h
+++ b/chrome/browser/ui/android/tab_model/tab_model_list.h
@@ -39,7 +39,7 @@
 
   static TabModel* GetTabModelForWebContents(
       content::WebContents* web_contents);
-  static TabModel* FindTabModelWithId(SessionID::id_type desired_id);
+  static TabModel* FindTabModelWithId(SessionID desired_id);
   static bool IsOffTheRecordSessionActive();
 
   static const_iterator begin();
diff --git a/chrome/browser/ui/android/usb_chooser_dialog_android.cc b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
index 8c4f37f..02becb02 100644
--- a/chrome/browser/ui/android/usb_chooser_dialog_android.cc
+++ b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
@@ -13,169 +13,129 @@
 #include "base/android/jni_string.h"
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
-#include "chrome/browser/usb/usb_blocklist.h"
-#include "chrome/browser/usb/usb_chooser_context.h"
-#include "chrome/browser/usb/usb_chooser_context_factory.h"
-#include "chrome/browser/usb/usb_util.h"
-#include "chrome/browser/usb/web_usb_histograms.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/common/url_constants.h"
 #include "components/security_state/core/security_state.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
-#include "device/base/device_client.h"
-#include "device/usb/mojo/type_converters.h"
-#include "device/usb/public/cpp/filter_utils.h"
-#include "device/usb/usb_device.h"
-#include "device/usb/webusb_descriptors.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "jni/UsbChooserDialog_jni.h"
 #include "ui/android/window_android.h"
 #include "url/gurl.h"
 
-using device::UsbDevice;
-
-namespace {
-
-void OnDevicePermissionRequestComplete(
-    scoped_refptr<UsbDevice> device,
-    device::mojom::UsbChooserService::GetPermissionCallback callback,
-    bool granted) {
-  device::mojom::UsbDeviceInfoPtr device_info;
-  if (granted)
-    device_info = device::mojom::UsbDeviceInfo::From(*device);
-  std::move(callback).Run(std::move(device_info));
-}
-
-}  // namespace
-
-UsbChooserDialogAndroid::UsbChooserDialogAndroid(
-    std::vector<device::mojom::UsbDeviceFilterPtr> filters,
+// static
+std::unique_ptr<UsbChooserDialogAndroid> UsbChooserDialogAndroid::Create(
     content::RenderFrameHost* render_frame_host,
-    device::mojom::UsbChooserService::GetPermissionCallback callback)
-    : render_frame_host_(render_frame_host),
-      callback_(std::move(callback)),
-      usb_service_observer_(this),
-      filters_(std::move(filters)),
-      weak_factory_(this) {
+    std::unique_ptr<ChooserController> controller,
+    base::OnceClosure on_close) {
   content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host_);
+      content::WebContents::FromRenderFrameHost(render_frame_host);
 
   // TODO(asimjour): This should be removed once we have proper
   // implementation of USB chooser in VR.
   if (vr::VrTabHelper::IsInVr(web_contents)) {
-    DCHECK(!callback_.is_null());
-    std::move(callback_).Run(nullptr);
     vr::VrTabHelper::UISuppressed(vr::UiSuppressedElement::kUsbChooser);
-    return;
+    return nullptr;
   }
 
-  device::UsbService* usb_service =
-      device::DeviceClient::Get()->GetUsbService();
-  if (!usb_service)
-    return;
-
-  if (!usb_service_observer_.IsObserving(usb_service))
-    usb_service_observer_.Add(usb_service);
-
   // Create (and show) the UsbChooser dialog.
   base::android::ScopedJavaLocalRef<jobject> window_android =
       web_contents->GetNativeView()->GetWindowAndroid()->GetJavaObject();
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::ScopedJavaLocalRef<jstring> origin_string =
       base::android::ConvertUTF16ToJavaString(
-          env, url_formatter::FormatUrlForSecurityDisplay(GURL(
-                   render_frame_host->GetLastCommittedOrigin().Serialize())));
+          env, url_formatter::FormatOriginForSecurityDisplay(
+                   render_frame_host->GetLastCommittedOrigin()));
   SecurityStateTabHelper* helper =
       SecurityStateTabHelper::FromWebContents(web_contents);
   DCHECK(helper);
   security_state::SecurityInfo security_info;
   helper->GetSecurityInfo(&security_info);
-  java_dialog_.Reset(Java_UsbChooserDialog_create(
-      env, window_android, origin_string, security_info.security_level,
-      reinterpret_cast<intptr_t>(this)));
 
-  if (!java_dialog_.is_null()) {
-    usb_service->GetDevices(
-        base::Bind(&UsbChooserDialogAndroid::GotUsbDeviceList,
-                   weak_factory_.GetWeakPtr()));
-  }
+  auto dialog = std::make_unique<UsbChooserDialogAndroid>(std::move(controller),
+                                                          std::move(on_close));
+  dialog->java_dialog_.Reset(Java_UsbChooserDialog_create(
+      env, window_android, origin_string, security_info.security_level,
+      reinterpret_cast<intptr_t>(dialog.get())));
+  if (dialog->java_dialog_.is_null())
+    return nullptr;
+
+  return dialog;
+}
+
+UsbChooserDialogAndroid::UsbChooserDialogAndroid(
+    std::unique_ptr<ChooserController> controller,
+    base::OnceClosure on_close)
+    : controller_(std::move(controller)), on_close_(std::move(on_close)) {
+  controller_->set_view(this);
 }
 
 UsbChooserDialogAndroid::~UsbChooserDialogAndroid() {
-  if (!callback_.is_null())
-    std::move(callback_).Run(nullptr);
-
   if (!java_dialog_.is_null()) {
     Java_UsbChooserDialog_closeDialog(base::android::AttachCurrentThread(),
                                       java_dialog_);
   }
+  controller_->set_view(nullptr);
 }
 
-void UsbChooserDialogAndroid::OnDeviceAdded(scoped_refptr<UsbDevice> device) {
-  if (DisplayDevice(device)) {
-    AddDeviceToChooserDialog(device);
-    devices_.push_back(device);
-  }
+void UsbChooserDialogAndroid::OnOptionsInitialized() {
+  for (size_t i = 0; i < controller_->NumOptions(); ++i)
+    OnOptionAdded(i);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_UsbChooserDialog_setIdleState(env, java_dialog_);
 }
 
-void UsbChooserDialogAndroid::OnDeviceRemoved(scoped_refptr<UsbDevice> device) {
-  auto it = std::find(devices_.begin(), devices_.end(), device);
-  if (it != devices_.end()) {
-    RemoveDeviceFromChooserDialog(device);
-    devices_.erase(it);
-  }
+void UsbChooserDialogAndroid::OnOptionAdded(size_t index) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  DCHECK_LE(index, item_id_map_.size());
+  int item_id = next_item_id_++;
+  std::string item_id_str = base::IntToString(item_id);
+  item_id_map_.insert(item_id_map_.begin() + index, item_id_str);
+
+  base::string16 device_name = controller_->GetOption(index);
+  Java_UsbChooserDialog_addDevice(
+      env, java_dialog_,
+      base::android::ConvertUTF8ToJavaString(env, item_id_str),
+      base::android::ConvertUTF16ToJavaString(env, device_name));
 }
 
-void UsbChooserDialogAndroid::Select(const std::string& guid) {
-  for (size_t i = 0; i < devices_.size(); ++i) {
-    scoped_refptr<UsbDevice>& device = devices_[i];
-    if (device->guid() == guid) {
-      content::WebContents* web_contents =
-          content::WebContents::FromRenderFrameHost(render_frame_host_);
-      GURL embedding_origin =
-          web_contents->GetMainFrame()->GetLastCommittedURL().GetOrigin();
-      Profile* profile =
-          Profile::FromBrowserContext(web_contents->GetBrowserContext());
-      UsbChooserContext* chooser_context =
-          UsbChooserContextFactory::GetForProfile(profile);
-      chooser_context->GrantDevicePermission(
-          render_frame_host_->GetLastCommittedURL().GetOrigin(),
-          embedding_origin, device->guid());
+void UsbChooserDialogAndroid::OnOptionRemoved(size_t index) {
+  JNIEnv* env = base::android::AttachCurrentThread();
 
-      device->RequestPermission(base::BindOnce(
-          &OnDevicePermissionRequestComplete, device, std::move(callback_)));
+  DCHECK_LT(index, item_id_map_.size());
+  std::string item_id = item_id_map_[index];
+  item_id_map_.erase(item_id_map_.begin() + index);
 
-      Java_UsbChooserDialog_closeDialog(base::android::AttachCurrentThread(),
-                                        java_dialog_);
-      RecordWebUsbChooserClosure(
-          device->serial_number().empty()
-              ? WEBUSB_CHOOSER_CLOSED_EPHEMERAL_PERMISSION_GRANTED
-              : WEBUSB_CHOOSER_CLOSED_PERMISSION_GRANTED);
-      return;
-    }
-  }
+  Java_UsbChooserDialog_removeDevice(
+      env, java_dialog_, base::android::ConvertUTF8ToJavaString(env, item_id));
 }
 
-void UsbChooserDialogAndroid::Cancel() {
-  DCHECK(!callback_.is_null());
-  std::move(callback_).Run(nullptr);
-  Java_UsbChooserDialog_closeDialog(base::android::AttachCurrentThread(),
-                                    java_dialog_);
+void UsbChooserDialogAndroid::OnOptionUpdated(size_t index) {
+  NOTREACHED();
+}
 
-  RecordWebUsbChooserClosure(devices_.size() == 0
-                                 ? WEBUSB_CHOOSER_CLOSED_CANCELLED_NO_DEVICES
-                                 : WEBUSB_CHOOSER_CLOSED_CANCELLED);
+void UsbChooserDialogAndroid::OnAdapterEnabledChanged(bool enabled) {
+  NOTREACHED();
+}
+
+void UsbChooserDialogAndroid::OnRefreshStateChanged(bool refreshing) {
+  NOTREACHED();
 }
 
 void UsbChooserDialogAndroid::OnItemSelected(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
-    const base::android::JavaParamRef<jstring>& device_id) {
-  Select(base::android::ConvertJavaStringToUTF8(env, device_id));
+    const base::android::JavaParamRef<jstring>& item_id_jstring) {
+  std::string item_id =
+      base::android::ConvertJavaStringToUTF8(env, item_id_jstring);
+  auto it = std::find(item_id_map_.begin(), item_id_map_.end(), item_id);
+  DCHECK(it != item_id_map_.end());
+  controller_->Select({std::distance(item_id_map_.begin(), it)});
+  base::ResetAndReturn(&on_close_).Run();
 }
 
 void UsbChooserDialogAndroid::OnDialogCancelled(
@@ -187,59 +147,11 @@
 void UsbChooserDialogAndroid::LoadUsbHelpPage(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
-  OpenUrl(chrome::kChooserUsbOverviewURL);
+  controller_->OpenHelpCenterUrl();
   Cancel();
 }
 
-// Get a list of devices that can be shown in the chooser bubble UI for
-// user to grant permsssion.
-void UsbChooserDialogAndroid::GotUsbDeviceList(
-    const std::vector<scoped_refptr<UsbDevice>>& devices) {
-  for (const auto& device : devices) {
-    if (DisplayDevice(device)) {
-      AddDeviceToChooserDialog(device);
-      devices_.push_back(device);
-    }
-  }
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_UsbChooserDialog_setIdleState(env, java_dialog_);
-}
-
-void UsbChooserDialogAndroid::AddDeviceToChooserDialog(
-    scoped_refptr<UsbDevice> device) const {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jstring> device_guid =
-      base::android::ConvertUTF8ToJavaString(env, device->guid());
-  base::android::ScopedJavaLocalRef<jstring> device_name =
-      base::android::ConvertUTF16ToJavaString(env, FormatUsbDeviceName(device));
-  Java_UsbChooserDialog_addDevice(env, java_dialog_, device_guid, device_name);
-}
-
-void UsbChooserDialogAndroid::RemoveDeviceFromChooserDialog(
-    scoped_refptr<UsbDevice> device) const {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jstring> device_guid =
-      base::android::ConvertUTF8ToJavaString(env, device->guid());
-  Java_UsbChooserDialog_removeDevice(env, java_dialog_, device_guid);
-}
-
-void UsbChooserDialogAndroid::OpenUrl(const std::string& url) {
-  content::WebContents::FromRenderFrameHost(render_frame_host_)
-      ->OpenURL(
-          content::OpenURLParams(GURL(url), content::Referrer(),
-                                 WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                                 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
-                                 false));  // is_renderer_initiated
-}
-
-bool UsbChooserDialogAndroid::DisplayDevice(
-    scoped_refptr<UsbDevice> device) const {
-  if (!UsbDeviceFilterMatchesAny(filters_, *device))
-    return false;
-
-  if (UsbBlocklist::Get().IsExcluded(device))
-    return false;
-
-  return true;
+void UsbChooserDialogAndroid::Cancel() {
+  controller_->Cancel();
+  base::ResetAndReturn(&on_close_).Run();
 }
diff --git a/chrome/browser/ui/android/usb_chooser_dialog_android.h b/chrome/browser/ui/android/usb_chooser_dialog_android.h
index c29f17e..97f5498 100644
--- a/chrome/browser/ui/android/usb_chooser_dialog_android.h
+++ b/chrome/browser/ui/android/usb_chooser_dialog_android.h
@@ -5,78 +5,66 @@
 #ifndef CHROME_BROWSER_UI_ANDROID_USB_CHOOSER_DIALOG_ANDROID_H_
 #define CHROME_BROWSER_UI_ANDROID_USB_CHOOSER_DIALOG_ANDROID_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "base/android/scoped_java_ref.h"
+#include "base/callback.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
-#include "base/strings/string16.h"
-#include "device/usb/public/mojom/chooser_service.mojom.h"
-#include "device/usb/usb_service.h"
+#include "chrome/browser/chooser_controller/chooser_controller.h"
 
 namespace content {
 class RenderFrameHost;
 }
 
-namespace device {
-class UsbDevice;
-}
-
 // Represents a way to ask the user to select a USB device from a list of
 // options.
-class UsbChooserDialogAndroid : public device::UsbService::Observer {
+class UsbChooserDialogAndroid : public ChooserController::View {
  public:
-  UsbChooserDialogAndroid(
-      std::vector<device::mojom::UsbDeviceFilterPtr> filters,
+  // Creates and shows the dialog. Will return nullptr if the dialog was not
+  // displayed. Otherwise |on_close| will be called when the user closes the
+  // dialog.
+  static std::unique_ptr<UsbChooserDialogAndroid> Create(
       content::RenderFrameHost* render_frame_host,
-      device::mojom::UsbChooserService::GetPermissionCallback callback);
+      std::unique_ptr<ChooserController> controller,
+      base::OnceClosure on_close);
+
+  explicit UsbChooserDialogAndroid(
+      std::unique_ptr<ChooserController> controller,
+      base::OnceClosure on_close);
   ~UsbChooserDialogAndroid() override;
 
-  // device::UsbService::Observer:
-  void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override;
-  void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override;
+  // ChooserController::View implementation
+  void OnOptionsInitialized() override;
+  void OnOptionAdded(size_t index) override;
+  void OnOptionRemoved(size_t index) override;
+  void OnOptionUpdated(size_t index) override;
+  void OnAdapterEnabledChanged(bool enabled) override;
+  void OnRefreshStateChanged(bool refreshing) override;
 
   // Report the dialog's result.
   void OnItemSelected(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj,
-                      const base::android::JavaParamRef<jstring>& device_id);
+                      const base::android::JavaParamRef<jstring>& item_id);
   void OnDialogCancelled(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& obj);
-
   void LoadUsbHelpPage(JNIEnv* env,
                        const base::android::JavaParamRef<jobject>& obj);
 
  private:
-  void GotUsbDeviceList(
-      const std::vector<scoped_refptr<device::UsbDevice>>& devices);
-
-  // Called when the user selects the USB device with |guid| from the chooser
-  // dialog.
-  void Select(const std::string& guid);
   // Called when the chooser dialog is closed.
   void Cancel();
 
-  void AddDeviceToChooserDialog(scoped_refptr<device::UsbDevice> device) const;
-  void RemoveDeviceFromChooserDialog(
-      scoped_refptr<device::UsbDevice> device) const;
+  std::unique_ptr<ChooserController> controller_;
+  base::OnceClosure on_close_;
 
-  void OpenUrl(const std::string& url);
-
-  bool DisplayDevice(scoped_refptr<device::UsbDevice> device) const;
-
-  content::RenderFrameHost* const render_frame_host_;
-  device::mojom::UsbChooserService::GetPermissionCallback callback_;
-  ScopedObserver<device::UsbService, device::UsbService::Observer>
-      usb_service_observer_;
-  std::vector<device::mojom::UsbDeviceFilterPtr> filters_;
-
-  std::vector<scoped_refptr<device::UsbDevice>> devices_;
+  // The Java dialog code expects items to have unique string IDs while the
+  // ChooserController code refers to devices by their position in the list.
+  int next_item_id_ = 0;
+  std::vector<std::string> item_id_map_;
 
   base::android::ScopedJavaGlobalRef<jobject> java_dialog_;
-  base::WeakPtrFactory<UsbChooserDialogAndroid> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(UsbChooserDialogAndroid);
 };
diff --git a/chrome/browser/ui/app_list/app_list_model_updater.h b/chrome/browser/ui/app_list/app_list_model_updater.h
index ffa9d61..798fe6c 100644
--- a/chrome/browser/ui/app_list/app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/app_list_model_updater.h
@@ -16,6 +16,7 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 
 class ChromeAppListItem;
 
@@ -64,7 +65,7 @@
   virtual void UpdateSearchBox(const base::string16& text,
                                bool initiated_by_user) {}
   virtual void PublishSearchResults(
-      std::vector<std::unique_ptr<app_list::SearchResult>> results) {}
+      std::vector<std::unique_ptr<ChromeSearchResult>> results) {}
 
   // Item field setters only used by ChromeAppListItem and its derived classes.
   virtual void SetItemIcon(const std::string& id, const gfx::ImageSkia& icon) {}
@@ -118,10 +119,9 @@
   virtual size_t BadgedItemCount() = 0;
   // For SearchModel:
   virtual bool SearchEngineIsGoogle() = 0;
-  virtual app_list::SearchResult* FindSearchResult(
+  virtual ChromeSearchResult* FindSearchResult(
       const std::string& result_id) = 0;
-  virtual app_list::SearchResult* GetResultByTitle(
-      const std::string& title) = 0;
+  virtual ChromeSearchResult* GetResultByTitle(const std::string& title) = 0;
 
   // Methods for handle model updates in ash:
   virtual void OnFolderCreated(ash::mojom::AppListItemMetadataPtr item) = 0;
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc
index 86e032d..b2d72c7 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.cc
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc
@@ -177,7 +177,7 @@
 
 void AppListViewDelegate::OpenSearchResult(const std::string& result_id,
                                            int event_flags) {
-  app_list::SearchResult* result = model_updater_->FindSearchResult(result_id);
+  ChromeSearchResult* result = model_updater_->FindSearchResult(result_id);
   if (result)
     search_controller_->OpenResult(result, event_flags);
 }
@@ -185,7 +185,7 @@
 void AppListViewDelegate::InvokeSearchResultAction(const std::string& result_id,
                                                    int action_index,
                                                    int event_flags) {
-  app_list::SearchResult* result = model_updater_->FindSearchResult(result_id);
+  ChromeSearchResult* result = model_updater_->FindSearchResult(result_id);
   if (result)
     search_controller_->InvokeResultAction(result, action_index, event_flags);
 }
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
index d22bd25..b02b1fce5 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "ash/app_list/model/search/search_model.h"
+#include "ash/app_list/model/search/search_result.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
@@ -15,6 +16,26 @@
 #include "extensions/common/constants.h"
 #include "ui/base/models/menu_model.h"
 
+namespace {
+
+// TODO(hejq): Get rid of these after refactoring ChromeSearchResult.
+ChromeSearchResult* ConvertToChromeSearchResult(
+    app_list::SearchResult* result) {
+  return static_cast<ChromeSearchResult*>(result);
+}
+
+std::vector<std::unique_ptr<app_list::SearchResult>> ConvertToSearchResults(
+    std::vector<std::unique_ptr<ChromeSearchResult>> results) {
+  std::vector<std::unique_ptr<app_list::SearchResult>> ash_results;
+  for (auto& result : results) {
+    ash_results.push_back(
+        base::WrapUnique<app_list::SearchResult>(result.release()));
+  }
+  return ash_results;
+}
+
+}  // namespace
+
 ChromeAppListModelUpdater::ChromeAppListModelUpdater(Profile* profile)
     : profile_(profile), weak_ptr_factory_(this) {
   // TODO(hejq): remove this when search migration is done.
@@ -139,9 +160,9 @@
 }
 
 void ChromeAppListModelUpdater::PublishSearchResults(
-    std::vector<std::unique_ptr<app_list::SearchResult>> results) {
+    std::vector<std::unique_ptr<ChromeSearchResult>> results) {
   if (!ash_util::IsRunningInMash())
-    search_model_->PublishResults(std::move(results));
+    search_model_->PublishResults(ConvertToSearchResults(std::move(results)));
 }
 
 void ChromeAppListModelUpdater::ActivateChromeItem(const std::string& id,
@@ -335,12 +356,14 @@
     chrome_item->ContextMenuItemSelected(command_id, event_flags);
 }
 
-app_list::SearchResult* ChromeAppListModelUpdater::FindSearchResult(
+ChromeSearchResult* ChromeAppListModelUpdater::FindSearchResult(
     const std::string& result_id) {
-  return search_model_ ? search_model_->FindSearchResult(result_id) : nullptr;
+  return search_model_ ? ConvertToChromeSearchResult(
+                             search_model_->FindSearchResult(result_id))
+                       : nullptr;
 }
 
-app_list::SearchResult* ChromeAppListModelUpdater::GetResultByTitle(
+ChromeSearchResult* ChromeAppListModelUpdater::GetResultByTitle(
     const std::string& title) {
   if (!search_model_)
     return nullptr;
@@ -354,7 +377,7 @@
         result->result_type() == ash::SearchResultType::kInstalledApp &&
         result->display_type() !=
             ash::SearchResultDisplayType::kRecommendation) {
-      return result.get();
+      return ConvertToChromeSearchResult(result.get());
     }
   }
   return nullptr;
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
index 93dc3b8..3b74609f 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
@@ -50,7 +50,7 @@
   void UpdateSearchBox(const base::string16& text,
                        bool initiated_by_user) override;
   void PublishSearchResults(
-      std::vector<std::unique_ptr<app_list::SearchResult>> results) override;
+      std::vector<std::unique_ptr<ChromeSearchResult>> results) override;
 
   // Methods only used by ChromeAppListItem that talk to ash directly.
   void SetItemIcon(const std::string& id, const gfx::ImageSkia& icon) override;
@@ -86,9 +86,8 @@
   void ContextMenuItemSelected(const std::string& id,
                                int command_id,
                                int event_flags) override;
-  app_list::SearchResult* FindSearchResult(
-      const std::string& result_id) override;
-  app_list::SearchResult* GetResultByTitle(const std::string& title) override;
+  ChromeSearchResult* FindSearchResult(const std::string& result_id) override;
+  ChromeSearchResult* GetResultByTitle(const std::string& title) override;
 
   // Methods for AppListSyncableService:
   void AddItemToOemFolder(
diff --git a/chrome/browser/ui/app_list/search/answer_card/answer_card_result.cc b/chrome/browser/ui/app_list/search/answer_card/answer_card_result.cc
index aa070fb..dcc97ed 100644
--- a/chrome/browser/ui/app_list/search/answer_card/answer_card_result.cc
+++ b/chrome/browser/ui/app_list/search/answer_card/answer_card_result.cc
@@ -42,7 +42,7 @@
   contents_ = nullptr;
 }
 
-std::unique_ptr<SearchResult> AnswerCardResult::Duplicate() const {
+std::unique_ptr<ChromeSearchResult> AnswerCardResult::Duplicate() const {
   return std::make_unique<AnswerCardResult>(
       profile_, list_controller_, id(), comparable_id(), title(), contents_);
 }
diff --git a/chrome/browser/ui/app_list/search/answer_card/answer_card_result.h b/chrome/browser/ui/app_list/search/answer_card/answer_card_result.h
index 7b309e7..70172a3 100644
--- a/chrome/browser/ui/app_list/search/answer_card/answer_card_result.h
+++ b/chrome/browser/ui/app_list/search/answer_card/answer_card_result.h
@@ -8,7 +8,7 @@
 #include <memory>
 #include <string>
 
-#include "ash/app_list/model/search/search_result.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 
 class AppListControllerDelegate;
 class Profile;
@@ -18,7 +18,7 @@
 class AnswerCardContents;
 
 // Result of AnswerCardSearchProvider.
-class AnswerCardResult : public SearchResult {
+class AnswerCardResult : public ChromeSearchResult {
  public:
   AnswerCardResult(Profile* profile,
                    AppListControllerDelegate* list_controller,
@@ -31,8 +31,8 @@
 
   void OnContentsDestroying();
 
-  // SearchResult overrides:
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  // ChromeSearchResult overrides:
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
 
   void Open(int event_flags) override;
 
diff --git a/chrome/browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc b/chrome/browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc
index 67144e4..d5dbd820 100644
--- a/chrome/browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc
+++ b/chrome/browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc
@@ -93,7 +93,7 @@
   result->Open(ui::EF_NONE);
   EXPECT_EQ(kResultUrl, GetLastOpenedUrl().spec());
 
-  std::unique_ptr<SearchResult> result1 = result->Duplicate();
+  std::unique_ptr<ChromeSearchResult> result1 = result->Duplicate();
 
   EXPECT_EQ(kResultUrl, result1->id());
   EXPECT_EQ(base::ASCIIToUTF16(kResultTitle), result1->title());
@@ -108,7 +108,7 @@
   // Shouldn't crash with null contents.
   std::unique_ptr<AnswerCardResult> result = CreateResult(
       kResultUrl, kResultUrlStripped, base::ASCIIToUTF16(kResultTitle));
-  std::unique_ptr<SearchResult> result1 = result->Duplicate();
+  std::unique_ptr<ChromeSearchResult> result1 = result->Duplicate();
 }
 
 TEST_F(AnswerCardResultTest, EarlyDeleteContents) {
diff --git a/chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc
index a005206..5fd26df 100644
--- a/chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc
@@ -9,7 +9,6 @@
 #include <string>
 #include <utility>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
@@ -19,6 +18,7 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/test/base/testing_profile.h"
@@ -108,7 +108,7 @@
     SCOPED_TRACE(message);
 
     EXPECT_EQ(1UL, results().size());
-    SearchResult* result = results()[0].get();
+    ChromeSearchResult* result = results()[0].get();
     EXPECT_EQ(ash::SearchResultDisplayType::kCard, result->display_type());
     EXPECT_EQ(id, result->id());
     EXPECT_EQ(1, result->relevance());
diff --git a/chrome/browser/ui/app_list/search/app_result.h b/chrome/browser/ui/app_list/search/app_result.h
index a51a342..f49c97f 100644
--- a/chrome/browser/ui/app_list/search/app_result.h
+++ b/chrome/browser/ui/app_list/search/app_result.h
@@ -8,9 +8,9 @@
 #include <memory>
 #include <string>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/app_context_menu_delegate.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 
 class AppListControllerDelegate;
 class Profile;
@@ -20,8 +20,7 @@
 }
 namespace app_list {
 
-class AppResult : public SearchResult,
-                  public AppContextMenuDelegate {
+class AppResult : public ChromeSearchResult, public AppContextMenuDelegate {
  public:
   ~AppResult() override;
 
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc
index bcb7742..c196c0e 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc
@@ -7,12 +7,12 @@
 #include <memory>
 #include <utility>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/search/arc/icon_decode_request.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 
 namespace app_list {
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.cc b/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.cc
index 23dd323a..06f9499 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.cc
@@ -99,7 +99,7 @@
 
 ArcAppDataSearchResult::~ArcAppDataSearchResult() = default;
 
-std::unique_ptr<SearchResult> ArcAppDataSearchResult::Duplicate() const {
+std::unique_ptr<ChromeSearchResult> ArcAppDataSearchResult::Duplicate() const {
   std::unique_ptr<ArcAppDataSearchResult> result =
       std::make_unique<ArcAppDataSearchResult>(data_.Clone(), profile_,
                                                list_controller_);
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.h b/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.h
index 7243d52..677a2899 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.h
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_data_search_result.h
@@ -9,9 +9,9 @@
 #include <string>
 #include <vector>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "components/arc/common/app.mojom.h"
 
 class AppListControllerDelegate;
@@ -21,15 +21,15 @@
 
 class IconDecodeRequest;
 
-class ArcAppDataSearchResult : public SearchResult {
+class ArcAppDataSearchResult : public ChromeSearchResult {
  public:
   ArcAppDataSearchResult(arc::mojom::AppDataResultPtr data,
                          Profile* profile,
                          AppListControllerDelegate* list_controller);
   ~ArcAppDataSearchResult() override;
 
-  // app_list::SearchResult:
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  // ChromeSearchResult:
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
   ui::MenuModel* GetContextMenuModel() override;
   void Open(int event_flags) override;
 
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc
index 24cb73b..d967222 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc
@@ -8,7 +8,6 @@
 #include <string>
 #include <utility>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -16,6 +15,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h"
 #include "chrome/browser/ui/app_list/search/arc/icon_decode_request.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/app/arc_playstore_search_request_state.h"
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
index d4d8fdd..fc2fd46 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.cc
@@ -82,7 +82,8 @@
 
 ArcPlayStoreSearchResult::~ArcPlayStoreSearchResult() = default;
 
-std::unique_ptr<SearchResult> ArcPlayStoreSearchResult::Duplicate() const {
+std::unique_ptr<ChromeSearchResult> ArcPlayStoreSearchResult::Duplicate()
+    const {
   std::unique_ptr<ArcPlayStoreSearchResult> result =
       std::make_unique<ArcPlayStoreSearchResult>(data_.Clone(), profile_,
                                                  list_controller_);
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h
index 3846780..8e17247 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h
@@ -9,10 +9,10 @@
 #include <string>
 #include <vector>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "chrome/browser/ui/app_list/app_context_menu_delegate.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "components/arc/common/app.mojom.h"
 
 class AppListControllerDelegate;
@@ -23,7 +23,7 @@
 
 class IconDecodeRequest;
 
-class ArcPlayStoreSearchResult : public SearchResult,
+class ArcPlayStoreSearchResult : public ChromeSearchResult,
                                  public AppContextMenuDelegate {
  public:
   ArcPlayStoreSearchResult(arc::mojom::AppDiscoveryResultPtr data,
@@ -31,8 +31,8 @@
                            AppListControllerDelegate* list_controller_);
   ~ArcPlayStoreSearchResult() override;
 
-  // app_list::SearchResult overrides:
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  // ChromeSearchResult overrides:
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
   ui::MenuModel* GetContextMenuModel() override;
   void Open(int event_flags) override;
 
diff --git a/chrome/browser/ui/app_list/search/arc/icon_decode_request.cc b/chrome/browser/ui/app_list/search/arc/icon_decode_request.cc
index f0fc7970..3365b90 100644
--- a/chrome/browser/ui/app_list/search/arc/icon_decode_request.cc
+++ b/chrome/browser/ui/app_list/search/arc/icon_decode_request.cc
@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/grit/component_extension_resources.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/app_list/app_list_constants.h"
diff --git a/chrome/browser/ui/app_list/search/arc_app_result.cc b/chrome/browser/ui/app_list/search/arc_app_result.cc
index 8eccac2..443c999 100644
--- a/chrome/browser/ui/app_list/search/arc_app_result.cc
+++ b/chrome/browser/ui/app_list/search/arc_app_result.cc
@@ -58,15 +58,15 @@
   controller()->DismissView();
 }
 
-std::unique_ptr<SearchResult> ArcAppResult::Duplicate() const {
-  std::unique_ptr<SearchResult> copy = std::make_unique<ArcAppResult>(
+std::unique_ptr<ChromeSearchResult> ArcAppResult::Duplicate() const {
+  ArcAppResult* copy = new ArcAppResult(
       profile(), app_id(), controller(),
       display_type() == ash::SearchResultDisplayType::kRecommendation);
   copy->set_title(title());
   copy->set_title_tags(title_tags());
   copy->set_relevance(relevance());
 
-  return copy;
+  return base::WrapUnique(copy);
 }
 
 ui::MenuModel* ArcAppResult::GetContextMenuModel() {
diff --git a/chrome/browser/ui/app_list/search/arc_app_result.h b/chrome/browser/ui/app_list/search/arc_app_result.h
index 60e7841..ee836fc 100644
--- a/chrome/browser/ui/app_list/search/arc_app_result.h
+++ b/chrome/browser/ui/app_list/search/arc_app_result.h
@@ -28,9 +28,9 @@
                bool is_recommendation);
   ~ArcAppResult() override;
 
-  // SearchResult overrides:
+  // ChromeSearchResult overrides:
   void Open(int event_flags) override;
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
   ui::MenuModel* GetContextMenuModel() override;
 
   // AppContextMenuDelegate overrides:
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.cc b/chrome/browser/ui/app_list/search/chrome_search_result.cc
new file mode 100644
index 0000000..05aedcc
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.cc
@@ -0,0 +1,7 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+
+ChromeSearchResult::ChromeSearchResult() = default;
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.h b/chrome/browser/ui/app_list/search/chrome_search_result.h
new file mode 100644
index 0000000..744a2007
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.h
@@ -0,0 +1,28 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_CHROME_SEARCH_RESULT_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_CHROME_SEARCH_RESULT_H_
+
+#include <memory>
+
+#include "ash/app_list/model/search/search_result.h"
+#include "base/macros.h"
+
+// ChromeSearchResult consists of an icon, title text and details text. Title
+// and details text can have tagged ranges that are displayed differently from
+// default style.
+class ChromeSearchResult : public app_list::SearchResult {
+ public:
+  ChromeSearchResult();
+
+  // TODO(mukai): Remove this method and really simplify the ownership of
+  // SearchResult. Ideally, SearchResult will be copyable.
+  virtual std::unique_ptr<ChromeSearchResult> Duplicate() const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChromeSearchResult);
+};
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_CHROME_SEARCH_RESULT_H_
diff --git a/chrome/browser/ui/app_list/search/extension_app_result.cc b/chrome/browser/ui/app_list/search/extension_app_result.cc
index 1d254887..c662925 100644
--- a/chrome/browser/ui/app_list/search/extension_app_result.cc
+++ b/chrome/browser/ui/app_list/search/extension_app_result.cc
@@ -65,7 +65,7 @@
   if (RunExtensionEnableFlow())
     return;
 
-  // Record the search metrics if the SearchResult is not a suggested app.
+  // Record the search metrics if the ChromeSearchResult is not a suggested app.
   if (display_type() != ash::SearchResultDisplayType::kRecommendation) {
     RecordHistogram(APP_SEARCH_RESULT);
     extensions::RecordAppListSearchLaunch(extension);
@@ -78,10 +78,11 @@
       event_flags);
 }
 
-std::unique_ptr<SearchResult> ExtensionAppResult::Duplicate() const {
-  std::unique_ptr<SearchResult> copy = std::make_unique<ExtensionAppResult>(
-      profile(), app_id(), controller(),
-      display_type() == ash::SearchResultDisplayType::kRecommendation);
+std::unique_ptr<ChromeSearchResult> ExtensionAppResult::Duplicate() const {
+  std::unique_ptr<ChromeSearchResult> copy =
+      std::make_unique<ExtensionAppResult>(
+          profile(), app_id(), controller(),
+          display_type() == ash::SearchResultDisplayType::kRecommendation);
   copy->set_title(title());
   copy->set_title_tags(title_tags());
   copy->set_relevance(relevance());
diff --git a/chrome/browser/ui/app_list/search/extension_app_result.h b/chrome/browser/ui/app_list/search/extension_app_result.h
index 6bd16f0..9409019 100644
--- a/chrome/browser/ui/app_list/search/extension_app_result.h
+++ b/chrome/browser/ui/app_list/search/extension_app_result.h
@@ -38,9 +38,9 @@
                      bool is_recommendation);
   ~ExtensionAppResult() override;
 
-  // SearchResult overrides:
+  // ChromeSearchResult overrides:
   void Open(int event_flags) override;
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
   ui::MenuModel* GetContextMenuModel() override;
 
  private:
diff --git a/chrome/browser/ui/app_list/search/internal_app_result.cc b/chrome/browser/ui/app_list/search/internal_app_result.cc
index 4959b82..8f86ae6 100644
--- a/chrome/browser/ui/app_list/search/internal_app_result.cc
+++ b/chrome/browser/ui/app_list/search/internal_app_result.cc
@@ -59,7 +59,7 @@
     keyboard_shortcut_viewer_util::ShowKeyboardShortcutViewer();
 }
 
-std::unique_ptr<SearchResult> InternalAppResult::Duplicate() const {
+std::unique_ptr<ChromeSearchResult> InternalAppResult::Duplicate() const {
   auto copy = std::make_unique<InternalAppResult>(
       profile(), app_id(), controller(),
       display_type() == DisplayType::kRecommendation);
diff --git a/chrome/browser/ui/app_list/search/internal_app_result.h b/chrome/browser/ui/app_list/search/internal_app_result.h
index ee92edb..169832b06 100644
--- a/chrome/browser/ui/app_list/search/internal_app_result.h
+++ b/chrome/browser/ui/app_list/search/internal_app_result.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_INTERNAL_APP_RESULT_H_
 
 #include <memory>
+#include <string>
 
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/search/app_result.h"
@@ -25,7 +26,7 @@
 
   // SearchResult overrides:
   void Open(int event_flags) override;
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
   ui::MenuModel* GetContextMenuModel() override;
 
   // AppContextMenuDelegate overrides:
diff --git a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.cc b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.cc
index a4282c3..eb1815a 100644
--- a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.cc
+++ b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.cc
@@ -52,7 +52,7 @@
     icon_image_loader_->RemoveObserver(this);
 }
 
-std::unique_ptr<SearchResult> LauncherSearchResult::Duplicate() const {
+std::unique_ptr<ChromeSearchResult> LauncherSearchResult::Duplicate() const {
   LauncherSearchResult* duplicated_result =
       new LauncherSearchResult(item_id_, discrete_value_relevance_, profile_,
                                extension_, icon_image_loader_);
diff --git a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.h b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.h
index 7f8f181..0270e2e4 100644
--- a/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.h
+++ b/chrome/browser/ui/app_list/search/launcher_search/launcher_search_result.h
@@ -8,17 +8,17 @@
 #include <memory>
 #include <string>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "base/memory/linked_ptr.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader.h"
 #include "extensions/common/extension.h"
 #include "url/gurl.h"
 
 namespace app_list {
 
-class LauncherSearchResult : public SearchResult,
+class LauncherSearchResult : public ChromeSearchResult,
                              public LauncherSearchIconImageLoader::Observer {
  public:
   LauncherSearchResult(
@@ -30,7 +30,7 @@
       std::unique_ptr<chromeos::launcher_search_provider::ErrorReporter>
           error_reporter);
   ~LauncherSearchResult() override;
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
   void Open(int event_flags) override;
 
   void OnIconImageChanged(LauncherSearchIconImageLoader* image_loader) override;
diff --git a/chrome/browser/ui/app_list/search/mixer.cc b/chrome/browser/ui/app_list/search/mixer.cc
index b1297c6e..df1dd4f 100644
--- a/chrome/browser/ui/app_list/search/mixer.cc
+++ b/chrome/browser/ui/app_list/search/mixer.cc
@@ -11,17 +11,17 @@
 #include <utility>
 #include <vector>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 
 namespace app_list {
 
 namespace {
 
-const std::string& GetComparableId(const SearchResult& result) {
+const std::string& GetComparableId(const ChromeSearchResult& result) {
   return !result.comparable_id().empty() ? result.comparable_id() : result.id();
 }
 
@@ -29,7 +29,7 @@
 
 Mixer::SortData::SortData() : result(nullptr), score(0.0) {}
 
-Mixer::SortData::SortData(SearchResult* result, double score)
+Mixer::SortData::SortData(ChromeSearchResult* result, double score)
     : result(result), score(score) {}
 
 bool Mixer::SortData::operator<(const SortData& other) const {
@@ -137,9 +137,9 @@
     std::sort(results.begin() + original_size, results.end());
   }
 
-  std::vector<std::unique_ptr<app_list::SearchResult>> new_results;
+  std::vector<std::unique_ptr<ChromeSearchResult>> new_results;
   for (const SortData& sort_data : results) {
-    std::unique_ptr<app_list::SearchResult> new_result =
+    std::unique_ptr<ChromeSearchResult> new_result =
         sort_data.result->Duplicate();
     new_result->set_relevance(sort_data.score);
     new_results.push_back(std::move(new_result));
diff --git a/chrome/browser/ui/app_list/search/mixer.h b/chrome/browser/ui/app_list/search/mixer.h
index c3cc3ea..fe17c07d 100644
--- a/chrome/browser/ui/app_list/search/mixer.h
+++ b/chrome/browser/ui/app_list/search/mixer.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/app_list/search/history_types.h"
 
 class AppListModelUpdater;
+class ChromeSearchResult;
 
 namespace app_list {
 
@@ -23,7 +24,6 @@
 }
 
 class SearchProvider;
-class SearchResult;
 
 // Mixer collects results from providers, sorts them and publishes them to the
 // SearchResults UI model. The targeted results have 6 slots to hold the
@@ -54,11 +54,11 @@
   // Used for sorting and mixing results.
   struct SortData {
     SortData();
-    SortData(SearchResult* result, double score);
+    SortData(ChromeSearchResult* result, double score);
 
     bool operator<(const SortData& other) const;
 
-    SearchResult* result;  // Not owned.
+    ChromeSearchResult* result;  // Not owned.
     double score;
   };
   typedef std::vector<Mixer::SortData> SortedResults;
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index 4bfd596e..a39e073 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -35,30 +35,30 @@
 int ACMatchStyleToTagStyle(int styles) {
   int tag_styles = 0;
   if (styles & ACMatchClassification::URL)
-    tag_styles |= SearchResult::Tag::URL;
+    tag_styles |= ash::SearchResultTag::URL;
   if (styles & ACMatchClassification::MATCH)
-    tag_styles |= SearchResult::Tag::MATCH;
+    tag_styles |= ash::SearchResultTag::MATCH;
   if (styles & ACMatchClassification::DIM)
-    tag_styles |= SearchResult::Tag::DIM;
+    tag_styles |= ash::SearchResultTag::DIM;
 
   return tag_styles;
 }
 
-// Translates ACMatchClassifications into SearchResult tags.
+// Translates ACMatchClassifications into ChromeSearchResult tags.
 void ACMatchClassificationsToTags(const base::string16& text,
                                   const ACMatchClassifications& text_classes,
-                                  SearchResult::Tags* tags) {
-  int tag_styles = SearchResult::Tag::NONE;
+                                  ChromeSearchResult::Tags* tags) {
+  int tag_styles = ash::SearchResultTag::NONE;
   size_t tag_start = 0;
 
   for (size_t i = 0; i < text_classes.size(); ++i) {
     const ACMatchClassification& text_class = text_classes[i];
 
     // Closes current tag.
-    if (tag_styles != SearchResult::Tag::NONE) {
+    if (tag_styles != ash::SearchResultTag::NONE) {
       tags->push_back(
-          SearchResult::Tag(tag_styles, tag_start, text_class.offset));
-      tag_styles = SearchResult::Tag::NONE;
+          ash::SearchResultTag(tag_styles, tag_start, text_class.offset));
+      tag_styles = ash::SearchResultTag::NONE;
     }
 
     if (text_class.style == ACMatchClassification::NONE)
@@ -68,8 +68,8 @@
     tag_styles = ACMatchStyleToTagStyle(text_class.style);
   }
 
-  if (tag_styles != SearchResult::Tag::NONE) {
-    tags->push_back(SearchResult::Tag(tag_styles, tag_start, text.length()));
+  if (tag_styles != ash::SearchResultTag::NONE) {
+    tags->push_back(ash::SearchResultTag(tag_styles, tag_start, text.length()));
   }
 }
 
@@ -154,9 +154,9 @@
                             ui::DispositionFromEventFlags(event_flags));
 }
 
-std::unique_ptr<SearchResult> OmniboxResult::Duplicate() const {
-  return std::unique_ptr<SearchResult>(new OmniboxResult(
-      profile_, list_controller_, autocomplete_controller_, match_));
+std::unique_ptr<ChromeSearchResult> OmniboxResult::Duplicate() const {
+  return std::make_unique<OmniboxResult>(profile_, list_controller_,
+                                         autocomplete_controller_, match_);
 }
 
 void OmniboxResult::UpdateIcon() {
@@ -175,7 +175,7 @@
   // the url description is presented as title, and url itself is presented as
   // details.
   const bool use_directly = !IsUrlResultWithDescription();
-  SearchResult::Tags title_tags;
+  ChromeSearchResult::Tags title_tags;
   if (use_directly) {
     set_title(match_.contents);
     ACMatchClassificationsToTags(match_.contents, match_.contents_class,
@@ -187,7 +187,7 @@
   }
   set_title_tags(title_tags);
 
-  SearchResult::Tags details_tags;
+  ChromeSearchResult::Tags details_tags;
   if (use_directly) {
     set_details(match_.description);
     ACMatchClassificationsToTags(match_.description, match_.description_class,
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.h b/chrome/browser/ui/app_list/search/omnibox_result.h
index f1578fb1..7827e57e 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.h
+++ b/chrome/browser/ui/app_list/search/omnibox_result.h
@@ -7,8 +7,8 @@
 
 #include <memory>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 
 class AppListControllerDelegate;
@@ -17,7 +17,7 @@
 
 namespace app_list {
 
-class OmniboxResult : public SearchResult {
+class OmniboxResult : public ChromeSearchResult {
  public:
   OmniboxResult(Profile* profile,
                 AppListControllerDelegate* list_controller,
@@ -25,10 +25,10 @@
                 const AutocompleteMatch& match);
   ~OmniboxResult() override;
 
-  // SearchResult overrides:
+  // ChromeSearchResult overrides:
   void Open(int event_flags) override;
 
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
 
  private:
   void UpdateIcon();
diff --git a/chrome/browser/ui/app_list/search/search_controller.cc b/chrome/browser/ui/app_list/search/search_controller.cc
index 96c9114..237346d4 100644
--- a/chrome/browser/ui/app_list/search/search_controller.cc
+++ b/chrome/browser/ui/app_list/search/search_controller.cc
@@ -9,11 +9,11 @@
 #include <utility>
 #include <vector>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/bind.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/history.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 #include "ui/app_list/app_list_constants.h"
@@ -42,7 +42,7 @@
   OnResultsChanged();
 }
 
-void SearchController::OpenResult(SearchResult* result, int event_flags) {
+void SearchController::OpenResult(ChromeSearchResult* result, int event_flags) {
   // This can happen in certain circumstances due to races. See
   // https://crbug.com/534772
   if (!result)
@@ -54,7 +54,7 @@
     history_->AddLaunchEvent(base::UTF16ToUTF8(last_raw_query_), result->id());
 }
 
-void SearchController::InvokeResultAction(SearchResult* result,
+void SearchController::InvokeResultAction(ChromeSearchResult* result,
                                           int action_index,
                                           int event_flags) {
   // TODO(xiyuan): Hook up with user learning.
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index edf9626..e6c8f6f 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -15,12 +15,12 @@
 #include "chrome/browser/ui/app_list/search/mixer.h"
 
 class AppListModelUpdater;
+class ChromeSearchResult;
 
 namespace app_list {
 
 class History;
 class SearchProvider;
-class SearchResult;
 
 // Controller that collects query from given SearchBoxModel, dispatches it
 // to all search providers, then invokes the mixer to mix and to publish the
@@ -33,8 +33,8 @@
   // TODO(hejq): can we accept a trimmed query here?
   void Start(const base::string16& raw_query);
 
-  void OpenResult(SearchResult* result, int event_flags);
-  void InvokeResultAction(SearchResult* result,
+  void OpenResult(ChromeSearchResult* result, int event_flags);
+  void InvokeResultAction(ChromeSearchResult* result,
                           int action_index,
                           int event_flags);
 
diff --git a/chrome/browser/ui/app_list/search/search_provider.cc b/chrome/browser/ui/app_list/search/search_provider.cc
index 1180e82..3d7afea3 100644
--- a/chrome/browser/ui/app_list/search/search_provider.cc
+++ b/chrome/browser/ui/app_list/search/search_provider.cc
@@ -6,14 +6,14 @@
 
 #include <utility>
 
-#include "ash/app_list/model/search/search_result.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 
 namespace app_list {
 
 SearchProvider::SearchProvider() {}
 SearchProvider::~SearchProvider() {}
 
-void SearchProvider::Add(std::unique_ptr<SearchResult> result) {
+void SearchProvider::Add(std::unique_ptr<ChromeSearchResult> result) {
   results_.emplace_back(std::move(result));
   FireResultChanged();
 }
diff --git a/chrome/browser/ui/app_list/search/search_provider.h b/chrome/browser/ui/app_list/search/search_provider.h
index 0a6d520..72bf24ef 100644
--- a/chrome/browser/ui/app_list/search/search_provider.h
+++ b/chrome/browser/ui/app_list/search/search_provider.h
@@ -12,13 +12,13 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 
-namespace app_list {
+class ChromeSearchResult;
 
-class SearchResult;
+namespace app_list {
 
 class SearchProvider {
  public:
-  using Results = std::vector<std::unique_ptr<SearchResult>>;
+  using Results = std::vector<std::unique_ptr<ChromeSearchResult>>;
   using ResultChangedCallback = base::Closure;
 
   SearchProvider();
@@ -35,7 +35,7 @@
 
  protected:
   // Interface for the derived class to generate search results.
-  void Add(std::unique_ptr<SearchResult> result);
+  void Add(std::unique_ptr<ChromeSearchResult> result);
 
   // Swaps the internal results with |new_results|.
   // This is useful when multiple results will be added, and the notification is
diff --git a/chrome/browser/ui/app_list/search/search_webstore_result.cc b/chrome/browser/ui/app_list/search/search_webstore_result.cc
index c3d03b8..fa5185a 100644
--- a/chrome/browser/ui/app_list/search/search_webstore_result.cc
+++ b/chrome/browser/ui/app_list/search/search_webstore_result.cc
@@ -34,7 +34,7 @@
   const base::string16 details =
       l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE);
   Tags details_tags;
-  details_tags.push_back(Tag(SearchResult::Tag::DIM, 0, details.length()));
+  details_tags.push_back(Tag(ash::SearchResultTag::DIM, 0, details.length()));
 
   set_details(details);
   set_details_tags(details_tags);
@@ -58,9 +58,8 @@
                        ui::DispositionFromEventFlags(event_flags));
 }
 
-std::unique_ptr<SearchResult> SearchWebstoreResult::Duplicate() const {
-  return std::unique_ptr<SearchResult>(
-      new SearchWebstoreResult(profile_, controller_, query_));
+std::unique_ptr<ChromeSearchResult> SearchWebstoreResult::Duplicate() const {
+  return std::make_unique<SearchWebstoreResult>(profile_, controller_, query_);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_webstore_result.h b/chrome/browser/ui/app_list/search/search_webstore_result.h
index a8a41579..78fabb8 100644
--- a/chrome/browser/ui/app_list/search/search_webstore_result.h
+++ b/chrome/browser/ui/app_list/search/search_webstore_result.h
@@ -8,8 +8,8 @@
 #include <memory>
 #include <string>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "url/gurl.h"
 
 class AppListControllerDelegate;
@@ -18,16 +18,16 @@
 namespace app_list {
 
 // A "search in webstore" result.
-class SearchWebstoreResult : public SearchResult {
+class SearchWebstoreResult : public ChromeSearchResult {
  public:
   SearchWebstoreResult(Profile* profile,
                        AppListControllerDelegate* controller,
                        const std::string& query);
   ~SearchWebstoreResult() override;
 
-  // SearchResult overrides:
+  // ChromeSearchResult overrides:
   void Open(int event_flags) override;
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
 
  private:
   Profile* profile_;
diff --git a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
index d6a17f6..2c605b0c 100644
--- a/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/app_search_provider_unittest.cc
@@ -11,7 +11,6 @@
 #include <string>
 #include <utility>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -22,6 +21,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/extension_app_model_builder.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/common/chrome_constants.h"
@@ -52,7 +52,8 @@
 
 const base::Time kTestCurrentTime = base::Time::FromInternalValue(100000);
 
-bool MoreRelevant(const SearchResult* result1, const SearchResult* result2) {
+bool MoreRelevant(const ChromeSearchResult* result1,
+                  const ChromeSearchResult* result2) {
   return result1->relevance() > result2->relevance();
 }
 
@@ -79,7 +80,7 @@
     app_search_->Start(base::UTF8ToUTF16(query));
 
     // Sort results by relevance.
-    std::vector<SearchResult*> sorted_results;
+    std::vector<ChromeSearchResult*> sorted_results;
     for (const auto& result : app_search_->results())
       sorted_results.emplace_back(result.get());
     std::sort(sorted_results.begin(), sorted_results.end(), &MoreRelevant);
diff --git a/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc b/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc
index aa155b97..9b42359 100644
--- a/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc
+++ b/chrome/browser/ui/app_list/search/tests/mixer_unittest.cc
@@ -32,7 +32,7 @@
 const size_t kMaxOmniboxResults = 4;
 const size_t kMaxWebstoreResults = 2;
 
-class TestSearchResult : public SearchResult {
+class TestSearchResult : public ChromeSearchResult {
  public:
   TestSearchResult(const std::string& id, double relevance)
       : instance_id_(instantiation_count++) {
@@ -42,17 +42,17 @@
   }
   ~TestSearchResult() override {}
 
-  // SearchResult overrides:
+  // ChromeSearchResult overrides:
   void Open(int event_flags) override {}
   void InvokeAction(int action_index, int event_flags) override {}
-  std::unique_ptr<SearchResult> Duplicate() const override {
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override {
     return std::make_unique<TestSearchResult>(id(), relevance());
   }
 
   // For reference equality testing. (Addresses cannot be used to test reference
   // equality because it is possible that an object will be allocated at the
   // same address as a previously deleted one.)
-  static int GetInstanceId(SearchResult* result) {
+  static int GetInstanceId(ChromeSearchResult* result) {
     return static_cast<const TestSearchResult*>(result)->instance_id_;
   }
 
@@ -87,12 +87,12 @@
         relevance = 10.0 - i * 10;
       TestSearchResult* result = new TestSearchResult(id, relevance);
       result->set_display_type(display_type_);
-      Add(std::unique_ptr<SearchResult>(result));
+      Add(std::unique_ptr<ChromeSearchResult>(result));
     }
   }
 
   void set_prefix(const std::string& prefix) { prefix_ = prefix; }
-  void set_display_type(SearchResult::DisplayType display_type) {
+  void set_display_type(ChromeSearchResult::DisplayType display_type) {
     display_type_ = display_type;
   }
   void set_count(size_t count) { count_ = count; }
@@ -102,7 +102,7 @@
   std::string prefix_;
   size_t count_;
   bool bad_relevance_range_;
-  SearchResult::DisplayType display_type_;
+  ChromeSearchResult::DisplayType display_type_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSearchProvider);
 };
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc b/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
index a5868211..8aa8a97 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_provider.cc
@@ -139,7 +139,7 @@
     if (!it->GetAsDictionary(&dict))
       continue;
 
-    std::unique_ptr<SearchResult> result(CreateResult(query, *dict));
+    std::unique_ptr<ChromeSearchResult> result(CreateResult(query, *dict));
     if (!result)
       continue;
 
@@ -153,7 +153,7 @@
   }
 }
 
-std::unique_ptr<SearchResult> WebstoreProvider::CreateResult(
+std::unique_ptr<ChromeSearchResult> WebstoreProvider::CreateResult(
     const TokenizedString& query,
     const base::DictionaryValue& dict) {
   std::string app_id;
@@ -164,16 +164,16 @@
       !dict.GetString(kKeyLocalizedName, &localized_name) ||
       !dict.GetString(kKeyIconUrl, &icon_url_string) ||
       !dict.GetBoolean(kKeyIsPaid, &is_paid)) {
-    return std::unique_ptr<SearchResult>();
+    return nullptr;
   }
 
   // If an app is already installed, don't show it in results.
   if (controller_->IsExtensionInstalled(profile_, app_id))
-    return std::unique_ptr<SearchResult>();
+    return nullptr;
 
   GURL icon_url(icon_url_string);
   if (!icon_url.is_valid())
-    return std::unique_ptr<SearchResult>();
+    return nullptr;
 
   std::string item_type_string;
   dict.GetString(kKeyItemType, &item_type_string);
@@ -186,12 +186,12 @@
   TokenizedString title(base::UTF8ToUTF16(localized_name));
   TokenizedStringMatch match;
   if (!match.Calculate(query, title))
-    return std::unique_ptr<SearchResult>();
+    return nullptr;
 
-  std::unique_ptr<SearchResult> result = std::make_unique<WebstoreResult>(
-      profile_, app_id, icon_url, is_paid, item_type, controller_);
+  WebstoreResult* result = new WebstoreResult(profile_, app_id, icon_url,
+                                              is_paid, item_type, controller_);
   result->UpdateFromMatch(title, match);
-  return result;
+  return base::WrapUnique(result);
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_provider.h b/chrome/browser/ui/app_list/search/webstore/webstore_provider.h
index 978afb946..87ed1bc 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_provider.h
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_provider.h
@@ -6,12 +6,14 @@
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_WEBSTORE_PROVIDER_H_
 
 #include <memory>
+#include <string>
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/search/common/webservice_search_provider.h"
 
 class AppListControllerDelegate;
+class ChromeSearchResult;
 
 namespace base {
 class DictionaryValue;
@@ -24,7 +26,6 @@
 }
 
 class JSONResponseFetcher;
-class SearchResult;
 class TokenizedString;
 
 // WebstoreProvider fetches search results from the web store server.
@@ -46,8 +47,9 @@
 
   void OnWebstoreSearchFetched(std::unique_ptr<base::DictionaryValue> json);
   void ProcessWebstoreSearchResults(const base::DictionaryValue* json);
-  std::unique_ptr<SearchResult> CreateResult(const TokenizedString& query,
-                                             const base::DictionaryValue& dict);
+  std::unique_ptr<ChromeSearchResult> CreateResult(
+      const TokenizedString& query,
+      const base::DictionaryValue& dict);
 
   void set_webstore_search_fetched_callback(const base::Closure& callback) {
     webstore_search_fetched_callback_ = callback;
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc b/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc
index 6d90261..0e73ccb 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc
@@ -11,13 +11,13 @@
 #include <string>
 #include <utility>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/webstore/webstore_result.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/common/chrome_switches.h"
@@ -217,7 +217,8 @@
                      size_t expected_result_size) {
     ASSERT_EQ(expected_result_size, webstore_provider_->results().size());
     for (size_t i = 0; i < expected_result_size; ++i) {
-      const SearchResult* result = (webstore_provider_->results()[i]).get();
+      const ChromeSearchResult* result =
+          (webstore_provider_->results()[i]).get();
       // A search for an installed app will return a general webstore search
       // instead of an app in the webstore.
       if (!strcmp(expected_results[i].id, kWebstoreUrlPlaceholder)) {
@@ -240,7 +241,7 @@
       }
 
       EXPECT_EQ(std::string(expected_results[i].title),
-                app_list::SearchResult::TagsDebugString(
+                ChromeSearchResult::TagsDebugString(
                     base::UTF16ToUTF8(result->title()), result->title_tags()));
 
       // Ensure the number of action buttons is appropriate for the item type.
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_result.cc b/chrome/browser/ui/app_list/search/webstore/webstore_result.cc
index a9b983f..c78f333b 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_result.cc
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_result.cc
@@ -102,8 +102,8 @@
   StartInstall();
 }
 
-std::unique_ptr<SearchResult> WebstoreResult::Duplicate() const {
-  std::unique_ptr<SearchResult> copy(new WebstoreResult(
+std::unique_ptr<ChromeSearchResult> WebstoreResult::Duplicate() const {
+  std::unique_ptr<ChromeSearchResult> copy(new WebstoreResult(
       profile_, app_id_, icon_url_, is_paid_, item_type_, controller_));
   copy->set_title(title());
   copy->set_title_tags(title_tags());
@@ -150,7 +150,7 @@
   const base::string16 details =
       l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE);
   Tags details_tags;
-  details_tags.push_back(Tag(SearchResult::Tag::DIM, 0, details.length()));
+  details_tags.push_back(Tag(ash::SearchResultTag::DIM, 0, details.length()));
 
   set_details(details);
   set_details_tags(details_tags);
diff --git a/chrome/browser/ui/app_list/search/webstore/webstore_result.h b/chrome/browser/ui/app_list/search/webstore/webstore_result.h
index f60c91c..4e833749 100644
--- a/chrome/browser/ui/app_list/search/webstore/webstore_result.h
+++ b/chrome/browser/ui/app_list/search/webstore/webstore_result.h
@@ -8,10 +8,10 @@
 #include <memory>
 #include <string>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/extensions/install_observer.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/common/extensions/webstore_install_result.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/common/manifest.h"
@@ -27,7 +27,7 @@
 
 namespace app_list {
 
-class WebstoreResult : public SearchResult,
+class WebstoreResult : public ChromeSearchResult,
                        public extensions::InstallObserver,
                        public extensions::ExtensionRegistryObserver {
  public:
@@ -47,10 +47,10 @@
   extensions::Manifest::Type item_type() const { return item_type_; }
   bool is_paid() const { return is_paid_; }
 
-  // SearchResult overrides:
+  // ChromeSearchResult overrides:
   void Open(int event_flags) override;
   void InvokeAction(int action_index, int event_flags) override;
-  std::unique_ptr<SearchResult> Duplicate() const override;
+  std::unique_ptr<ChromeSearchResult> Duplicate() const override;
 
  private:
   // Set the initial state and start observing both InstallObserver and
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
index c5fb8df7..acd33a7 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
@@ -123,7 +123,7 @@
   return search_engine_is_google_;
 }
 
-app_list::SearchResult* FakeAppListModelUpdater::FindSearchResult(
+ChromeSearchResult* FakeAppListModelUpdater::FindSearchResult(
     const std::string& result_id) {
   for (auto& result : search_results_) {
     if (result->id() == result_id)
@@ -132,13 +132,13 @@
   return nullptr;
 }
 
-app_list::SearchResult* FakeAppListModelUpdater::GetResultByTitle(
+ChromeSearchResult* FakeAppListModelUpdater::GetResultByTitle(
     const std::string& title) {
   return nullptr;
 }
 
 void FakeAppListModelUpdater::PublishSearchResults(
-    std::vector<std::unique_ptr<app_list::SearchResult>> results) {
+    std::vector<std::unique_ptr<ChromeSearchResult>> results) {
   search_results_ = std::move(results);
 }
 
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
index dd9df728..41b54a8a 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
@@ -10,8 +10,8 @@
 #include <unordered_map>
 #include <vector>
 
-#include "ash/app_list/model/search/search_result.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 
 class ChromeAppListItem;
 
@@ -41,7 +41,7 @@
   // For SearchModel:
   void SetSearchEngineIsGoogle(bool is_google) override;
   void PublishSearchResults(
-      std::vector<std::unique_ptr<app_list::SearchResult>> results) override;
+      std::vector<std::unique_ptr<ChromeSearchResult>> results) override;
 
   void ActivateChromeItem(const std::string& id, int event_flags) override;
 
@@ -56,10 +56,9 @@
   size_t BadgedItemCount() override;
   // For SearchModel:
   bool SearchEngineIsGoogle() override;
-  app_list::SearchResult* FindSearchResult(
-      const std::string& result_id) override;
-  app_list::SearchResult* GetResultByTitle(const std::string& title) override;
-  const std::vector<std::unique_ptr<app_list::SearchResult>>& search_results()
+  ChromeSearchResult* FindSearchResult(const std::string& result_id) override;
+  ChromeSearchResult* GetResultByTitle(const std::string& title) override;
+  const std::vector<std::unique_ptr<ChromeSearchResult>>& search_results()
       const {
     return search_results_;
   }
@@ -73,7 +72,7 @@
  private:
   bool search_engine_is_google_ = false;
   std::vector<std::unique_ptr<ChromeAppListItem>> items_;
-  std::vector<std::unique_ptr<app_list::SearchResult>> search_results_;
+  std::vector<std::unique_ptr<ChromeSearchResult>> search_results_;
   AppListModelUpdaterDelegate* delegate_ = nullptr;
 
   ash::mojom::AppListItemMetadataPtr FindOrCreateOemFolder(
diff --git a/chrome/browser/ui/browser_finder.cc b/chrome/browser/ui/browser_finder.cc
index b7cc33f..ed48d48e 100644
--- a/chrome/browser/ui/browser_finder.cc
+++ b/chrome/browser/ui/browser_finder.cc
@@ -184,9 +184,9 @@
   return FindBrowserWithTabbedOrAnyType(profile, false, false);
 }
 
-Browser* FindBrowserWithID(SessionID::id_type desired_id) {
+Browser* FindBrowserWithID(SessionID desired_id) {
   for (auto* browser : *BrowserList::GetInstance()) {
-    if (browser->session_id().id() == desired_id)
+    if (browser->session_id() == desired_id)
       return browser;
   }
   return NULL;
diff --git a/chrome/browser/ui/browser_finder.h b/chrome/browser/ui/browser_finder.h
index 19a335f..7b9ca16 100644
--- a/chrome/browser/ui/browser_finder.h
+++ b/chrome/browser/ui/browser_finder.h
@@ -47,7 +47,7 @@
 
 // Find an existing browser with the provided ID. Returns NULL if no such
 // browser currently exists.
-Browser* FindBrowserWithID(SessionID::id_type desired_id);
+Browser* FindBrowserWithID(SessionID desired_id);
 
 // Find the browser represented by |window| or NULL if not found.
 Browser* FindBrowserWithWindow(gfx::NativeWindow window);
diff --git a/chrome/browser/ui/browser_live_tab_context.cc b/chrome/browser/ui/browser_live_tab_context.cc
index 6268760e..0bca9d7 100644
--- a/chrome/browser/ui/browser_live_tab_context.cc
+++ b/chrome/browser/ui/browser_live_tab_context.cc
@@ -174,7 +174,7 @@
 
 // static
 sessions::LiveTabContext* BrowserLiveTabContext::FindContextWithID(
-    SessionID::id_type desired_id) {
+    SessionID desired_id) {
   Browser* browser = chrome::FindBrowserWithID(desired_id);
   return browser ? browser->live_tab_context() : nullptr;
 }
diff --git a/chrome/browser/ui/browser_live_tab_context.h b/chrome/browser/ui/browser_live_tab_context.h
index 6d5b5ec..f49729a 100644
--- a/chrome/browser/ui/browser_live_tab_context.h
+++ b/chrome/browser/ui/browser_live_tab_context.h
@@ -77,8 +77,7 @@
   // see chrome::FindBrowserWithID
   // Returns the LiveTabContext of the Browser with |desired_id| if
   // such a Browser exists.
-  static sessions::LiveTabContext* FindContextWithID(
-      SessionID::id_type desired_id);
+  static sessions::LiveTabContext* FindContextWithID(SessionID desired_id);
 
  private:
   Browser* const browser_;
diff --git a/chrome/browser/ui/cocoa/applescript/tab_applescript.mm b/chrome/browser/ui/cocoa/applescript/tab_applescript.mm
index e5ac419b..33216fe 100644
--- a/chrome/browser/ui/cocoa/applescript/tab_applescript.mm
+++ b/chrome/browser/ui/cocoa/applescript/tab_applescript.mm
@@ -62,8 +62,7 @@
 
 - (instancetype)init {
   if ((self = [super init])) {
-    SessionID session;
-    SessionID::id_type futureSessionIDOfTab = session.id() + 1;
+    SessionID::id_type futureSessionIDOfTab = SessionID::NewUnique().id() + 1;
     // Holds the SessionID that the new tab is going to get.
     base::scoped_nsobject<NSNumber> numID(
         [[NSNumber alloc] initWithInt:futureSessionIDOfTab]);
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h b/chrome/browser/ui/cocoa/infobars/infobar_background_view.h
similarity index 64%
rename from chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h
rename to chrome/browser/ui/cocoa/infobars/infobar_background_view.h
index be72e4a5..062bd49 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h
+++ b/chrome/browser/ui/cocoa/infobars/infobar_background_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_GRADIENT_VIEW_H_
-#define CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_GRADIENT_VIEW_H_
+#ifndef CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_BACKGROUND_VIEW_H_
+#define CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_BACKGROUND_VIEW_H_
 
 #import "chrome/browser/ui/cocoa/vertical_gradient_view.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -11,11 +11,11 @@
 #import <Cocoa/Cocoa.h>
 
 // A custom view that draws the background gradient for an infobar.
-@interface InfoBarGradientView : VerticalGradientView
+@interface InfoBarBackgroundView : VerticalGradientView
 
 // Sets the infobar background color.
 - (void)setInfobarBackgroundColor:(SkColor)color;
 
 @end
 
-#endif  // CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_GRADIENT_VIEW_H_
+#endif  // CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_BACKGROUND_VIEW_H_
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.mm b/chrome/browser/ui/cocoa/infobars/infobar_background_view.mm
similarity index 96%
rename from chrome/browser/ui/cocoa/infobars/infobar_gradient_view.mm
rename to chrome/browser/ui/cocoa/infobars/infobar_background_view.mm
index 148ecdf..1d905be 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_background_view.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.
 
-#include "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h"
+#include "chrome/browser/ui/cocoa/infobars/infobar_background_view.h"
 
 #include "base/mac/scoped_nsobject.h"
 #import "chrome/browser/themes/theme_properties.h"
@@ -14,7 +14,7 @@
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/base/theme_provider.h"
 
-@implementation InfoBarGradientView
+@implementation InfoBarBackgroundView
 
 - (id)initWithFrame:(NSRect)frame {
   self = [super initWithFrame:frame];
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_background_view_unittest.mm b/chrome/browser/ui/cocoa/infobars/infobar_background_view_unittest.mm
new file mode 100644
index 0000000..b04a1a72
--- /dev/null
+++ b/chrome/browser/ui/cocoa/infobars/infobar_background_view_unittest.mm
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 The Chromium 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/infobars/infobar_background_view.h"
+
+#include "base/mac/scoped_nsobject.h"
+#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
+
+namespace {
+
+class InfoBarBackgroundViewTest : public CocoaTest {
+ public:
+  InfoBarBackgroundViewTest() {
+    NSRect frame = NSMakeRect(0, 0, 100, 30);
+    base::scoped_nsobject<InfoBarBackgroundView> view(
+        [[InfoBarBackgroundView alloc] initWithFrame:frame]);
+    view_ = view.get();
+    [[test_window() contentView] addSubview:view_];
+  }
+
+  InfoBarBackgroundView* view_;  // Weak. Retained by view hierarchy.
+};
+
+TEST_VIEW(InfoBarBackgroundViewTest, view_);
+
+// Assert that the view is non-opaque, because otherwise we will end
+// up with findbar painting issues.
+TEST_F(InfoBarBackgroundViewTest, AssertViewNonOpaque) {
+  EXPECT_FALSE([view_ isOpaque]);
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.h b/chrome/browser/ui/cocoa/infobars/infobar_controller.h
index 1668aad6..56b2bfe 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_controller.h
+++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.h
@@ -11,7 +11,7 @@
 #include "base/memory/weak_ptr.h"
 
 class InfoBarCocoa;
-@class InfoBarGradientView;
+@class InfoBarBackgroundView;
 
 namespace infobars {
 class InfoBarDelegate;
@@ -26,7 +26,7 @@
   base::WeakPtr<InfoBarCocoa> infobar_;
 
  @protected
-  IBOutlet InfoBarGradientView* infoBarView_;
+  IBOutlet InfoBarBackgroundView* infoBarView_;
   IBOutlet NSImageView* image_;
   IBOutlet NSTextField* labelPlaceholder_;
   IBOutlet NSButton* okButton_;
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
index 90f026eb..5e8b895 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
@@ -11,10 +11,10 @@
 #import "chrome/browser/ui/cocoa/animatable_view.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/image_button_cell.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_background_view.h"
 #include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
-#import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h"
 #include "chrome/browser/ui/cocoa/l10n_util.h"
 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
 #include "chrome/grit/theme_resources.h"
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view_unittest.mm b/chrome/browser/ui/cocoa/infobars/infobar_gradient_view_unittest.mm
deleted file mode 100644
index 9017aa1e8..0000000
--- a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view_unittest.mm
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2011 The Chromium 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/mac/scoped_nsobject.h"
-#import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h"
-#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
-
-namespace {
-
-class InfoBarGradientViewTest : public CocoaTest {
- public:
-  InfoBarGradientViewTest() {
-    NSRect frame = NSMakeRect(0, 0, 100, 30);
-    base::scoped_nsobject<InfoBarGradientView> view(
-        [[InfoBarGradientView alloc] initWithFrame:frame]);
-    view_ = view.get();
-    [[test_window() contentView] addSubview:view_];
-  }
-
-  InfoBarGradientView* view_;  // Weak. Retained by view hierarchy.
-};
-
-TEST_VIEW(InfoBarGradientViewTest, view_);
-
-// Assert that the view is non-opaque, because otherwise we will end
-// up with findbar painting issues.
-TEST_F(InfoBarGradientViewTest, AssertViewNonOpaque) {
-  EXPECT_FALSE([view_ isOpaque]);
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm b/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm
index 00279c2..1669eaad 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm
@@ -15,8 +15,8 @@
 @implementation InfobarLabelTextField
 
 - (void)drawRect:(NSRect)rect {
-  NSView* infobarGradientView = [self superview];
-  [self cr_drawUsingAncestor:infobarGradientView inRect:rect];
+  NSView* infobarBackgroundView = [self superview];
+  [self cr_drawUsingAncestor:infobarBackgroundView inRect:rect];
   [super drawRect:rect];
 }
 
diff --git a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
index 393cb848..8b313cf 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
@@ -89,7 +89,7 @@
   }
 
   // Looks up a tab based on its tab ID.
-  content::WebContents* FindWebContentsByTabId(SessionID::id_type tab_id) {
+  content::WebContents* FindWebContentsByTabId(SessionID tab_id) {
     auto& all_tabs = AllTabContentses();
     auto tab_id_matches = [tab_id](content::WebContents* web_contents) {
       return SessionTabHelper::IdForTab(web_contents) == tab_id;
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.cc b/chrome/browser/ui/extensions/extension_action_view_controller.cc
index 3b4a6f1..71359114 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller.cc
+++ b/chrome/browser/ui/extensions/extension_action_view_controller.cc
@@ -102,8 +102,8 @@
   if (!ExtensionIsValid())
     return base::string16();
 
-  std::string title =
-      extension_action()->GetTitle(SessionTabHelper::IdForTab(web_contents));
+  std::string title = extension_action()->GetTitle(
+      SessionTabHelper::IdForTab(web_contents).id());
   return base::UTF8ToUTF16(title.empty() ? extension()->name() : title);
 }
 
@@ -118,7 +118,7 @@
     return false;
 
   return extension_action_->GetIsVisible(
-             SessionTabHelper::IdForTab(web_contents)) ||
+             SessionTabHelper::IdForTab(web_contents).id()) ||
          HasBeenBlocked(web_contents);
 }
 
@@ -133,8 +133,8 @@
   if (!ExtensionIsValid())
     return false;
 
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
-  return (tab_id < 0) ? false : extension_action_->HasPopup(tab_id);
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents);
+  return tab_id.is_valid() ? extension_action_->HasPopup(tab_id.id()) : false;
 }
 
 void ExtensionActionViewController::HidePopup() {
@@ -214,7 +214,7 @@
   if (action_runner->RunAction(extension(), grant_tab_permissions) ==
       ExtensionAction::ACTION_SHOW_POPUP) {
     GURL popup_url = extension_action_->GetPopupUrl(
-        SessionTabHelper::IdForTab(web_contents));
+        SessionTabHelper::IdForTab(web_contents).id());
     return GetPreferredPopupViewController()
         ->TriggerPopupWithUrl(show_action, popup_url, grant_tab_permissions);
   }
@@ -370,7 +370,7 @@
 ExtensionActionViewController::GetIconImageSource(
     content::WebContents* web_contents,
     const gfx::Size& size) {
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   std::unique_ptr<IconWithBadgeImageSource> image_source(
       new IconWithBadgeImageSource(size));
 
@@ -410,7 +410,7 @@
   return extension_action_->action_type() ==
              extensions::ActionInfo::TYPE_PAGE &&
          extension_action_->GetIsVisible(
-             SessionTabHelper::IdForTab(web_contents));
+             SessionTabHelper::IdForTab(web_contents).id());
 }
 
 bool ExtensionActionViewController::HasBeenBlocked(
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 1f09733..8e38609 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -354,7 +354,7 @@
       browser()->swap_toolbar_models(&toolbar_model);
     }
 
-    test_toolbar_model_->set_text(text);
+    test_toolbar_model_->set_formatted_full_url(text);
     omnibox_view->Update();
   }
 
diff --git a/chrome/browser/ui/settings_window_manager_chromeos.cc b/chrome/browser/ui/settings_window_manager_chromeos.cc
index f6b69e56..ce64fa8 100644
--- a/chrome/browser/ui/settings_window_manager_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_chromeos.cc
@@ -64,7 +64,7 @@
   params.user_gesture = true;
   params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
   Navigate(&params);
-  settings_session_map_[profile] = params.browser->session_id().id();
+  settings_session_map_[profile] = params.browser->session_id();
   DCHECK(params.browser->is_trusted_source());
 
   for (SettingsWindowManagerObserver& observer : observers_)
@@ -82,7 +82,7 @@
   ProfileSessionMap::const_iterator iter =
       settings_session_map_.find(browser->profile());
   return (iter != settings_session_map_.end() &&
-          iter->second == browser->session_id().id());
+          iter->second == browser->session_id());
 }
 
 SettingsWindowManager::SettingsWindowManager() {}
diff --git a/chrome/browser/ui/settings_window_manager_chromeos.h b/chrome/browser/ui/settings_window_manager_chromeos.h
index 3f6882c..37eddfe 100644
--- a/chrome/browser/ui/settings_window_manager_chromeos.h
+++ b/chrome/browser/ui/settings_window_manager_chromeos.h
@@ -45,7 +45,7 @@
 
  private:
   friend struct base::DefaultSingletonTraits<SettingsWindowManager>;
-  typedef std::map<Profile*, SessionID::id_type> ProfileSessionMap;
+  typedef std::map<Profile*, SessionID> ProfileSessionMap;
 
   SettingsWindowManager();
   ~SettingsWindowManager();
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index 6b44ad7..db0825383 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -103,7 +103,16 @@
 
     // This flag allows sites to access protected media identifiers without
     // getting the user's permission.
-    switches::kUnsafelyAllowProtectedMediaIdentifierForDomain};
+    switches::kUnsafelyAllowProtectedMediaIdentifierForDomain,
+
+    // This flag delays execution of base::TaskPriority::BACKGROUND tasks until
+    // shutdown. The queue of base::TaskPriority::BACKGROUND tasks can increase
+    // memory usage. Also, while it should be possible to use Chrome almost
+    // normally with this flag, it is expected that some non-visible operations
+    // such as writing user data to disk, cleaning caches, reporting metrics or
+    // updating components won't be performed until shutdown.
+    switches::kDisableBackgroundTasks,
+};
 #endif  // OS_ANDROID
 
 // Dangerous feature flags in about:flags for which to display a warning that
diff --git a/chrome/browser/ui/sync/browser_synced_window_delegates_getter.cc b/chrome/browser/ui/sync/browser_synced_window_delegates_getter.cc
index 0963c3b..132bca6 100644
--- a/chrome/browser/ui/sync/browser_synced_window_delegates_getter.cc
+++ b/chrome/browser/ui/sync/browser_synced_window_delegates_getter.cc
@@ -31,7 +31,7 @@
 
 const sync_sessions::SyncedWindowDelegate*
 BrowserSyncedWindowDelegatesGetter::FindById(SessionID id) {
-  Browser* browser = chrome::FindBrowserWithID(id.id());
+  Browser* browser = chrome::FindBrowserWithID(id);
   return (browser != nullptr) ? browser->synced_window_delegate() : nullptr;
 }
 
diff --git a/chrome/browser/ui/tabs/window_activity_watcher.cc b/chrome/browser/ui/tabs/window_activity_watcher.cc
index 2571fb0..ea3a39e 100644
--- a/chrome/browser/ui/tabs/window_activity_watcher.cc
+++ b/chrome/browser/ui/tabs/window_activity_watcher.cc
@@ -34,7 +34,7 @@
   bool operator!=(const WindowMetrics& other) { return !operator==(other); }
 
   // ID for the window, unique within the Chrome session.
-  SessionID::id_type window_id;
+  SessionID window_id;
   WindowMetricsEvent::Type type;
   // TODO(michaelpg): Observe the show state and log when it changes.
   WindowMetricsEvent::ShowState show_state;
@@ -68,7 +68,7 @@
 // Returns a populated WindowMetrics for the browser.
 WindowMetrics CreateMetrics(const Browser* browser, bool is_active) {
   WindowMetrics window_metrics;
-  window_metrics.window_id = browser->session_id().id();
+  window_metrics.window_id = browser->session_id();
 
   switch (browser->type()) {
     case Browser::TYPE_TABBED:
@@ -90,7 +90,7 @@
     return;
 
   ukm::builders::TabManager_WindowMetrics entry(ukm::AssignNewSourceId());
-  entry.SetWindowId(window_metrics.window_id)
+  entry.SetWindowId(window_metrics.window_id.id())
       .SetIsActive(window_metrics.is_active)
       .SetShowState(window_metrics.show_state)
       .SetType(window_metrics.type);
diff --git a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
index 56c95c8f..fb51e99a 100644
--- a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
+++ b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
@@ -281,7 +281,7 @@
   ASSERT_TRUE(action);
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents).id();
   action->SetIsVisible(tab_id, true);
   extensions::ExtensionActionAPI* extension_action_api =
       extensions::ExtensionActionAPI::Get(profile());
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
index d4d740a..a8cad741 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
@@ -33,50 +33,45 @@
   return t1.timestamp > t2.timestamp;
 }
 
-int CreateUniqueID() {
-  static int s_id = 0;
-  ++s_id;
-  return s_id;
+std::string ToSessionTag(SessionID session_id) {
+  return std::string(kBaseSessionTag + base::IntToString(session_id.id()));
 }
 
-std::string ToSessionTag(SessionID::id_type session_id) {
-  return std::string(kBaseSessionTag + base::IntToString(session_id));
+std::string ToSessionName(SessionID session_id) {
+  return std::string(kBaseSessionName + base::IntToString(session_id.id()));
 }
 
-std::string ToSessionName(SessionID::id_type session_id) {
-  return std::string(kBaseSessionName + base::IntToString(session_id));
+std::string ToTabTitle(SessionID session_id,
+                       SessionID window_id,
+                       SessionID tab_id) {
+  return base::StringPrintf(kTabTitleFormat, session_id.id(), window_id.id(),
+                            tab_id.id());
 }
 
-std::string ToTabTitle(SessionID::id_type session_id,
-                       SessionID::id_type window_id,
-                       SessionID::id_type tab_id) {
-  return base::StringPrintf(kTabTitleFormat, session_id, window_id, tab_id);
-}
-
-std::string ToTabUrl(SessionID::id_type session_id,
-                     SessionID::id_type window_id,
-                     SessionID::id_type tab_id) {
+std::string ToTabUrl(SessionID session_id,
+                     SessionID window_id,
+                     SessionID tab_id) {
   return std::string(kBaseTabUrl + ToTabTitle(session_id, window_id, tab_id));
 }
 
 }  // namespace
 
 struct RecentTabsBuilderTestHelper::TabInfo {
-  TabInfo() : id(0) {}
-  SessionID::id_type id;
+  TabInfo() : id(SessionID::InvalidValue()) {}
+  SessionID id;
   base::Time timestamp;
   base::string16 title;
 };
 struct RecentTabsBuilderTestHelper::WindowInfo {
-  WindowInfo() : id(0) {}
+  WindowInfo() : id(SessionID::InvalidValue()) {}
   ~WindowInfo() {}
-  SessionID::id_type id;
+  SessionID id;
   std::vector<TabInfo> tabs;
 };
 struct RecentTabsBuilderTestHelper::SessionInfo {
-  SessionInfo() : id(0) {}
+  SessionInfo() : id(SessionID::InvalidValue()) {}
   ~SessionInfo() {}
-  SessionID::id_type id;
+  SessionID id;
   std::vector<WindowInfo> windows;
 };
 
@@ -90,7 +85,7 @@
 
 void RecentTabsBuilderTestHelper::AddSession() {
   SessionInfo info;
-  info.id = CreateUniqueID();
+  info.id = SessionID::NewUnique();
   sessions_.push_back(info);
 }
 
@@ -98,8 +93,7 @@
   return sessions_.size();
 }
 
-SessionID::id_type RecentTabsBuilderTestHelper::GetSessionID(
-    int session_index) {
+SessionID RecentTabsBuilderTestHelper::GetSessionID(int session_index) {
   return sessions_[session_index].id;
 }
 
@@ -119,7 +113,7 @@
 
 void RecentTabsBuilderTestHelper::AddWindow(int session_index) {
   WindowInfo window_info;
-  window_info.id = CreateUniqueID();
+  window_info.id = SessionID::NewUnique();
   sessions_[session_index].windows.push_back(window_info);
 }
 
@@ -127,8 +121,8 @@
   return sessions_[session_index].windows.size();
 }
 
-SessionID::id_type RecentTabsBuilderTestHelper::GetWindowID(int session_index,
-                                                            int window_index) {
+SessionID RecentTabsBuilderTestHelper::GetWindowID(int session_index,
+                                                   int window_index) {
   return sessions_[session_index].windows[window_index].id;
 }
 
@@ -144,7 +138,7 @@
                                                  base::Time timestamp,
                                                  const base::string16& title) {
   TabInfo tab_info;
-  tab_info.id = CreateUniqueID();
+  tab_info.id = SessionID::NewUnique();
   tab_info.timestamp = timestamp;
   tab_info.title = title;
   sessions_[session_index].windows[window_index].tabs.push_back(tab_info);
@@ -155,9 +149,9 @@
   return sessions_[session_index].windows[window_index].tabs.size();
 }
 
-SessionID::id_type RecentTabsBuilderTestHelper::GetTabID(int session_index,
-                                                         int window_index,
-                                                         int tab_index) {
+SessionID RecentTabsBuilderTestHelper::GetTabID(int session_index,
+                                                int window_index,
+                                                int tab_index) {
   return sessions_[session_index].windows[window_index].tabs[tab_index].id;
 }
 
@@ -250,7 +244,7 @@
 void RecentTabsBuilderTestHelper::BuildSessionSpecifics(
     int session_index,
     sync_pb::SessionSpecifics* meta) {
-  SessionID::id_type session_id = GetSessionID(session_index);
+  SessionID session_id = GetSessionID(session_index);
   meta->set_session_tag(ToSessionTag(session_id));
   sync_pb::SessionHeader* header = meta->mutable_header();
   header->set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_CROS);
@@ -263,12 +257,12 @@
     sync_pb::SessionSpecifics* meta) {
   sync_pb::SessionHeader* header = meta->mutable_header();
   sync_pb::SessionWindow* window = header->add_window();
-  SessionID::id_type window_id = GetWindowID(session_index, window_index);
-  window->set_window_id(window_id);
+  SessionID window_id = GetWindowID(session_index, window_index);
+  window->set_window_id(window_id.id());
   window->set_selected_tab_index(0);
   window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
   for (int i = 0; i < GetTabCount(session_index, window_index); ++i)
-    window->add_tab(GetTabID(session_index, window_index, i));
+    window->add_tab(GetTabID(session_index, window_index, i).id());
 }
 
 void RecentTabsBuilderTestHelper::BuildTabSpecifics(
@@ -276,15 +270,15 @@
     int window_index,
     int tab_index,
     sync_pb::SessionSpecifics* tab_base) {
-  SessionID::id_type session_id = GetSessionID(session_index);
-  SessionID::id_type window_id = GetWindowID(session_index, window_index);
-  SessionID::id_type tab_id = GetTabID(session_index, window_index, tab_index);
+  SessionID session_id = GetSessionID(session_index);
+  SessionID window_id = GetWindowID(session_index, window_index);
+  SessionID tab_id = GetTabID(session_index, window_index, tab_index);
 
   tab_base->set_session_tag(ToSessionTag(session_id));
   tab_base->set_tab_node_id(++max_tab_node_id_);
   sync_pb::SessionTab* tab = tab_base->mutable_tab();
-  tab->set_window_id(window_id);
-  tab->set_tab_id(tab_id);
+  tab->set_window_id(window_id.id());
+  tab->set_tab_id(tab_id.id());
   tab->set_tab_visual_index(1);
   tab->set_current_navigation_index(0);
   tab->set_pinned(true);
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
index b7a020f8..d834326 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h
@@ -29,12 +29,12 @@
 
   void AddSession();
   int GetSessionCount();
-  SessionID::id_type GetSessionID(int session_index);
+  SessionID GetSessionID(int session_index);
   base::Time GetSessionTimestamp(int session_index);
 
   void AddWindow(int session_index);
   int GetWindowCount(int session_index);
-  SessionID::id_type GetWindowID(int session_index, int window_index);
+  SessionID GetWindowID(int session_index, int window_index);
 
   void AddTab(int session_index, int window_index);
   void AddTabWithInfo(int session_index,
@@ -42,9 +42,7 @@
                       base::Time timestamp,
                       const base::string16& title);
   int GetTabCount(int session_index, int window_index);
-  SessionID::id_type GetTabID(int session_index,
-                              int window_index,
-                              int tab_index);
+  SessionID GetTabID(int session_index, int window_index, int tab_index);
   base::Time GetTabTimestamp(int session_index,
                              int window_index,
                              int tab_index);
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
index d3ff9d6..0da68927 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
@@ -205,7 +205,8 @@
     ExtensionAction* action,
     content::WebContents* web_contents,
     bool wants_to_run) {
-  action->SetIsVisible(SessionTabHelper::IdForTab(web_contents), wants_to_run);
+  action->SetIsVisible(SessionTabHelper::IdForTab(web_contents).id(),
+                       wants_to_run);
   extensions::ExtensionActionAPI::Get(profile())->NotifyChange(
       action, web_contents, profile());
 }
diff --git a/chrome/browser/ui/views/harmony/chrome_layout_provider.cc b/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
index acbf8b2..3dba5720 100644
--- a/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
+++ b/chrome/browser/ui/views/harmony/chrome_layout_provider.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "chrome/browser/ui/views/harmony/chrome_typography.h"
 #include "chrome/browser/ui/views/harmony/harmony_layout_provider.h"
+#include "chrome/browser/ui/views/harmony/material_refresh_layout_provider.h"
 #include "ui/base/material_design/material_design_controller.h"
 
 namespace {
@@ -37,6 +38,9 @@
 // static
 std::unique_ptr<views::LayoutProvider>
 ChromeLayoutProvider::CreateLayoutProvider() {
+  if (ui::MaterialDesignController::GetMode() ==
+      ui::MaterialDesignController::MATERIAL_REFRESH)
+    return std::make_unique<MaterialRefreshLayoutProvider>();
   return ui::MaterialDesignController::IsSecondaryUiMaterial()
              ? std::make_unique<HarmonyLayoutProvider>()
              : std::make_unique<ChromeLayoutProvider>();
diff --git a/chrome/browser/ui/views/harmony/material_refresh_layout_provider.cc b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.cc
new file mode 100644
index 0000000..77d5d78
--- /dev/null
+++ b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.cc
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/harmony/material_refresh_layout_provider.h"
+
+int MaterialRefreshLayoutProvider::GetCornerRadiusMetric(
+    ChromeEmphasisMetric emphasis_metric,
+    const gfx::Rect& bounds) const {
+  switch (emphasis_metric) {
+    case EMPHASIS_LOW:
+      return 4;
+    case EMPHASIS_MEDIUM:
+      return 8;
+    case EMPHASIS_HIGH:
+      return std::min(bounds.width(), bounds.height()) / 2;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
diff --git a/chrome/browser/ui/views/harmony/material_refresh_layout_provider.h b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.h
new file mode 100644
index 0000000..47ef6cf
--- /dev/null
+++ b/chrome/browser/ui/views/harmony/material_refresh_layout_provider.h
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_HARMONY_MATERIAL_REFRESH_LAYOUT_PROVIDER_H_
+#define CHROME_BROWSER_UI_VIEWS_HARMONY_MATERIAL_REFRESH_LAYOUT_PROVIDER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/views/harmony/harmony_layout_provider.h"
+
+class MaterialRefreshLayoutProvider : public HarmonyLayoutProvider {
+ public:
+  MaterialRefreshLayoutProvider() = default;
+  ~MaterialRefreshLayoutProvider() override = default;
+
+  int GetCornerRadiusMetric(
+      ChromeEmphasisMetric emphasis_metric,
+      const gfx::Rect& bounds = gfx::Rect()) const override;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_HARMONY_MATERIAL_REFRESH_LAYOUT_PROVIDER_H_
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
index 6f27b6a..d89c4404b 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
@@ -518,7 +518,7 @@
   // destroyed by the time we get this call. Also note parent_window() (if set)
   // may also be destroyed: the call to WindowClosing() may be triggered by
   // parent window destruction tearing down its child windows.
-  Browser* browser = chrome::FindBrowserWithID(session_id_.id());
+  Browser* browser = chrome::FindBrowserWithID(session_id_);
   if (browser && browser->window() && browser->window()->GetLocationBar())
     browser->window()->GetLocationBar()->UpdateZoomViewVisibility();
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index ab4c220..45867e9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -121,6 +121,8 @@
   FRIEND_TEST_ALL_PREFIXES(OmniboxViewViewsTest, MaintainCursorAfterFocusCycle);
   FRIEND_TEST_ALL_PREFIXES(OmniboxViewViewsTest, OnBlur);
   FRIEND_TEST_ALL_PREFIXES(OmniboxViewViewsTest, DoNotNavigateOnDrop);
+  FRIEND_TEST_ALL_PREFIXES(OmniboxViewViewsSteadyStateElisionsTest,
+                           UnelideOnArrowKey);
 
   // Update the field with |text| and set the selection.
   void SetTextAndSelectedRange(const base::string16& text,
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index ca5771d..1e3ee02c 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
@@ -20,6 +21,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/toolbar/test_toolbar_model.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -197,11 +199,12 @@
     return test_api_->GetRenderText()->cursor_enabled();
   }
 
- private:
+ protected:
   // testing::Test:
   void SetUp() override;
   void TearDown() override;
 
+ private:
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfile profile_;
   TemplateURLServiceFactoryTestUtil util_;
@@ -390,7 +393,7 @@
 }
 
 TEST_F(OmniboxViewViewsTest, RevertOnBlur) {
-  toolbar_model()->set_text(base::ASCIIToUTF16("permanent text"));
+  toolbar_model()->set_formatted_full_url(base::ASCIIToUTF16("permanent text"));
   omnibox_view()->model()->ResetDisplayUrls();
   omnibox_view()->RevertAll();
 
@@ -415,3 +418,79 @@
   EXPECT_EQ(base::ASCIIToUTF16("permanent text"), omnibox_view()->text());
   EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
 }
+
+class OmniboxViewViewsSteadyStateElisionsTest : public OmniboxViewViewsTest {
+ protected:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        omnibox::kUIExperimentHideSteadyStateUrlSchemeAndSubdomains);
+
+    OmniboxViewViewsTest::SetUp();
+
+    toolbar_model()->set_formatted_full_url(
+        base::ASCIIToUTF16("https://example.com"));
+    toolbar_model()->set_url_for_display(base::ASCIIToUTF16("example.com"));
+    omnibox_view()->model()->ResetDisplayUrls();
+    omnibox_view()->RevertAll();
+
+    ExpectElidedUrlDisplayed();
+  }
+
+  void ExpectFullUrlDisplayed() {
+    EXPECT_EQ(base::ASCIIToUTF16("https://example.com"),
+              omnibox_view()->text());
+    EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
+  }
+
+  void ExpectElidedUrlDisplayed() {
+    EXPECT_EQ(base::ASCIIToUTF16("example.com"), omnibox_view()->text());
+    EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
+  }
+
+  // Used to access members that are marked private in views::TextField.
+  views::View* omnibox_textfield_view() { return omnibox_view(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(OmniboxViewViewsSteadyStateElisionsTest, StayElidedOnFocus) {
+  // We should not unelide on focus.
+  omnibox_textfield()->OnFocus();
+
+  EXPECT_EQ(OMNIBOX_FOCUS_VISIBLE, omnibox_view()->model()->focus_state());
+  ExpectElidedUrlDisplayed();
+}
+
+TEST_F(OmniboxViewViewsSteadyStateElisionsTest, UnelideOnArrowKey) {
+  omnibox_textfield()->OnFocus();
+  EXPECT_EQ(OMNIBOX_FOCUS_VISIBLE, omnibox_view()->model()->focus_state());
+
+  omnibox_view()->SelectAll(true);
+  EXPECT_TRUE(omnibox_view()->IsSelectAll());
+  ExpectElidedUrlDisplayed();
+
+  // Right key should unelide and move the cursor to the end.
+  omnibox_textfield_view()->OnKeyPressed(
+      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, 0));
+  ExpectFullUrlDisplayed();
+  size_t start, end;
+  omnibox_view()->GetSelectionBounds(&start, &end);
+  EXPECT_EQ(19U, start);
+  EXPECT_EQ(19U, end);
+
+  // Blur and restore the elided URL.
+  omnibox_textfield()->OnBlur();
+  omnibox_textfield()->OnFocus();
+  omnibox_view()->SelectAll(true);
+  ExpectElidedUrlDisplayed();
+
+  // Left key should unelide and move the cursor to the beginning of the elided
+  // part.
+  omnibox_textfield_view()->OnKeyPressed(
+      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LEFT, 0));
+  ExpectFullUrlDisplayed();
+  omnibox_view()->GetSelectionBounds(&start, &end);
+  EXPECT_EQ(8U, start);
+  EXPECT_EQ(8U, end);
+}
diff --git a/chrome/browser/ui/views/payments/payment_request_data_url_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_data_url_browsertest.cc
index 9851dbd0..8fee346 100644
--- a/chrome/browser/ui/views/payments/payment_request_data_url_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_data_url_browsertest.cc
@@ -24,12 +24,19 @@
       "'1.00'}}})).show(); } catch(e) { "
       "document.getElementById('result').innerHTML = e; }\">Data URL "
       "Test</button><div id='result'></div></body></html>");
+
+  // PaymentRequest should not be defined in non-secure context.
+  bool result = true;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      GetActiveWebContents(),
+      "window.domAutomationController.send('PaymentRequest' in window);",
+      &result));
+  ASSERT_FALSE(result);
+
   ASSERT_TRUE(content::ExecuteScript(
       GetActiveWebContents(),
       "(function() { document.getElementById('buy').click(); })();"));
-  ExpectBodyContains(
-      {"SecurityError: Failed to construct 'PaymentRequest': Must be in a "
-       "secure context"});
+  ExpectBodyContains({"PaymentRequest is not defined"});
 }
 
 }  // namespace payments
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index c2a42693..68dd931 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -1151,7 +1151,7 @@
 
       // If all 3 icons are visible, we add an extra left padding for favicon.
       // See comment for |extra_padding_before_content_|.
-      if (!showing_close_button_ || !showing_alert_indicator_)
+      if (!showing_alert_indicator_)
         extra_padding = 0;
 
       showing_icon_ =
@@ -1169,9 +1169,17 @@
       if (!showing_icon_ || !showing_alert_indicator_)
         extra_padding = 0;
 
-      showing_close_button_ =
-          !force_hide_close_button &&
-          close_button_width + extra_padding <= available_width;
+      // For an inactive tab, the close button will be visible only when
+      // it is not forced to hide and the total width can accomodate all 3
+      // icons. When favicon or alert button is not visible, its space
+      // will be occupied by the title of this tab.
+      int title_width =
+          (!showing_icon_ + !showing_alert_indicator_) * favicon_width;
+      if (!force_hide_close_button &&
+          (title_width + close_button_width + extra_padding <=
+           available_width)) {
+        showing_close_button_ = true;
+      }
 
       // If no other controls are visible, show favicon even though we
       // don't have enough space. We'll clip the favicon in PaintChildren().
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index 3dee833..0955298 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -170,6 +170,8 @@
   friend class TabTest;
   friend class TabStripTest;
   FRIEND_TEST_ALL_PREFIXES(TabStripTest, TabCloseButtonVisibilityWhenStacked);
+  FRIEND_TEST_ALL_PREFIXES(TabStripTest,
+                           TabCloseButtonVisibilityWhenNotStacked);
 
   // gfx::AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 5447d0a..75e8ead 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -284,6 +284,8 @@
   FRIEND_TEST_ALL_PREFIXES(TabDragControllerTest, GestureEndShouldEndDragTest);
   FRIEND_TEST_ALL_PREFIXES(TabStripTest, TabForEventWhenStacked);
   FRIEND_TEST_ALL_PREFIXES(TabStripTest, TabCloseButtonVisibilityWhenStacked);
+  FRIEND_TEST_ALL_PREFIXES(TabStripTest,
+                           TabCloseButtonVisibilityWhenNotStacked);
   FRIEND_TEST_ALL_PREFIXES(TabStripTest, ActiveTabWidthWhenTabsAreTiny);
 
   // Used during a drop session of a url. Tracks the position of the drop as
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index 8a546ff..260b684 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -399,6 +399,91 @@
   EXPECT_TRUE(tab3->showing_close_button_);
 }
 
+// Tests that the tab close buttons of non-active tabs are hidden when
+// the tabstrip is not in stacked tab mode and the tab sizes are shrunk
+// into small sizes.
+TEST_P(TabStripTest, TabCloseButtonVisibilityWhenNotStacked) {
+  if (GetParam()) {
+    // TODO(malaykeshav): Fix test failure in touch-optimized UI mode.
+    // https://crbug.com/814847.
+    return;
+  }
+
+  // Set the tab strip width to be wide enough for three tabs to show all
+  // three icons, but not enough for five tabs to show all three icons.
+  tab_strip_->SetBounds(0, 0, 240, 20);
+  controller_->AddTab(0, false);
+  controller_->AddTab(1, true);
+  controller_->AddTab(2, false);
+  ASSERT_EQ(3, tab_strip_->tab_count());
+
+  Tab* tab0 = tab_strip_->tab_at(0);
+  ASSERT_FALSE(tab0->IsActive());
+  Tab* tab1 = tab_strip_->tab_at(1);
+  ASSERT_TRUE(tab1->IsActive());
+  Tab* tab2 = tab_strip_->tab_at(2);
+  ASSERT_FALSE(tab2->IsActive());
+
+  // Ensure this is not in stacked layout mode.
+  ASSERT_FALSE(tab_strip_->touch_layout_.get());
+
+  // Ensure that all tab close buttons are initially visible.
+  EXPECT_TRUE(tab0->showing_close_button_);
+  EXPECT_TRUE(tab1->showing_close_button_);
+  EXPECT_TRUE(tab2->showing_close_button_);
+
+  // Shrink the tab sizes by adding more tabs.
+  // An inactive tab added to the tabstrip, now each tab size is not
+  // big enough to accomodate 3 icons, so it should not show its
+  // tab close button.
+  controller_->AddTab(3, false);
+  Tab* tab3 = tab_strip_->tab_at(3);
+  EXPECT_FALSE(tab3->showing_close_button_);
+
+  // This inactive tab doesn't have alert button, but its favicon and
+  // title would be shown.
+  EXPECT_TRUE(tab3->showing_icon_);
+  EXPECT_FALSE(tab3->showing_alert_indicator_);
+  EXPECT_TRUE(tab3->title_->visible());
+
+  // The active tab's close button still shows.
+  EXPECT_TRUE(tab1->showing_close_button_);
+
+  // An active tab added to the tabstrip should show its tab close
+  // button.
+  controller_->AddTab(4, true);
+  Tab* tab4 = tab_strip_->tab_at(4);
+  ASSERT_TRUE(tab4->IsActive());
+  EXPECT_TRUE(tab4->showing_close_button_);
+
+  // The previous active button is now inactive so its close
+  // button should not show.
+  EXPECT_FALSE(tab1->showing_close_button_);
+
+  // After switching tabs, the previously-active tab should have its
+  // tab close button hidden and the newly-active tab should show
+  // its tab close button.
+  tab_strip_->SelectTab(tab2);
+  ASSERT_FALSE(tab4->IsActive());
+  ASSERT_TRUE(tab2->IsActive());
+  EXPECT_FALSE(tab0->showing_close_button_);
+  EXPECT_FALSE(tab1->showing_close_button_);
+  EXPECT_TRUE(tab2->showing_close_button_);
+  EXPECT_FALSE(tab3->showing_close_button_);
+  EXPECT_FALSE(tab4->showing_close_button_);
+
+  // After closing the active tab, the tab which becomes active should
+  // show its tab close button.
+  tab_strip_->CloseTab(tab2, CLOSE_TAB_FROM_TOUCH);
+  tab2 = nullptr;
+  ASSERT_TRUE(tab3->IsActive());
+  tab_strip_->DoLayout();
+  EXPECT_FALSE(tab0->showing_close_button_);
+  EXPECT_FALSE(tab1->showing_close_button_);
+  EXPECT_TRUE(tab3->showing_close_button_);
+  EXPECT_FALSE(tab4->showing_close_button_);
+}
+
 TEST_P(TabStripTest, GetEventHandlerForOverlappingArea) {
   tab_strip_->SetBounds(0, 0, 1000, 20);
 
diff --git a/chrome/browser/ui/views/task_manager_view_browsertest.cc b/chrome/browser/ui/views/task_manager_view_browsertest.cc
index 393ce73..b463e10 100644
--- a/chrome/browser/ui/views/task_manager_view_browsertest.cc
+++ b/chrome/browser/ui/views/task_manager_view_browsertest.cc
@@ -87,7 +87,7 @@
   }
 
   // Looks up a tab based on its tab ID.
-  content::WebContents* FindWebContentsByTabId(SessionID::id_type tab_id) {
+  content::WebContents* FindWebContentsByTabId(SessionID tab_id) {
     auto& all_tabs = AllTabContentses();
     auto tab_id_matches = [tab_id](content::WebContents* web_contents) {
       return SessionTabHelper::IdForTab(web_contents) == tab_id;
@@ -100,7 +100,7 @@
   // Returns the current TaskManagerTableModel index for a particular tab. Don't
   // cache this value, since it can change whenever the message loop runs.
   int FindRowForTab(content::WebContents* tab) {
-    int32_t tab_id = SessionTabHelper::IdForTab(tab);
+    SessionID tab_id = SessionTabHelper::IdForTab(tab);
     std::unique_ptr<TaskManagerTester> tester =
         TaskManagerTester::Create(base::Closure());
     for (int i = 0; i < tester->GetRowCount(); ++i) {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index b8d2e856..df5a69d 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -153,7 +153,7 @@
 void ToolbarActionView::UpdateState() {
   content::WebContents* web_contents = GetCurrentWebContents();
   SetAccessibleName(view_controller_->GetAccessibleName(web_contents));
-  if (SessionTabHelper::IdForTab(web_contents) < 0)
+  if (!SessionTabHelper::IdForTab(web_contents).is_valid())
     return;
 
   if (!view_controller_->IsEnabled(web_contents) &&
diff --git a/chrome/browser/ui/webui/foreign_session_handler.cc b/chrome/browser/ui/webui/foreign_session_handler.cc
index f42df1d..aa938b1 100644
--- a/chrome/browser/ui/webui/foreign_session_handler.cc
+++ b/chrome/browser/ui/webui/foreign_session_handler.cc
@@ -103,7 +103,7 @@
 // Helper for initializing a boilerplate SessionWindow JSON compatible object.
 std::unique_ptr<base::DictionaryValue> BuildWindowData(
     base::Time modification_time,
-    SessionID::id_type window_id) {
+    SessionID window_id) {
   std::unique_ptr<base::DictionaryValue> dictionary(
       new base::DictionaryValue());
   // The items which are to be written into |dictionary| are also described in
@@ -121,7 +121,7 @@
           : ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
                                    ui::TimeFormat::LENGTH_SHORT, last_synced));
 
-  dictionary->SetInteger("sessionId", window_id);
+  dictionary->SetInteger("sessionId", window_id.id());
   return dictionary;
 }
 
@@ -145,7 +145,7 @@
   if (tab_values->GetSize() == 0)
     return nullptr;
   std::unique_ptr<base::DictionaryValue> dictionary(
-      BuildWindowData(window.timestamp, window.window_id.id()));
+      BuildWindowData(window.timestamp, window.window_id));
   dictionary->Set("tabs", std::move(tab_values));
   return dictionary;
 }
@@ -168,18 +168,17 @@
 void ForeignSessionHandler::OpenForeignSessionTab(
     content::WebUI* web_ui,
     const std::string& session_string_value,
-    SessionID::id_type window_num,
-    SessionID::id_type tab_id,
+    int window_num,
+    SessionID tab_id,
     const WindowOpenDisposition& disposition) {
   sync_sessions::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui);
   if (!open_tabs)
     return;
 
   // We don't actually care about |window_num|, this is just a sanity check.
-  DCHECK_LT(kInvalidId, window_num);
+  DCHECK_LE(0, window_num);
   const ::sessions::SessionTab* tab;
-  if (!open_tabs->GetForeignTab(session_string_value,
-                                SessionID::FromSerializedValue(tab_id), &tab)) {
+  if (!open_tabs->GetForeignTab(session_string_value, tab_id, &tab)) {
     LOG(ERROR) << "Failed to load foreign tab.";
     return;
   }
@@ -195,7 +194,7 @@
 void ForeignSessionHandler::OpenForeignSessionWindows(
     content::WebUI* web_ui,
     const std::string& session_string_value,
-    SessionID::id_type window_num) {
+    int window_num) {
   sync_sessions::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui);
   if (!open_tabs)
     return;
@@ -208,11 +207,12 @@
     return;
   }
   std::vector<const ::sessions::SessionWindow*>::const_iterator iter_begin =
-      windows.begin() + (window_num == kInvalidId ? 0 : window_num);
+      windows.begin() + (window_num < 0 ? 0 : window_num);
   std::vector<const ::sessions::SessionWindow*>::const_iterator iter_end =
-      window_num == kInvalidId ?
-      std::vector<const ::sessions::SessionWindow*>::const_iterator(
-          windows.end()) : iter_begin + 1;
+      window_num < 0
+          ? std::vector<const ::sessions::SessionWindow*>::const_iterator(
+                windows.end())
+          : iter_begin + 1;
   SessionRestore::RestoreForeignSessionWindows(Profile::FromWebUI(web_ui),
                                                iter_begin, iter_end);
 }
@@ -327,37 +327,13 @@
         current_collapsed_sessions->SetBoolean(session_tag, true);
 
       std::unique_ptr<base::ListValue> window_list(new base::ListValue());
-      const std::string group_name =
-          base::FieldTrialList::FindFullName("TabSyncByRecency");
-      if (group_name != "Enabled") {
-        // Order tabs by visual order within window.
-        for (const auto& window_pair : session->windows) {
-          std::unique_ptr<base::DictionaryValue> window_data(
-              SessionWindowToValue(window_pair.second->wrapped_window));
-          if (window_data.get())
-            window_list->Append(std::move(window_data));
-        }
-      } else {
-        // Order tabs by recency. This involves creating a synthetic singleton
-        // window that contains all the tabs of the session.
-        base::Time modification_time;
-        std::vector<const ::sessions::SessionTab*> tabs;
-        open_tabs->GetForeignSessionTabs(session_tag, &tabs);
-        std::unique_ptr<base::ListValue> tab_values(new base::ListValue());
-        for (const ::sessions::SessionTab* tab : tabs) {
-          std::unique_ptr<base::DictionaryValue> tab_value(
-              SessionTabToValue(*tab));
-          if (tab_value.get()) {
-            modification_time = std::max(modification_time, tab->timestamp);
-            tab_values->Append(std::move(tab_value));
-          }
-        }
-        if (tab_values->GetSize() != 0) {
-          std::unique_ptr<base::DictionaryValue> window_data(
-              BuildWindowData(modification_time, 1));
-          window_data->Set("tabs", std::move(tab_values));
+
+      // Order tabs by visual order within window.
+      for (const auto& window_pair : session->windows) {
+        std::unique_ptr<base::DictionaryValue> window_data(
+            SessionWindowToValue(window_pair.second->wrapped_window));
+        if (window_data)
           window_list->Append(std::move(window_data));
-        }
       }
 
       session_data->Set("windows", std::move(window_list));
@@ -390,7 +366,7 @@
 
   // Extract window number.
   std::string window_num_str;
-  int window_num = kInvalidId;
+  int window_num = -1;
   if (num_args >= 2 && (!args->GetString(1, &window_num_str) ||
       !base::StringToInt(window_num_str, &window_num))) {
     LOG(ERROR) << "Failed to extract window number.";
@@ -399,14 +375,15 @@
 
   // Extract tab id.
   std::string tab_id_str;
-  SessionID::id_type tab_id = kInvalidId;
+  SessionID::id_type tab_id_value = 0;
   if (num_args >= 3 && (!args->GetString(2, &tab_id_str) ||
-      !base::StringToInt(tab_id_str, &tab_id))) {
+                        !base::StringToInt(tab_id_str, &tab_id_value))) {
     LOG(ERROR) << "Failed to extract tab SessionID.";
     return;
   }
 
-  if (tab_id != kInvalidId) {
+  SessionID tab_id = SessionID::FromSerializedValue(tab_id_value);
+  if (tab_id.is_valid()) {
     WindowOpenDisposition disposition = webui::GetDispositionFromClick(args, 3);
     OpenForeignSessionTab(
         web_ui(), session_string_value, window_num, tab_id, disposition);
diff --git a/chrome/browser/ui/webui/foreign_session_handler.h b/chrome/browser/ui/webui/foreign_session_handler.h
index d0cb245..81712db1 100644
--- a/chrome/browser/ui/webui/foreign_session_handler.h
+++ b/chrome/browser/ui/webui/foreign_session_handler.h
@@ -30,9 +30,6 @@
 class ForeignSessionHandler : public content::WebUIMessageHandler,
                               public syncer::SyncServiceObserver {
  public:
-  // Invalid value, used to note that we don't have a tab or window number.
-  static const int kInvalidId = -1;
-
   // WebUIMessageHandler implementation.
   void RegisterMessages() override;
 
@@ -43,13 +40,13 @@
 
   static void OpenForeignSessionTab(content::WebUI* web_ui,
                                     const std::string& session_string_value,
-                                    SessionID::id_type window_num,
-                                    SessionID::id_type tab_id,
+                                    int window_num,
+                                    SessionID tab_id,
                                     const WindowOpenDisposition& disposition);
 
   static void OpenForeignSessionWindows(content::WebUI* web_ui,
                                         const std::string& session_string_value,
-                                        SessionID::id_type window_num);
+                                        int window_num);
 
   // Returns a pointer to the current session model associator or NULL.
   static sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate(
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui.cc b/chrome/browser/ui/webui/media_router/media_router_ui.cc
index 6be6429..270006388 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -467,9 +467,9 @@
   }
 
   initiator_ = initiator;
-  SessionID::id_type tab_id = SessionTabHelper::IdForTab(initiator);
-  if (tab_id != -1) {
-    MediaSource mirroring_source(MediaSourceForTab(tab_id));
+  SessionID tab_id = SessionTabHelper::IdForTab(initiator);
+  if (tab_id.is_valid()) {
+    MediaSource mirroring_source(MediaSourceForTab(tab_id.id()));
     query_result_manager_->SetSourcesForCastMode(MediaCastMode::TAB_MIRROR,
                                                  {mirroring_source}, origin);
   }
@@ -618,8 +618,8 @@
     GURL url = media_router_file_dialog_->GetLastSelectedFileUrl();
     tab_contents = OpenTabWithUrl(url);
 
-    SessionID::id_type tab_id = SessionTabHelper::IdForTab(tab_contents);
-    source_id = MediaSourceForTab(tab_id).id();
+    SessionID tab_id = SessionTabHelper::IdForTab(tab_contents);
+    source_id = MediaSourceForTab(tab_id.id()).id();
 
     SetLocalFileRouteParameters(sink_id, &origin, url, tab_contents,
                                 &route_response_callbacks, &timeout,
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
index 6a6e17ef..b6a7d62 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -462,8 +462,8 @@
 
 TEST_F(MediaRouterUITest, UIMediaRoutesObserverAssignsCurrentCastModes) {
   CreateMediaRouterUI(profile());
-  SessionID::id_type tab_id = SessionTabHelper::IdForTab(web_contents());
-  MediaSource media_source_1(MediaSourceForTab(tab_id));
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents());
+  MediaSource media_source_1(MediaSourceForTab(tab_id.id()));
   MediaSource media_source_2("mediaSource");
   MediaSource media_source_3(MediaSourceForDesktop());
   std::unique_ptr<MediaRouterUI::UIMediaRoutesObserver> observer(
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index cf0c0afa0..c5e55c53 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -105,7 +105,6 @@
     "elements/transient_element.h",
     "elements/ui_element.cc",
     "elements/ui_element.h",
-    "elements/ui_element_iterator.h",
     "elements/ui_element_name.cc",
     "elements/ui_element_name.h",
     "elements/ui_element_type.cc",
@@ -278,7 +277,6 @@
     "elements/text_unittest.cc",
     "elements/throbber_unittest.cc",
     "elements/transient_element_unittest.cc",
-    "elements/ui_element_iterator_unittest.cc",
     "elements/ui_element_unittest.cc",
     "elements/url_text_unittest.cc",
     "elements/vector_icon_button_unittest.cc",
diff --git a/chrome/browser/vr/elements/ui_element.cc b/chrome/browser/vr/elements/ui_element.cc
index 455c2c9..c39a374 100644
--- a/chrome/browser/vr/elements/ui_element.cc
+++ b/chrome/browser/vr/elements/ui_element.cc
@@ -113,9 +113,13 @@
 }
 
 UiElement* UiElement::GetDescendantByType(UiElementType type) {
-  for (auto& descendant : *this) {
-    if (descendant.type() == type)
-      return &descendant;
+  if (type_ == type)
+    return this;
+
+  for (auto& child : children_) {
+    auto* result = child->GetDescendantByType(type);
+    if (result)
+      return result;
   }
   return nullptr;
 }
@@ -596,11 +600,15 @@
 }
 
 void UiElement::AddChild(std::unique_ptr<UiElement> child) {
+  for (UiElement* current = this; current; current = current->parent())
+    current->set_descendants_updated(true);
   child->parent_ = this;
   children_.push_back(std::move(child));
 }
 
 std::unique_ptr<UiElement> UiElement::RemoveChild(UiElement* to_remove) {
+  for (UiElement* current = this; current; current = current->parent())
+    current->set_descendants_updated(true);
   DCHECK_EQ(this, to_remove->parent_);
   to_remove->parent_ = nullptr;
   size_t old_size = children_.size();
@@ -621,12 +629,16 @@
   bindings_.push_back(std::move(binding));
 }
 
-void UiElement::UpdateBindings() {
+void UiElement::UpdateBindingsRecursive() {
   updated_bindings_this_frame_ = false;
   for (auto& binding : bindings_) {
     if (binding->Update())
       updated_bindings_this_frame_ = true;
   }
+  set_update_phase(UiElement::kUpdatedBindings);
+  for (auto& child : children_) {
+    child->UpdateBindingsRecursive();
+  }
 }
 
 gfx::Point3F UiElement::GetCenter() const {
diff --git a/chrome/browser/vr/elements/ui_element.h b/chrome/browser/vr/elements/ui_element.h
index fcaccee..93d86a3 100644
--- a/chrome/browser/vr/elements/ui_element.h
+++ b/chrome/browser/vr/elements/ui_element.h
@@ -19,7 +19,6 @@
 #include "chrome/browser/vr/databinding/binding_base.h"
 #include "chrome/browser/vr/elements/corner_radii.h"
 #include "chrome/browser/vr/elements/draw_phase.h"
-#include "chrome/browser/vr/elements/ui_element_iterator.h"
 #include "chrome/browser/vr/elements/ui_element_name.h"
 #include "chrome/browser/vr/elements/ui_element_type.h"
 #include "chrome/browser/vr/model/camera_model.h"
@@ -355,7 +354,7 @@
     return bindings_;
   }
 
-  void UpdateBindings();
+  void UpdateBindingsRecursive();
 
   gfx::Point3F GetCenter() const;
   gfx::Vector3dF GetNormal() const;
@@ -421,23 +420,6 @@
     return children_;
   }
 
-  typedef ForwardUiElementIterator iterator;
-  typedef ConstForwardUiElementIterator const_iterator;
-  typedef ReverseUiElementIterator reverse_iterator;
-  typedef ConstReverseUiElementIterator const_reverse_iterator;
-
-  iterator begin() { return iterator(this); }
-  iterator end() { return iterator(nullptr); }
-  const_iterator begin() const { return const_iterator(this); }
-  const_iterator end() const { return const_iterator(nullptr); }
-
-  reverse_iterator rbegin() { return reverse_iterator(this); }
-  reverse_iterator rend() { return reverse_iterator(nullptr); }
-  const_reverse_iterator rbegin() const { return const_reverse_iterator(this); }
-  const_reverse_iterator rend() const {
-    return const_reverse_iterator(nullptr);
-  }
-
   void set_update_phase(UpdatePhase phase) { phase_ = phase; }
 
   // This is true for all elements that respect the given view model matrix. If
@@ -486,6 +468,9 @@
     resizable_by_layout_ = resizable;
   }
 
+  bool descendants_updated() const { return descendants_updated_; }
+  void set_descendants_updated(bool updated) { descendants_updated_ = updated; }
+
  protected:
   Animation& animation() { return animation_; }
 
@@ -623,6 +608,10 @@
   UiElement* parent_ = nullptr;
   std::vector<std::unique_ptr<UiElement>> children_;
 
+  // This is true if a descendant has been added and the total list has not yet
+  // been collected by the scene.
+  bool descendants_updated_ = false;
+
   std::vector<std::unique_ptr<BindingBase>> bindings_;
 
   UpdatePhase phase_ = kClean;
diff --git a/chrome/browser/vr/elements/ui_element_iterator.h b/chrome/browser/vr/elements/ui_element_iterator.h
deleted file mode 100644
index f92b1da..0000000
--- a/chrome/browser/vr/elements/ui_element_iterator.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_ITERATOR_H_
-#define CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_ITERATOR_H_
-
-#include <vector>
-
-namespace vr {
-
-class UiElement;
-
-// The UiElementIterator traverses a UiElement subtree. Do not use this class
-// directly. You should, instead, use UiElement::end/begin/rend/rbegin. NB: you
-// may find base::Reversed handy if you're doing reverse iteration.
-template <typename T, typename U>
-class UiElementIterator {
- public:
-  explicit UiElementIterator(T* root) {
-    current_ = U::Increment(&index_stack_, root, true);
-  }
-  ~UiElementIterator() {}
-
-  void operator++() { current_ = U::Increment(&index_stack_, current_, false); }
-  void operator++(int) {
-    current_ = U::Increment(&index_stack_, current_, false);
-  }
-  T& operator*() { return *current_; }
-  bool operator!=(const UiElementIterator& rhs) {
-    return current_ != rhs.current_ ||
-           index_stack_.empty() != rhs.index_stack_.empty();
-  }
-
- private:
-  T* current_ = nullptr;
-
-  // The iterator maintains a stack of indices which represent the current index
-  // in each list of children along the ancestor path.
-  std::vector<size_t> index_stack_;
-};
-
-template <typename T>
-struct ForwardIncrementer {
-  static T* Increment(std::vector<size_t>* index_stack, T* e, bool init) {
-    if (!e || init)
-      return e;
-
-    if (!e->children().empty()) {
-      index_stack->push_back(0lu);
-      return e->children().front().get();
-    }
-
-    while (e->parent() && !index_stack->empty() &&
-           index_stack->back() + 1lu >= e->parent()->children().size()) {
-      index_stack->pop_back();
-      e = e->parent();
-    }
-
-    if (!e->parent() || index_stack->empty())
-      return nullptr;
-
-    index_stack->back()++;
-    return e->parent()->children()[index_stack->back()].get();
-  }
-};
-
-template <typename T>
-struct ReverseIncrementer {
-  static T* Increment(std::vector<size_t>* index_stack, T* e, bool init) {
-    if (!e || (index_stack->empty() && !init))
-      return nullptr;
-
-    bool should_descend = false;
-    if (index_stack->empty()) {
-      should_descend = true;
-    } else if (e->parent() && index_stack->back() > 0lu) {
-      index_stack->back()--;
-      e = e->parent()->children()[index_stack->back()].get();
-      should_descend = true;
-    }
-
-    if (should_descend) {
-      while (!e->children().empty()) {
-        index_stack->push_back(e->children().size() - 1lu);
-        e = e->children().back().get();
-      }
-      return e;
-    }
-
-    index_stack->pop_back();
-    return e->parent();
-  }
-};
-
-typedef UiElementIterator<UiElement, ForwardIncrementer<UiElement>>
-    ForwardUiElementIterator;
-typedef UiElementIterator<const UiElement, ForwardIncrementer<const UiElement>>
-    ConstForwardUiElementIterator;
-typedef UiElementIterator<UiElement, ReverseIncrementer<UiElement>>
-    ReverseUiElementIterator;
-typedef UiElementIterator<const UiElement, ReverseIncrementer<const UiElement>>
-    ConstReverseUiElementIterator;
-
-}  // namespace vr
-
-#endif  // CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_ITERATOR_H_
diff --git a/chrome/browser/vr/elements/ui_element_iterator_unittest.cc b/chrome/browser/vr/elements/ui_element_iterator_unittest.cc
deleted file mode 100644
index 59887ee..0000000
--- a/chrome/browser/vr/elements/ui_element_iterator_unittest.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/vr/elements/ui_element.h"
-
-#include "base/containers/adapters.h"
-#include "base/test/gtest_util.h"
-#include "chrome/browser/vr/ui_scene.h"
-
-namespace vr {
-
-namespace {
-
-// Constructs a tree of the following form
-// 1 kRoot
-//   2 k2dBrowsingRoot
-//     3 kFloor
-//     4 k2dBrowsingContentGroup
-//       5 kBackplane
-//       6 kContentQuad
-//       7 kUrlBar
-//     8 kCeiling
-void MakeTree(UiScene* scene) {
-  auto element = std::make_unique<UiElement>();
-  element->SetName(k2dBrowsingRoot);
-  scene->AddUiElement(kRoot, std::move(element));
-
-  element = std::make_unique<UiElement>();
-  element->SetName(kFloor);
-  scene->AddUiElement(k2dBrowsingRoot, std::move(element));
-
-  element = std::make_unique<UiElement>();
-  element->SetName(k2dBrowsingContentGroup);
-  scene->AddUiElement(k2dBrowsingRoot, std::move(element));
-
-  element = std::make_unique<UiElement>();
-  element->SetName(kBackplane);
-  scene->AddUiElement(k2dBrowsingContentGroup, std::move(element));
-
-  element = std::make_unique<UiElement>();
-  element->SetName(kContentQuad);
-  scene->AddUiElement(k2dBrowsingContentGroup, std::move(element));
-
-  element = std::make_unique<UiElement>();
-  element->SetName(kUrlBar);
-  scene->AddUiElement(k2dBrowsingContentGroup, std::move(element));
-
-  element = std::make_unique<UiElement>();
-  element->SetName(kCeiling);
-  scene->AddUiElement(k2dBrowsingRoot, std::move(element));
-}
-
-template <typename T>
-void CollectElements(T* e, std::vector<T*>* elements) {
-  elements->push_back(e);
-  for (auto& child : e->children()) {
-    CollectElements(child.get(), elements);
-  }
-}
-
-}  // namespace
-
-struct UiElementIteratorTestCase {
-  UiElementName root;
-  size_t num_elements_in_subtree;
-};
-
-class UiElementIteratorTest
-    : public ::testing::TestWithParam<UiElementIteratorTestCase> {};
-
-TEST_P(UiElementIteratorTest, VerifyTraversal) {
-  UiScene scene;
-  MakeTree(&scene);
-  std::vector<UiElement*> elements;
-  CollectElements(scene.GetUiElementByName(GetParam().root), &elements);
-
-  size_t i = 0;
-  for (auto& e : *(scene.GetUiElementByName(GetParam().root))) {
-    EXPECT_GT(elements.size(), i);
-    EXPECT_EQ(elements[i++]->id(), e.id());
-  }
-  EXPECT_EQ(elements.size(), i);
-  EXPECT_EQ(GetParam().num_elements_in_subtree, i);
-
-  i = 0;
-  for (auto& e : *const_cast<const UiElement*>(
-           scene.GetUiElementByName(GetParam().root))) {
-    EXPECT_GT(elements.size(), i);
-    EXPECT_EQ(elements[i++]->id(), e.id());
-  }
-  EXPECT_EQ(elements.size(), i);
-  EXPECT_EQ(GetParam().num_elements_in_subtree, i);
-
-  i = 0;
-  for (auto& e : base::Reversed(*scene.GetUiElementByName(GetParam().root))) {
-    EXPECT_GT(elements.size(), i);
-    EXPECT_EQ(elements[elements.size() - i++ - 1lu]->id(), e.id());
-  }
-  EXPECT_EQ(elements.size(), i);
-  EXPECT_EQ(GetParam().num_elements_in_subtree, i);
-
-  i = 0;
-  for (auto& e : base::Reversed(*const_cast<const UiElement*>(
-           scene.GetUiElementByName(GetParam().root)))) {
-    EXPECT_GT(elements.size(), i);
-    EXPECT_EQ(elements[elements.size() - i++ - 1lu]->id(), e.id());
-  }
-  EXPECT_EQ(elements.size(), i);
-  EXPECT_EQ(GetParam().num_elements_in_subtree, i);
-}
-
-const std::vector<UiElementIteratorTestCase> iterator_test_cases = {
-    {kRoot, 8},
-    {k2dBrowsingContentGroup, 4},
-    {kCeiling, 1},
-};
-
-INSTANTIATE_TEST_CASE_P(UiElementIteratorTestCases,
-                        UiElementIteratorTest,
-                        ::testing::ValuesIn(iterator_test_cases));
-
-}  // namespace vr
diff --git a/chrome/browser/vr/elements/ui_element_name.cc b/chrome/browser/vr/elements/ui_element_name.cc
index 5bded7a..26cbaa15 100644
--- a/chrome/browser/vr/elements/ui_element_name.cc
+++ b/chrome/browser/vr/elements/ui_element_name.cc
@@ -110,8 +110,6 @@
     "kPermissionDialogBackplane",
     "kWebVrUrlToastTransientParent",
     "kWebVrUrlToast",
-    "kExclusiveScreenToastTransientParent",
-    "kExclusiveScreenToast",
     "kWebVrExclusiveScreenToast",
     "kPlatformToastTransientParent",
     "kPlatformToast",
diff --git a/chrome/browser/vr/elements/ui_element_name.h b/chrome/browser/vr/elements/ui_element_name.h
index b862f74..eff40db 100644
--- a/chrome/browser/vr/elements/ui_element_name.h
+++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -109,8 +109,6 @@
   kPermissionDialogBackplane,
   kWebVrUrlToastTransientParent,
   kWebVrUrlToast,
-  kExclusiveScreenToastTransientParent,
-  kExclusiveScreenToast,
   kWebVrExclusiveScreenToast,
   kPlatformToastTransientParent,
   kPlatformToast,
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.cc b/chrome/browser/vr/metrics/session_metrics_helper.cc
index f9494060..4e8092d 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.cc
+++ b/chrome/browser/vr/metrics/session_metrics_helper.cc
@@ -290,13 +290,26 @@
   }
 }
 
+void SessionMetricsHelper::RecordPresentationStartAction(
+    PresentationStartAction action) {
+  if (!presentation_session_tracker_ || mode_ != Mode::kWebXrVrPresentation) {
+    pending_presentation_start_action_ = action;
+  } else {
+    presentation_session_tracker_->ukm_entry()->SetStartAction(action);
+    pending_presentation_start_action_ = base::nullopt;
+  }
+}
+
 void SessionMetricsHelper::ReportRequestPresent() {
   // If we're not in VR, log this as an entry into VR from 2D.
   if (mode_ == Mode::kNoVr) {
     RecordVrStartAction(VrStartAction::kPresentationRequest);
-    // TODO(offenwanger): Record entered presentation from 2D.
+    RecordPresentationStartAction(
+        PresentationStartAction::kRequestFrom2dBrowsing);
+  } else {
+    RecordPresentationStartAction(
+        PresentationStartAction::kRequestFromVrBrowsing);
   }
-  // TODO(offenwanger): Else record entered presentation from VR.
 }
 
 void SessionMetricsHelper::MaybeSetVrStartAction(VrStartAction action) {
@@ -471,6 +484,14 @@
       SessionTracker<ukm::builders::XR_WebXR_PresentationSession>>(
       std::make_unique<ukm::builders::XR_WebXR_PresentationSession>(
           ukm::GetSourceIdForWebContentsDocument(web_contents())));
+
+  if (!pending_presentation_start_action_) {
+    pending_presentation_start_action_ = PresentationStartAction::kOther;
+  }
+
+  presentation_session_tracker_->ukm_entry()->SetStartAction(
+      *pending_presentation_start_action_);
+  pending_presentation_start_action_ = base::nullopt;
 }
 
 void SessionMetricsHelper::OnExitPresentation() {
@@ -617,6 +638,11 @@
           SessionTracker<ukm::builders::XR_WebXR_PresentationSession>>(
           std::make_unique<ukm::builders::XR_WebXR_PresentationSession>(
               ukm::GetSourceIdForWebContentsDocument(web_contents())));
+      if (pending_presentation_start_action_) {
+        presentation_session_tracker_->ukm_entry()->SetStartAction(
+            *pending_presentation_start_action_);
+        pending_presentation_start_action_ = base::nullopt;
+      }
     }
 
     num_session_navigation_++;
diff --git a/chrome/browser/vr/metrics/session_metrics_helper.h b/chrome/browser/vr/metrics/session_metrics_helper.h
index 90864cfc9..6701613 100644
--- a/chrome/browser/vr/metrics/session_metrics_helper.h
+++ b/chrome/browser/vr/metrics/session_metrics_helper.h
@@ -37,6 +37,24 @@
   kVrStartActionLast = kIntentLaunch,
 };
 
+// The source of a request to enter XR Presentation.
+enum PresentationStartAction {
+  // A catch all for methods of Presentation entry that are not otherwise
+  // logged.
+  kOther = 0,
+  // The user triggered a presentation request on a page in 2D, probably by
+  // clicking an enter VR button.
+  kRequestFrom2dBrowsing = 1,
+  // The user triggered a presentation request on a page in VR browsing,
+  // probably by clicking an enter VR button.
+  kRequestFromVrBrowsing = 2,
+  // The user activated a headset on a page that listens for headset activations
+  // and requests presentation.
+  kHeadsetActivation = 3,
+  // The user opened a deep linked app, probably from the Daydream homescreen.
+  kDeepLinkedApp = 4,
+};
+
 // SessionTimer will monitor the time between calls to StartSession and
 // StopSession.  It will combine multiple segments into a single session if they
 // are sufficiently close in time.  It will also only include segments if they
@@ -142,6 +160,7 @@
   void RecordUrlRequested(GURL url, NavigationMethod method);
 
   void RecordVrStartAction(VrStartAction action);
+  void RecordPresentationStartAction(PresentationStartAction action);
   void ReportRequestPresent();
 
  private:
@@ -195,6 +214,7 @@
   NavigationMethod last_url_request_method_;
 
   base::Optional<VrStartAction> pending_page_session_start_action_;
+  base::Optional<PresentationStartAction> pending_presentation_start_action_;
 
   int num_videos_playing_ = 0;
   int num_session_navigation_ = 0;
diff --git a/chrome/browser/vr/test/ui_test.cc b/chrome/browser/vr/test/ui_test.cc
index 0fb58867..30773ad 100644
--- a/chrome/browser/vr/test/ui_test.cc
+++ b/chrome/browser/vr/test/ui_test.cc
@@ -61,6 +61,13 @@
   return WillElementFaceCamera(element);
 }
 
+int NumVisibleInTreeRecursive(const UiElement* element) {
+  int visible = WillElementBeVisible(element) ? 1 : 0;
+  for (auto& child : element->children())
+    visible += NumVisibleInTreeRecursive(child.get());
+  return visible;
+}
+
 }  // namespace
 
 UiTest::UiTest() {}
@@ -133,18 +140,18 @@
     const std::set<UiElementName>& names) const {
   OnBeginFrame();
   SCOPED_TRACE(trace_context);
-  for (const auto& element : scene_->root_element()) {
-    SCOPED_TRACE(element.DebugName());
-    UiElementName name = element.name();
-    UiElementName owner_name = element.owner_name_for_test();
-    if (element.draw_phase() == kPhaseNone && owner_name == kNone) {
+  for (auto* element : scene_->GetAllElements()) {
+    SCOPED_TRACE(element->DebugName());
+    UiElementName name = element->name();
+    UiElementName owner_name = element->owner_name_for_test();
+    if (element->draw_phase() == kPhaseNone && owner_name == kNone) {
       EXPECT_TRUE(names.find(name) == names.end());
       continue;
     }
     if (name == kNone)
       name = owner_name;
     bool should_be_visible = (names.find(name) != names.end());
-    EXPECT_EQ(WillElementBeVisible(&element), should_be_visible);
+    EXPECT_EQ(WillElementBeVisible(element), should_be_visible);
   }
 }
 
@@ -155,12 +162,7 @@
   if (!root) {
     return 0;
   }
-  int visible = 0;
-  for (const auto& element : *root) {
-    if (WillElementBeVisible(&element))
-      visible++;
-  }
-  return visible;
+  return NumVisibleInTreeRecursive(root);
 }
 
 bool UiTest::VerifyIsAnimating(const std::set<UiElementName>& names,
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc
index 4dc7a73f..5e73d6a7 100644
--- a/chrome/browser/vr/ui.cc
+++ b/chrome/browser/vr/ui.cc
@@ -155,6 +155,9 @@
     case UiUnsupportedMode::kNeedsKeyboardUpdate:
       model_->active_modal_prompt_type = kModalPromptTypeUpdateKeyboard;
       return;
+    // kSearchEnginePromo should DOFF directly. It should never try to change
+    // the state of UI.
+    case UiUnsupportedMode::kSearchEnginePromo:
     case UiUnsupportedMode::kCount:
       NOTREACHED();  // Should never be used as a mode (when |enabled| is true).
       return;
diff --git a/chrome/browser/vr/ui_input_manager.cc b/chrome/browser/vr/ui_input_manager.cc
index bf72bcff..ddadcbb 100644
--- a/chrome/browser/vr/ui_input_manager.cc
+++ b/chrome/browser/vr/ui_input_manager.cc
@@ -57,13 +57,15 @@
   return false;
 }
 
-void HitTestElements(UiElement* root_element,
+void HitTestElements(UiScene* scene,
                      ReticleModel* reticle_model,
                      HitTestRequest* request) {
+  auto& all_elements = scene->GetAllElements();
+
   std::vector<const UiElement*> elements;
-  for (auto& element : *root_element) {
-    if (element.IsVisible()) {
-      elements.push_back(&element);
+  for (auto* element : all_elements) {
+    if (element->IsVisible()) {
+      elements.push_back(element);
     }
   }
 
@@ -374,7 +376,7 @@
   request.ray_origin = ray_origin;
   request.ray_target = reticle_model->target_point;
   request.max_distance_to_plane = distance_limit;
-  HitTestElements(&scene_->root_element(), reticle_model, &request);
+  HitTestElements(scene_, reticle_model, &request);
 }
 
 void UiInputManager::UpdateQuiescenceState(
diff --git a/chrome/browser/vr/ui_scene.cc b/chrome/browser/vr/ui_scene.cc
index ddc21d8..e83b7ba 100644
--- a/chrome/browser/vr/ui_scene.cc
+++ b/chrome/browser/vr/ui_scene.cc
@@ -24,16 +24,42 @@
 namespace {
 
 template <typename P>
-UiScene::Elements GetVisibleElements(UiElement* root,
-                                     P predicate) {
+UiScene::Elements GetVisibleElements(
+    const std::vector<UiElement*>& all_elements,
+    P predicate) {
   UiScene::Elements elements;
-  for (auto& element : *root) {
-    if (element.IsVisible() && predicate(&element))
-      elements.push_back(&element);
+  for (auto* element : all_elements) {
+    if (element->IsVisible() && predicate(element))
+      elements.push_back(element);
   }
   return elements;
 }
 
+void GetAllElementsRecursive(std::vector<UiElement*>* elements, UiElement* e) {
+  e->set_descendants_updated(false);
+  elements->push_back(e);
+  for (auto& child : e->children())
+    GetAllElementsRecursive(elements, child.get());
+}
+
+template <typename P>
+UiElement* FindElement(UiElement* e, P predicate) {
+  if (predicate.Run(e))
+    return e;
+  for (auto& child : e->children()) {
+    if (UiElement* result = FindElement(child.get(), predicate)) {
+      return result;
+    }
+  }
+  return nullptr;
+}
+
+void InitializeElementRecursive(UiElement* e, SkiaSurfaceProvider* provider) {
+  e->Initialize(provider);
+  for (auto& child : e->children())
+    InitializeElementRecursive(child.get(), provider);
+}
+
 }  // namespace
 
 void UiScene::AddUiElement(UiElementName parent,
@@ -73,22 +99,20 @@
     TRACE_EVENT0("gpu", "UiScene::OnBeginFrame.UpdateBindings");
 
     // Propagate updates across bindings.
-    for (auto& element : *root_element_) {
-      element.UpdateBindings();
-      element.set_update_phase(UiElement::kUpdatedBindings);
-    }
+    root_element_->UpdateBindingsRecursive();
   }
 
+  auto& elements = GetAllElements();
   {
     TRACE_EVENT0("gpu", "UiScene::OnBeginFrame.UpdateAnimationsAndOpacity");
 
     // Process all animations and pre-binding work. I.e., induce any
     // time-related "dirtiness" on the scene graph.
-    for (auto& element : *root_element_) {
-      element.set_update_phase(UiElement::kDirty);
-      if ((element.DoBeginFrame(current_time, head_pose) ||
-           element.updated_bindings_this_frame()) &&
-          (element.IsVisible() || element.updated_visiblity_this_frame())) {
+    for (auto* element : elements) {
+      element->set_update_phase(UiElement::kDirty);
+      if ((element->DoBeginFrame(current_time, head_pose) ||
+           element->updated_bindings_this_frame()) &&
+          (element->IsVisible() || element->updated_visiblity_this_frame())) {
         scene_dirty = true;
       }
     }
@@ -104,21 +128,21 @@
     // Textures will have to know what their size would be, if they were to draw
     // with their current state, and changing anything other than texture
     // synchronously in response to input should be prohibited.
-    for (auto& element : *root_element_) {
-      element.set_update_phase(UiElement::kUpdatedTexturesAndSizes);
+    for (auto* element : elements) {
+      element->set_update_phase(UiElement::kUpdatedTexturesAndSizes);
     }
     if (root_element_->SizeAndLayOut())
       scene_dirty = true;
-    for (auto& element : *root_element_) {
-      element.set_update_phase(UiElement::kUpdatedLayout);
+    for (auto* element : elements) {
+      element->set_update_phase(UiElement::kUpdatedLayout);
     }
   }
 
   if (!scene_dirty) {
     // Nothing to update, so set all elements to the final update phase and
     // return early.
-    for (auto& element : *root_element_) {
-      element.set_update_phase(UiElement::kUpdatedWorldSpaceTransform);
+    for (auto* element : elements) {
+      element->set_update_phase(UiElement::kUpdatedWorldSpaceTransform);
     }
     return false;
   }
@@ -139,8 +163,8 @@
   TRACE_EVENT0("gpu", "UiScene::UpdateTextures");
   bool needs_redraw = false;
   // Update textures and sizes.
-  for (auto& element : *root_element_) {
-    if (element.PrepareToDraw())
+  for (auto* element : GetAllElements()) {
+    if (element->PrepareToDraw())
       needs_redraw = true;
   }
   return needs_redraw;
@@ -151,43 +175,40 @@
 }
 
 UiElement* UiScene::GetUiElementById(int element_id) const {
-  for (auto& element : *root_element_) {
-    if (element.id() == element_id)
-      return &element;
-  }
-  return nullptr;
+  return FindElement(
+      root_element_.get(),
+      base::BindRepeating([](int id, UiElement* e) { return e->id() == id; },
+                          element_id));
 }
 
 UiElement* UiScene::GetUiElementByName(UiElementName name) const {
-  for (auto& element : *root_element_) {
-    if (element.name() == name)
-      return &element;
-  }
-  return nullptr;
+  return FindElement(
+      root_element_.get(),
+      base::BindRepeating(
+          [](UiElementName name, UiElement* e) { return e->name() == name; },
+          name));
 }
 
-UiScene::Elements UiScene::GetVisibleElementsToDraw() const {
-  return GetVisibleElements(GetUiElementByName(kRoot), [](UiElement* element) {
+std::vector<UiElement*>& UiScene::GetAllElements() {
+  if (root_element_->descendants_updated()) {
+    all_elements_.clear();
+    GetAllElementsRecursive(&all_elements_, root_element_.get());
+  }
+  return all_elements_;
+}
+
+UiScene::Elements UiScene::GetVisibleElementsToDraw() {
+  return GetVisibleElements(GetAllElements(), [](UiElement* element) {
     return element->draw_phase() == kPhaseForeground ||
            element->draw_phase() == kPhaseBackplanes ||
            element->draw_phase() == kPhaseBackground;
   });
 }
 
-UiScene::Elements UiScene::GetVisibleWebVrOverlayElementsToDraw() const {
-  return GetVisibleElements(
-      GetUiElementByName(kWebVrRoot), [](UiElement* element) {
-        return element->draw_phase() == kPhaseOverlayForeground;
-      });
-}
-
-UiScene::Elements UiScene::GetPotentiallyVisibleElements() const {
-  UiScene::Elements elements;
-  for (auto& element : *root_element_) {
-    if (element.draw_phase() != kPhaseNone)
-      elements.push_back(&element);
-  }
-  return elements;
+UiScene::Elements UiScene::GetVisibleWebVrOverlayElementsToDraw() {
+  return GetVisibleElements(GetAllElements(), [](UiElement* element) {
+    return element->draw_phase() == kPhaseOverlayForeground;
+  });
 }
 
 UiScene::UiScene() {
@@ -200,19 +221,15 @@
 void UiScene::OnGlInitialized(SkiaSurfaceProvider* provider) {
   gl_initialized_ = true;
   provider_ = provider;
-  for (auto& element : *root_element_)
-    element.Initialize(provider_);
+  InitializeElementRecursive(root_element_.get(), provider_);
 }
 
 void UiScene::InitializeElement(UiElement* element) {
   CHECK_GE(element->id(), 0);
   CHECK_EQ(GetUiElementById(element->id()), nullptr);
   CHECK_GE(element->draw_phase(), 0);
-  if (gl_initialized_) {
-    for (auto& child : *element) {
-      child.Initialize(provider_);
-    }
-  }
+  if (gl_initialized_)
+    InitializeElementRecursive(element, provider_);
 }
 
 }  // namespace vr
diff --git a/chrome/browser/vr/ui_scene.h b/chrome/browser/vr/ui_scene.h
index 4282d71..4526c05 100644
--- a/chrome/browser/vr/ui_scene.h
+++ b/chrome/browser/vr/ui_scene.h
@@ -11,7 +11,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/vr/elements/ui_element.h"
-#include "chrome/browser/vr/elements/ui_element_iterator.h"
 #include "chrome/browser/vr/elements/ui_element_name.h"
 #include "chrome/browser/vr/keyboard_delegate.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -56,9 +55,9 @@
 
   typedef std::vector<const UiElement*> Elements;
 
-  Elements GetVisibleElementsToDraw() const;
-  Elements GetVisibleWebVrOverlayElementsToDraw() const;
-  Elements GetPotentiallyVisibleElements() const;
+  std::vector<UiElement*>& GetAllElements();
+  Elements GetVisibleElementsToDraw();
+  Elements GetVisibleWebVrOverlayElementsToDraw();
 
   float background_distance() const { return background_distance_; }
   void set_background_distance(float d) { background_distance_ = d; }
@@ -84,6 +83,8 @@
   // easily compute dirtiness.
   bool is_dirty_ = false;
 
+  std::vector<UiElement*> all_elements_;
+
   SkiaSurfaceProvider* provider_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(UiScene);
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index cc46ef7..4f0796d3 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -2958,14 +2958,6 @@
   layout->SetTranslate(0, kIndicatorVerticalOffset, kIndicatorDistanceOffset);
   layout->set_margin(kWebVrPermissionMargin);
 
-  auto fullscreen_toast = CreateTextToast(
-      kExclusiveScreenToastTransientParent, kExclusiveScreenToast, model_,
-      l10n_util::GetStringUTF16(IDS_PRESS_APP_TO_EXIT_FULLSCREEN));
-  fullscreen_toast->AddBinding(
-      VR_BIND(bool, Model, model_, model->fullscreen_enabled(), UiElement,
-              fullscreen_toast.get(), SetVisibleInLayout(view, value)));
-  layout->AddChild(std::move(fullscreen_toast));
-
   auto platform_toast = CreateTextToast(
       kPlatformToastTransientParent, kPlatformToast, model_, base::string16());
   platform_toast->AddBinding(std::make_unique<Binding<const PlatformToast*>>(
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc
index e9a045b6..9f6123a 100644
--- a/chrome/browser/vr/ui_unittest.cc
+++ b/chrome/browser/vr/ui_unittest.cc
@@ -116,57 +116,40 @@
   EXPECT_EQ(button->background()->center_color(), background_color);
 }
 
+void VerifyNoHitTestableElementInSubtree(UiElement* element) {
+  EXPECT_FALSE(element->IsHitTestable());
+  for (auto& child : element->children())
+    VerifyNoHitTestableElementInSubtree(child.get());
+}
+
 }  // namespace
 
-TEST_F(UiTest, ToastStateTransitions) {
+TEST_F(UiTest, WebVrToastStateTransitions) {
   // Tests toast not showing when directly entering VR though WebVR
   // presentation.
   CreateScene(kNotInCct, kInWebVr);
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
   EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
 
   CreateScene(kNotInCct, kNotInWebVr);
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
-  EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
-
-  ui_->SetFullscreen(true);
-  EXPECT_TRUE(IsVisible(kExclusiveScreenToast));
   EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
 
   ui_->SetWebVrMode(true);
   ui_->OnWebVrFrameAvailable();
   ui_->SetCapturingState(CapturingStateModel());
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
   EXPECT_TRUE(IsVisible(kWebVrExclusiveScreenToast));
 
   ui_->SetWebVrMode(false);
-  // TODO(crbug.com/787582): we should not show the fullscreen toast again when
-  // returning to fullscreen mode after presenting webvr.
-  EXPECT_TRUE(IsVisible(kExclusiveScreenToast));
-  EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
-
-  ui_->SetFullscreen(false);
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
   EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
 
   ui_->SetWebVrMode(true);
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
   EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
 
   ui_->SetWebVrMode(false);
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
   EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
 }
 
-TEST_F(UiTest, ToastTransience) {
+TEST_F(UiTest, WebVrToastTransience) {
   CreateScene(kNotInCct, kNotInWebVr);
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
-
-  ui_->SetFullscreen(true);
-  EXPECT_TRUE(IsVisible(kExclusiveScreenToast));
-  EXPECT_TRUE(RunFor(base::TimeDelta::FromSecondsD(kToastTimeoutSeconds +
-                                                   kSmallDelaySeconds)));
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
 
   ui_->SetWebVrMode(true);
   ui_->OnWebVrFrameAvailable();
@@ -211,7 +194,6 @@
 
 TEST_F(UiTest, CaptureToasts) {
   CreateScene(kNotInCct, kNotInWebVr);
-  EXPECT_FALSE(IsVisible(kExclusiveScreenToast));
 
   for (auto& spec : GetIndicatorSpecs()) {
     for (int i = 0; i < 3; ++i) {
@@ -529,7 +511,6 @@
   visible_in_fullscreen.insert(kContentQuadShadow);
   visible_in_fullscreen.insert(kBackplane);
   visible_in_fullscreen.insert(kCloseButton);
-  visible_in_fullscreen.insert(kExclusiveScreenToast);
   visible_in_fullscreen.insert(kController);
   visible_in_fullscreen.insert(kControllerTouchpadButton);
   visible_in_fullscreen.insert(kControllerAppButton);
@@ -1305,8 +1286,7 @@
 TEST_F(UiTest, ControllerHitTest) {
   CreateScene(kNotInCct, kNotInWebVr);
   auto* controller = scene_->GetUiElementByName(kControllerRoot);
-  for (auto& child : *controller)
-    EXPECT_FALSE(child.IsHitTestable());
+  VerifyNoHitTestableElementInSubtree(controller);
 }
 
 TEST_F(UiTest, BrowsingRootBounds) {
diff --git a/chrome/browser/vr/ui_unsupported_mode.h b/chrome/browser/vr/ui_unsupported_mode.h
index e850e0f..3d7c3a7 100644
--- a/chrome/browser/vr/ui_unsupported_mode.h
+++ b/chrome/browser/vr/ui_unsupported_mode.h
@@ -19,6 +19,7 @@
   kVoiceSearchNeedsRecordAudioOsPermission = 4,  // TODO(ddorwin): Android only.
   kGenericUnsupportedFeature = 5,
   kNeedsKeyboardUpdate = 6,
+  kSearchEnginePromo = 7,
   // This must be last.
   kCount,
 };
diff --git a/chrome/profiling/BUILD.gn b/chrome/profiling/BUILD.gn
deleted file mode 100644
index 5c1d7c45..0000000
--- a/chrome/profiling/BUILD.gn
+++ /dev/null
@@ -1,76 +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("//chrome/common/features.gni")
-import("//services/service_manager/public/cpp/service.gni")
-import("//services/service_manager/public/service_manifest.gni")
-
-static_library("profiling") {
-  sources = [
-    "address.h",
-    "allocation_event.cc",
-    "allocation_event.h",
-    "allocation_tracker.cc",
-    "allocation_tracker.h",
-    "backtrace.cc",
-    "backtrace.h",
-    "backtrace_storage.cc",
-    "backtrace_storage.h",
-    "json_exporter.cc",
-    "json_exporter.h",
-    "memlog_connection_manager.cc",
-    "memlog_connection_manager.h",
-    "memlog_receiver.h",
-    "memlog_receiver_pipe.cc",
-    "memlog_receiver_pipe.h",
-    "memlog_receiver_pipe_posix.cc",
-    "memlog_receiver_pipe_posix.h",
-    "memlog_receiver_pipe_win.cc",
-    "memlog_receiver_pipe_win.h",
-    "memlog_stream_parser.cc",
-    "memlog_stream_parser.h",
-    "memlog_stream_receiver.h",
-    "profiling_service.cc",
-    "profiling_service.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chrome/common",
-    "//content/public/child",
-    "//mojo/edk",
-    "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
-    "//third_party/zlib",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "backtrace_storage_unittest.cc",
-    "json_exporter_unittest.cc",
-    "memlog_stream_parser_unittest.cc",
-  ]
-  deps = [
-    ":profiling",
-    "//base",
-    "//testing/gtest",
-  ]
-}
-
-service_manifest("manifest") {
-  name = "profiling"
-  source = "profiling_manifest.json"
-}
-
-fuzzer_test("profiling_fuzzer") {
-  sources = [
-    "memlog_stream_fuzzer.cc",
-  ]
-  deps = [
-    ":profiling",
-  ]
-  libfuzzer_options = [ "max_len = 64000" ]
-  dict = "memlog_stream_fuzzer.dict"
-}
diff --git a/chrome/profiling/OWNERS b/chrome/profiling/OWNERS
deleted file mode 100644
index b7a89e5..0000000
--- a/chrome/profiling/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-ajwong@chromium.org
-erikchen@chromium.org
-
-per-file profiling_manifest.json=set noparent
-per-file profiling_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chrome/profiling/README.md b/chrome/profiling/README.md
deleted file mode 100644
index 7ce8ba4..0000000
--- a/chrome/profiling/README.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# chrome/profiling
-
-This document describes the architecture for the profiling process, which
-tracks memory allocations in other processes. See [design doc] for more details.
-
-[design doc]: https://docs.google.com/document/d/1eRAgOFgHwYEPge8G1_5UEvu8TJs5VkYCxd6aFU8AIKY
-
-There is some additional information in //chrome/common/profiling/README.md
-
-How To Enable Out of Process Heap Profiling
--------------------------------------------
-Navigate to `chrome://flags/#memlog` and set the flag to
-"Profile only the browser process." It's possible to profile all processes, but
-that has a higher performance impact, and heap dumps of renderer processes are
-less actionable.
-
-How To Use Out of Process Heap Profiling
--------------------------------------------
-By default, you don't need to do anything. The heap profiler will detect when
-the browser's memory usage has exceeded a certain threshold and upload a trace.
-
-To force an upload, or to create a heap dump manually, see
-`chrome://memory-internals`. The resulting heap dump is intended to be used with
-[symbolize_trace][1] and [diff_heap_profiler.py][2].
-
-It's also possible to view the heap dump within a [memory-infra][3] trace,
-although the results will still need to be symbolized with [symbolize_trace][1].
-
-Due to size constraints, most allocations are pruned from the heap dump. Only
-allocations of sufficient size and/or frequency are kept. After pruning, the
-result is ~100x smaller, but still accounts for about half of all allocations.
-More importantly, it still accounts for all obvious memory leaks.
-
-[1]: https://cs.chromium.org/chromium/src/third_party/catapult/tracing/bin/symbolize_trace
-[2]: https://cs.chromium.org/chromium/src/third_party/catapult/experimental/tracing/bin/diff_heap_profiler.py
-[3]: /docs/memory-infra/README.md
-
-Communication Model
--------------------
-When profiling is enabled, the browser process will spawn the profiling service.
-The services lives in a sandboxed, utility process, and its interface is at
-`chrome/common/profiling/memlog_service.mojom`.
-
-All other processes, including the browser process, are ProfilingClients. See
-`profiling_client.mojom`. Depending on the profiling mode, the browser process
-will start profiling for just itself and the GPU process [`--memlog=minimal`],
-or itself and all child processes [`--memlog=all`].
-
-The browser process creates a pipe for each ProfilingClient that allows the
-client processes to communicate memory events to the profiling process.
-
-Code Locations
---------------
-`//chrome/common/profiling` - Logic for ProfilingClient.
-`//chrome/browser/profiling_host` - Logic in browser process for starting
-profiling service, and connecting ProfilingClients to the profiling service.
-`//chrome/profiling` - Profiling service.
-
diff --git a/chrome/profiling/memlog_receiver_pipe.cc b/chrome/profiling/memlog_receiver_pipe.cc
deleted file mode 100644
index 12c3406..0000000
--- a/chrome/profiling/memlog_receiver_pipe.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/profiling/memlog_receiver_pipe.h"
-
-#include "base/bind.h"
-#include "base/task_runner.h"
-#include "chrome/profiling/memlog_stream_receiver.h"
-
-namespace profiling {
-
-MemlogReceiverPipeBase::MemlogReceiverPipeBase(
-    mojo::edk::ScopedPlatformHandle handle)
-    : handle_(std::move(handle)) {}
-
-MemlogReceiverPipeBase::~MemlogReceiverPipeBase() = default;
-
-void MemlogReceiverPipeBase::SetReceiver(
-    scoped_refptr<base::TaskRunner> task_runner,
-    scoped_refptr<MemlogStreamReceiver> receiver) {
-  receiver_task_runner_ = std::move(task_runner);
-  receiver_ = receiver;
-}
-
-void MemlogReceiverPipeBase::ReportError() {
-  handle_.reset();
-}
-
-void MemlogReceiverPipeBase::OnStreamDataThunk(
-    scoped_refptr<base::TaskRunner> pipe_task_runner,
-    std::unique_ptr<char[]> data,
-    size_t size) {
-  if (!receiver_->OnStreamData(std::move(data), size)) {
-    pipe_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(&MemlogReceiverPipeBase::ReportError, this));
-  }
-}
-
-}  // namespace profiling
diff --git a/chrome/profiling/memlog_receiver_pipe_posix.h b/chrome/profiling/memlog_receiver_pipe_posix.h
deleted file mode 100644
index ede0790..0000000
--- a/chrome/profiling/memlog_receiver_pipe_posix.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_POSIX_H_
-#define CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_POSIX_H_
-
-#include <string>
-
-#include "base/files/platform_file.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "build/build_config.h"
-#include "chrome/profiling/memlog_receiver_pipe.h"
-
-namespace profiling {
-
-class MemlogReceiverPipe : public MemlogReceiverPipeBase,
-                           public base::MessageLoopForIO::Watcher {
- public:
-  explicit MemlogReceiverPipe(mojo::edk::ScopedPlatformHandle handle);
-
-  // Must be called on the IO thread.
-  void StartReadingOnIOThread();
-
- private:
-  ~MemlogReceiverPipe() override;
-
-  // MessageLoopForIO::Watcher implementation.
-  void OnFileCanReadWithoutBlocking(int fd) override;
-  void OnFileCanWriteWithoutBlocking(int fd) override;
-
-  base::MessageLoopForIO::FileDescriptorWatcher controller_;
-  std::unique_ptr<char[]> read_buffer_;
-
-  DISALLOW_COPY_AND_ASSIGN(MemlogReceiverPipe);
-};
-
-}  // namespace profiling
-
-#endif  // CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_POSIX_H_
diff --git a/chrome/profiling/profiling_browsertest.cc b/chrome/profiling/profiling_browsertest.cc
deleted file mode 100644
index ea42f04..0000000
--- a/chrome/profiling/profiling_browsertest.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/command_line.h"
-#include "base/process/launch.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-
-namespace profiling {
-
-#if !defined(OS_MACOSX)
-class ProfilingBrowserTest : public InProcessBrowserTest {
- protected:
-  void RelaunchWithMemlog() {
-    // TODO(ajwong): Remove this once Brett lands is process model change so the
-    // browser process actually launches the profiling process. Until then, it's
-    // okay to have this skip on Mac (which has a different launch setup such
-    // that this sort of relaunch doesn't work). See GetCommandLineForRelaunch()
-    // function for details.
-    // TODO(awong): Can we do this with just SetUpCommandLine() and no Relaunch?
-    base::CommandLine new_command_line(GetCommandLineForRelaunch());
-    new_command_line.AppendSwitchASCII(switches::kMemlog,
-                                       switches::kMemlogModeAll);
-
-    ui_test_utils::BrowserAddedObserver observer;
-    base::LaunchProcess(new_command_line, base::LaunchOptionsForTest());
-
-    observer.WaitForSingleNewBrowser();
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(ProfilingBrowserTest, InterceptsNew) {
-  RelaunchWithMemlog();
-}
-#endif
-
-}  // namespace profiling
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1e21fe4..a8772a3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -880,7 +880,6 @@
       "../common/mac/mock_launchd.cc",
       "../common/mac/mock_launchd.h",
       "../common/time_format_browsertest.cc",
-      "../profiling/profiling_browsertest.cc",
       "../renderer/autofill/autofill_renderer_browsertest.cc",
       "../renderer/autofill/fake_content_password_manager_driver.cc",
       "../renderer/autofill/fake_content_password_manager_driver.h",
@@ -1662,6 +1661,7 @@
         "../browser/extensions/api/screenlock_private/screenlock_private_apitest.cc",
         "../browser/extensions/api/vpn_provider/vpn_provider_apitest.cc",
         "../browser/mash_service_registry_browsertest.cc",
+        "../browser/notifications/chrome_ash_message_center_client_browsertest.cc",
         "../browser/resources/chromeos/zip_archiver/test/zip_archiver_jstest.cc",
         "../browser/signin/chromeos_mirror_account_consistency_browsertest.cc",
         "../browser/ui/app_list/app_list_browsertest.cc",
@@ -2136,13 +2136,8 @@
     "//tools/perf/:perf_experimental",
   ]
 
-  data = [
-    # Needed for isolate script to execute.
-    "//testing/scripts/common.py",
-    "//testing/xvfb.py",
-    "//testing/scripts/run_gtest_perf_test.py",
-    "//testing/scripts/run_performance_tests.py",
-    "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
+  data_deps = [
+    "//testing:run_perf_test",
   ]
 }
 
@@ -2154,10 +2149,8 @@
     "//chrome/test:telemetry_perf_tests",
   ]
 
-  data = [
-    "//testing/scripts/run_performance_tests.py",
-    "//testing/scripts/run_gtest_perf_test.py",
-    "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
+  data_deps = [
+    "//testing:run_perf_test",
   ]
 }
 
@@ -2173,19 +2166,12 @@
 
 group("angle_perftests") {
   testonly = true
-  if (is_win || is_linux || is_android) {
-    data_deps = [
-      "//third_party/angle/src/tests:angle_perftests",
-    ]
-  }
-
-  data = [
-    # Needed for isolate script to execute.
-    "//testing/scripts/common.py",
-    "//testing/xvfb.py",
-    "//testing/scripts/run_gtest_perf_test.py",
-    "//tools/perf/generate_legacy_perf_dashboard_json.py",
+  data_deps = [
+    "//testing:run_perf_test",
   ]
+  if (is_win || is_linux || is_android) {
+    data_deps += [ "//third_party/angle/src/tests:angle_perftests" ]
+  }
 }
 
 if (is_mac) {
@@ -2727,7 +2713,6 @@
     "//chrome:strings",
     "//chrome/browser/media/router:test_support",
     "//chrome/common:test_support",
-    "//chrome/profiling:unit_tests",
     "//components/autofill/content/renderer:test_support",
     "//components/browser_sync:test_support",
     "//components/component_updater:test_support",
@@ -3999,8 +3984,8 @@
         "../browser/ui/cocoa/info_bubble_view_unittest.mm",
         "../browser/ui/cocoa/info_bubble_window_unittest.mm",
         "../browser/ui/cocoa/infobars/confirm_infobar_controller_unittest.mm",
+        "../browser/ui/cocoa/infobars/infobar_background_view_unittest.mm",
         "../browser/ui/cocoa/infobars/infobar_container_controller_unittest.mm",
-        "../browser/ui/cocoa/infobars/infobar_gradient_view_unittest.mm",
         "../browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.cc",
         "../browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.h",
         "../browser/ui/cocoa/infobars/translate_infobar_unittest.mm",
@@ -5070,12 +5055,10 @@
 
     data = [
       "//chrome/test/data/extensions/api_test/",
+    ]
 
-      # Needed for isolate script to execute.
-      "//testing/scripts/common.py",
-      "//testing/xvfb.py",
-      "//testing/scripts/run_gtest_perf_test.py",
-      "//tools/perf/generate_legacy_perf_dashboard_json.py",
+    data_deps = [
+      "//testing:run_perf_test",
     ]
 
     if (is_win) {
@@ -5454,47 +5437,6 @@
   }
 
   # Executable to measure time to load libraries.
-  test("load_library_perf_tests_v2") {
-    sources = [
-      "../browser/load_library_perf_test.cc",
-    ]
-
-    # This test deliberately does not depend in chrome's test support targets.
-    # This is a small test and Chrome's test support targets bring in the
-    # world, causing link time to explode. Please don't add more dependencies
-    # here without understanding how it affects link time (and factor them
-    # differently if possible).
-    deps = [
-      "//base/test:test_support_perf",
-      "//media:media_buildflags",
-      "//testing/gtest",
-      "//testing/perf",
-      "//third_party/widevine/cdm:headers",
-    ]
-
-    if (enable_library_cdms) {
-      deps += [ "//media/cdm:cdm_paths" ]
-      data_deps = [
-        "//media/cdm/library_cdm/clear_key_cdm",
-        "//third_party/widevine/cdm",
-      ]
-    }
-
-    data = [
-      # Needed for isolate script to execute.
-      "//testing/scripts/common.py",
-      "//testing/xvfb.py",
-      "//testing/scripts/run_gtest_perf_test.py",
-      "//testing/scripts/run_performance_tests.py",
-      "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
-      "//tools/perf/generate_legacy_perf_dashboard_json.py",
-    ]
-
-    # This target should not require the Chrome executable to run.
-    assert_no_deps = [ "//chrome" ]
-  }
-
-  # Executable to measure time to load libraries.
   test("load_library_perf_tests") {
     sources = [
       "../browser/load_library_perf_test.cc",
@@ -5513,24 +5455,18 @@
       "//third_party/widevine/cdm:headers",
     ]
 
+    data_deps = [
+      "//testing:run_perf_test",
+    ]
+
     if (enable_library_cdms) {
       deps += [ "//media/cdm:cdm_paths" ]
-      data_deps = [
+      data_deps += [
         "//media/cdm/library_cdm/clear_key_cdm",
         "//third_party/widevine/cdm",
       ]
     }
 
-    data = [
-      # Needed for isolate script to execute.
-      "//testing/scripts/common.py",
-      "//testing/xvfb.py",
-      "//testing/scripts/run_gtest_perf_test.py",
-      "//testing/scripts/run_performance_tests.py",
-      "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
-      "//tools/perf/generate_legacy_perf_dashboard_json.py",
-    ]
-
     # This target should not require the Chrome executable to run.
     assert_no_deps = [ "//chrome" ]
   }
diff --git a/chrome/test/data/extensions/api_test/webrequest_clients_google_com/background.js b/chrome/test/data/extensions/api_test/webrequest_clients_google_com/background.js
index bafc3fd0..9419edd 100644
--- a/chrome/test/data/extensions/api_test/webrequest_clients_google_com/background.js
+++ b/chrome/test/data/extensions/api_test/webrequest_clients_google_com/background.js
@@ -6,6 +6,6 @@
 
 chrome.webRequest.onBeforeRequest.addListener(function(details) {
   ++window.webRequestCount;
-}, {urls: ['http://clients1.google.com/']});
+}, {urls: ['https://clients1.google.com/']});
 
 chrome.test.sendMessage('ready');
diff --git a/chrome/test/media_router/media_router_e2e_browsertest.cc b/chrome/test/media_router/media_router_e2e_browsertest.cc
index fc3ba441..b40c275 100644
--- a/chrome/test/media_router/media_router_e2e_browsertest.cc
+++ b/chrome/test/media_router/media_router_e2e_browsertest.cc
@@ -142,10 +142,10 @@
       browser(), GURL("about:blank"), 1);
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  SessionID tab_id = SessionTabHelper::IdForTab(web_contents);
 
   // Wait for 30 seconds to make sure the route is stable.
-  CreateMediaRoute(MediaSourceForTab(tab_id),
+  CreateMediaRoute(MediaSourceForTab(tab_id.id()),
                    url::Origin::Create(GURL(kOrigin)), web_contents);
   Wait(base::TimeDelta::FromSeconds(30));
 
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index a778b94..64561b3 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -29,8 +29,8 @@
     "//chrome:strings",
     "//chrome/common",
     "//chrome/common:mojo_bindings",
-    "//chrome/profiling",
     "//components/search_engines",
+    "//components/services/heap_profiling",
     "//components/services/heap_profiling/public/cpp",
     "//components/services/patch:lib",
     "//components/services/unzip:lib",
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index 7305d09a..1d9b056 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+chrome/grit",
   "+chrome/installer/util",
-  "+chrome/profiling",
   "+chrome/services/file_util/file_util_service.h",
   "+chrome/services/file_util/public/mojom",
   "+chrome/services/media_gallery_util/media_gallery_util_service.h",
@@ -18,6 +17,7 @@
   "+chrome/services/wifi_util_win/wifi_util_win_service.h",
   "+chrome/services/wifi_util_win/public/mojom",
   "+components/crash/core/common/crash_keys.h",
+  "+components/services/heap_profiling/heap_profiling_service.h",
   "+components/services/heap_profiling/public",
   "+components/services/font/font_service_app.h",
   "+components/services/patch",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 6d30f87..7fef4b4 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -15,7 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "chrome/common/buildflags.h"
-#include "chrome/profiling/profiling_service.h"
+#include "components/services/heap_profiling/heap_profiling_service.h"
 #include "components/services/heap_profiling/public/mojom/constants.mojom.h"
 #include "components/services/patch/patch_service.h"
 #include "components/services/patch/public/interfaces/constants.mojom.h"
@@ -193,7 +193,7 @@
   service_manager::EmbeddedServiceInfo profiling_info;
   profiling_info.task_runner = content::ChildThread::Get()->GetIOTaskRunner();
   profiling_info.factory =
-      base::Bind(&profiling::ProfilingService::CreateService);
+      base::Bind(&profiling::HeapProfilingService::CreateService);
   services->emplace(profiling::mojom::kServiceName, profiling_info);
 
 #if !defined(OS_ANDROID)
diff --git a/chromecast/media/cma/backend/android/volume_control_android.cc b/chromecast/media/cma/backend/android/volume_control_android.cc
index d850157..ed033016 100644
--- a/chromecast/media/cma/backend/android/volume_control_android.cc
+++ b/chromecast/media/cma/backend/android/volume_control_android.cc
@@ -23,7 +23,9 @@
 #include "chromecast/base/serializers.h"
 #include "chromecast/chromecast_buildflags.h"
 #include "jni/VolumeControl_jni.h"
+#if BUILDFLAG(ENABLE_VOLUME_TABLES_ACCESS)
 #include "jni/VolumeMap_jni.h"
+#endif
 
 namespace chromecast {
 namespace media {
@@ -166,8 +168,10 @@
 
   for (auto type : {AudioContentType::kMedia, AudioContentType::kAlarm,
                     AudioContentType::kCommunication}) {
+#if BUILDFLAG(ENABLE_VOLUME_TABLES_ACCESS)
     Java_VolumeMap_dumpVolumeTables(base::android::AttachCurrentThread(),
                                     static_cast<int>(type));
+#endif
     volumes_[type] =
         Java_VolumeControl_getVolume(base::android::AttachCurrentThread(),
                                      j_volume_control_, static_cast<int>(type));
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index e46e802..a155b6e 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -21,6 +21,7 @@
 
   deps = [
     "//base",
+    "//build/util:webkit_version",
     "//chromeos",
     "//chromeos/assistant:buildflags",
     "//chromeos/services/assistant/public/mojom",
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index a088f36..20a4d20 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -11,6 +11,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/sys_info.h"
 #include "base/task_scheduler/post_task.h"
+#include "build/util/webkit_version.h"
 #include "chromeos/assistant/internal/internal_constants.h"
 #include "chromeos/assistant/internal/internal_util.h"
 #include "chromeos/services/assistant/service.h"
@@ -154,9 +155,13 @@
       &os_major_version, &os_minor_version, &os_bugfix_version);
 
   std::string user_agent;
-  base::StringAppendF(&user_agent, "Mozilla/5.0 (X11; CrOS %s %d.%d.%d)",
+  base::StringAppendF(&user_agent,
+                      "Mozilla/5.0 (X11; CrOS %s %d.%d.%d; %s) "
+                      "AppleWebKit/%d.%d (KHTML, like Gecko)",
                       base::SysInfo::OperatingSystemArchitecture().c_str(),
-                      os_major_version, os_minor_version, os_bugfix_version);
+                      os_major_version, os_minor_version, os_bugfix_version,
+                      base::SysInfo::GetLsbReleaseBoard().c_str(),
+                      WEBKIT_VERSION_MAJOR, WEBKIT_VERSION_MINOR);
 
   if (!arc_version.empty()) {
     base::StringAppendF(&user_agent, " ARC/%s", arc_version.c_str());
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 94a848b5..bd18adc5 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -224,6 +224,7 @@
       "//components/safe_browsing/password_protection:password_protection_unittest",
       "//components/safe_browsing/triggers:unit_tests",
       "//components/security_state/content:unit_tests",
+      "//components/services/heap_profiling:unit_tests",
       "//components/spellcheck/browser:unit_tests",
       "//components/spellcheck/renderer:unit_tests",
       "//components/subresource_filter/content/browser:unit_tests",
diff --git a/components/arc/common/bluetooth.mojom b/components/arc/common/bluetooth.mojom
index f22585f..0371ccf7 100644
--- a/components/arc/common/bluetooth.mojom
+++ b/components/arc/common/bluetooth.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 8
+// Next MinVersion: 9
 
 module arc.mojom;
 
@@ -292,7 +292,7 @@
   uint32 service_handle;
 };
 
-// Next Method ID: 43
+// Next Method ID: 44
 interface BluetoothHost {
   EnableAdapter@0() => (BluetoothAdapterState state);
   DisableAdapter@1() => (BluetoothAdapterState state);
@@ -396,11 +396,13 @@
   // Multi-advertisement functions
   [MinVersion=6] ReserveAdvertisementHandle@40()
       => (BluetoothGattStatus status, int32 adv_handle);
-  [MinVersion=6] BroadcastAdvertisement@41(int32 adv_handle,
-                                           BluetoothAdvertisement adv)
+  [MinVersion=6] EnableAdvertisement@41(int32 adv_handle,
+                                        BluetoothAdvertisement adv)
       => (BluetoothGattStatus status);
   [MinVersion=6] ReleaseAdvertisementHandle@42(int32 adv_handle)
       => (BluetoothGattStatus status);
+  [MinVersion=8] DisableAdvertisement@43(int32 adv_handle)
+      => (BluetoothGattStatus status);
 };
 
 // Next Method ID: 19
diff --git a/components/browser_sync/BUILD.gn b/components/browser_sync/BUILD.gn
index d8c3092..716f676 100644
--- a/components/browser_sync/BUILD.gn
+++ b/components/browser_sync/BUILD.gn
@@ -43,6 +43,7 @@
     "//components/version_info:generate_version_info",
     "//google_apis",
     "//net",
+    "//services/identity/public/cpp",
   ]
 }
 
@@ -126,6 +127,7 @@
     "//components/sync_sessions:test_support",
     "//google_apis",
     "//net:test_support",
+    "//services/identity/public/cpp",
     "//testing/gmock",
   ]
 }
diff --git a/components/browser_sync/DEPS b/components/browser_sync/DEPS
index 6b9d9d2d..3bbcc3d 100644
--- a/components/browser_sync/DEPS
+++ b/components/browser_sync/DEPS
@@ -23,4 +23,5 @@
   "+components/webdata_services",
   "+google_apis",
   "+net",
+  "+services/identity/public/cpp",
 ]
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index e75266b..ba9c0ed 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -338,13 +338,13 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   oauth2_token_service_->AddObserver(this);
   if (signin_)
-    signin_->GetOriginal()->AddObserver(this);
+    signin_->GetSigninManager()->AddObserver(this);
 }
 
 void ProfileSyncService::UnregisterAuthNotifications() {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (signin_)
-    signin_->GetOriginal()->RemoveObserver(this);
+    signin_->GetSigninManager()->RemoveObserver(this);
   if (oauth2_token_service_)
     oauth2_token_service_->RemoveObserver(this);
 }
@@ -1116,7 +1116,7 @@
       // On every platform except ChromeOS, sign out the user after a dashboard
       // clear.
       if (!IsLocalSyncEnabled()) {
-        SigninManager::FromSigninManagerBase(signin_->GetOriginal())
+        SigninManager::FromSigninManagerBase(signin_->GetSigninManager())
             ->SignOut(signin_metrics::SERVER_FORCED_DISABLE,
                       signin_metrics::SignoutDelete::IGNORE_METRIC);
       }
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc
index 47bfd14..16eddb0 100644
--- a/components/browser_sync/profile_sync_service_unittest.cc
+++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -191,10 +191,14 @@
     std::string account_id =
         account_tracker()->SeedAccountInfo(kGaiaId, kEmail);
     auth_service()->UpdateCredentials(account_id, "oauth2_login_token");
+#if defined(OS_CHROMEOS)
+    signin_manager()->SignIn(account_id);
+#else
+    signin_manager()->SignIn(kGaiaId, kEmail, "password");
+#endif
   }
 
   void CreateService(ProfileSyncService::StartBehavior behavior) {
-    signin_manager()->SetAuthenticatedAccountInfo(kGaiaId, kEmail);
     component_factory_ = profile_sync_service_bundle_.component_factory();
     ProfileSyncServiceBundle::SyncClientBuilder builder(
         &profile_sync_service_bundle_);
@@ -318,9 +322,9 @@
   }
 
 #if defined(OS_CHROMEOS)
-  SigninManagerBase* signin_manager()
+  FakeSigninManagerBase* signin_manager()
 #else
-  SigninManager* signin_manager()
+  FakeSigninManager* signin_manager()
 #endif
   // Opening brace is outside of macro to avoid confusing lint.
   {
diff --git a/components/browser_sync/profile_sync_test_util.cc b/components/browser_sync/profile_sync_test_util.cc
index b4515c2d..b2b6123 100644
--- a/components/browser_sync/profile_sync_test_util.cc
+++ b/components/browser_sync/profile_sync_test_util.cc
@@ -233,6 +233,7 @@
                       &account_tracker_,
                       nullptr),
 #endif
+      identity_manager_(&signin_manager_, &auth_service_),
       url_request_context_(new net::TestURLRequestContextGetter(
           base::ThreadTaskRunnerHandle::Get())) {
   RegisterPrefsForProfileSyncService(pref_service_.registry());
@@ -250,8 +251,8 @@
 
   init_params.start_behavior = start_behavior;
   init_params.sync_client = std::move(sync_client);
-  init_params.signin_wrapper =
-      std::make_unique<SigninManagerWrapper>(signin_manager());
+  init_params.signin_wrapper = std::make_unique<SigninManagerWrapper>(
+      identity_manager(), signin_manager());
   init_params.signin_scoped_device_id_callback =
       base::BindRepeating([]() { return std::string(); });
   init_params.oauth2_token_service = auth_service();
diff --git a/components/browser_sync/profile_sync_test_util.h b/components/browser_sync/profile_sync_test_util.h
index 44eccf00..a576c343 100644
--- a/components/browser_sync/profile_sync_test_util.h
+++ b/components/browser_sync/profile_sync_test_util.h
@@ -21,6 +21,7 @@
 #include "components/sync/driver/sync_api_component_factory_mock.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/sync_sessions/mock_sync_sessions_client.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 namespace history {
 class HistoryService;
@@ -132,6 +133,8 @@
 
   FakeSigninManagerType* signin_manager() { return &signin_manager_; }
 
+  identity::IdentityManager* identity_manager() { return &identity_manager_; }
+
   AccountTrackerService* account_tracker() { return &account_tracker_; }
 
   syncer::SyncApiComponentFactoryMock* component_factory() {
@@ -160,6 +163,7 @@
   AccountTrackerService account_tracker_;
   FakeSigninManagerType signin_manager_;
   FakeProfileOAuth2TokenService auth_service_;
+  identity::IdentityManager identity_manager_;
   syncer::SyncApiComponentFactoryMock component_factory_;
   testing::NiceMock<sync_sessions::MockSyncSessionsClient>
       sync_sessions_client_;
diff --git a/components/cbor/cbor_reader.cc b/components/cbor/cbor_reader.cc
index 4f4ebb3c..c08bbd6a 100644
--- a/components/cbor/cbor_reader.cc
+++ b/components/cbor/cbor_reader.cc
@@ -225,13 +225,19 @@
 
 base::Optional<CBORValue> CBORReader::DecodeToSimpleValue(
     const DataItemHeader& header) {
+  // ReadVariadicLengthInteger provides this bound.
+  CHECK_LE(header.additional_info, 27);
   // Floating point numbers are not supported.
-  if (header.additional_info > 24 && header.additional_info < 28) {
+  if (header.additional_info > 24) {
     error_code_ = DecoderError::UNSUPPORTED_FLOATING_POINT_VALUE;
     return base::nullopt;
   }
 
+  // Since |header.additional_info| <= 24, ReadVariadicLengthInteger also
+  // provides this bound for |header.value|.
   CHECK_LE(header.value, 255u);
+  // |SimpleValue| is an enum class and so the underlying type is specified to
+  // be |int|. So this cast is safe.
   CBORValue::SimpleValue possibly_unsupported_simple_value =
       static_cast<CBORValue::SimpleValue>(static_cast<int>(header.value));
   switch (possibly_unsupported_simple_value) {
diff --git a/components/content_view/java/src/org/chromium/components/content_view/ContentView.java b/components/content_view/java/src/org/chromium/components/content_view/ContentView.java
index 7c5c713..ea638ed9 100644
--- a/components/content_view/java/src/org/chromium/components/content_view/ContentView.java
+++ b/components/content_view/java/src/org/chromium/components/content_view/ContentView.java
@@ -153,7 +153,10 @@
             TraceEvent.begin("ContentView.onFocusChanged");
             super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
             ContentViewCore cvc = getContentViewCore();
-            if (cvc != null) cvc.onFocusChanged(gainFocus, true /* hideKeyboardOnBlur */);
+            if (cvc != null) {
+                cvc.setHideKeyboardOnBlur(true);
+                cvc.onViewFocusChanged(gainFocus);
+            }
         } finally {
             TraceEvent.end("ContentView.onFocusChanged");
         }
diff --git a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
index a1251925..80bf0a4 100644
--- a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
+++ b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.net.http.HttpResponseCache;
 import android.support.annotation.VisibleForTesting;
+import android.util.Log;
 
 import java.io.IOException;
 import java.net.URL;
@@ -27,6 +28,8 @@
  * using {@link Builder}.
  */
 public abstract class CronetEngine {
+    private static final String TAG = CronetEngine.class.getSimpleName();
+
     /**
      * A builder for {@link CronetEngine}s, which allows runtime configuration of
      * {@code CronetEngine}. Configuration options are set on the builder and
@@ -320,7 +323,13 @@
         private static ICronetEngineBuilder createBuilderDelegate(Context context) {
             List<CronetProvider> providerList =
                     getEnabledCronetProviders(context, CronetProvider.getAllProviders(context));
-            return providerList.get(0).createBuilder().mBuilderDelegate;
+            CronetProvider provider = providerList.get(0);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG,
+                        String.format("Using '%s' provider for creating CronetEngine.Builder.",
+                                provider));
+            }
+            return provider.createBuilder().mBuilderDelegate;
         }
 
         /**
diff --git a/components/data_usage/android/traffic_stats_amortizer.cc b/components/data_usage/android/traffic_stats_amortizer.cc
index 3a0acfc..51a3094 100644
--- a/components/data_usage/android/traffic_stats_amortizer.cc
+++ b/components/data_usage/android/traffic_stats_amortizer.cc
@@ -165,7 +165,7 @@
 }
 
 void RecordConcurrentTabsHistogram(const DataUseBuffer& data_use_buffer) {
-  std::set<int32_t> unique_tabs;
+  std::set<SessionID> unique_tabs;
   for (const auto& data_use_buffer_pair : data_use_buffer)
     unique_tabs.insert(data_use_buffer_pair.first->tab_id);
   UMA_HISTOGRAM_COUNTS_100("TrafficStatsAmortizer.ConcurrentTabs",
diff --git a/components/data_usage/android/traffic_stats_amortizer_unittest.cc b/components/data_usage/android/traffic_stats_amortizer_unittest.cc
index 462400c8..a91d9642 100644
--- a/components/data_usage/android/traffic_stats_amortizer_unittest.cc
+++ b/components/data_usage/android/traffic_stats_amortizer_unittest.cc
@@ -73,7 +73,7 @@
 // Synthesizes a fake std::unique_ptr<DataUse> with the given |url|, |tab_id|,
 // |tx_bytes| and |rx_bytes|, using arbitrary values for all other fields.
 std::unique_ptr<DataUse> CreateDataUseWithURLAndTab(const GURL& url,
-                                                    int32_t tab_id,
+                                                    SessionID tab_id,
                                                     int64_t tx_bytes,
                                                     int64_t rx_bytes) {
   return std::unique_ptr<DataUse>(
@@ -89,13 +89,14 @@
 std::unique_ptr<DataUse> CreateDataUseWithURL(const GURL& url,
                                               int64_t tx_bytes,
                                               int64_t rx_bytes) {
-  return CreateDataUseWithURLAndTab(url, 10, tx_bytes, rx_bytes);
+  return CreateDataUseWithURLAndTab(url, SessionID::FromSerializedValue(10),
+                                    tx_bytes, rx_bytes);
 }
 
 // Synthesizes a fake std::unique_ptr<DataUse> with the given |tab_id|,
 // |tx_bytes|
 // and |rx_bytes|, using arbitrary values for all other fields.
-std::unique_ptr<DataUse> CreateDataUseWithTab(int32_t tab_id,
+std::unique_ptr<DataUse> CreateDataUseWithTab(SessionID tab_id,
                                               int64_t tx_bytes,
                                               int64_t rx_bytes) {
   return CreateDataUseWithURLAndTab(GURL("http://example.com"), tab_id,
@@ -795,6 +796,9 @@
 }
 
 TEST_F(TrafficStatsAmortizerTest, ConcurrentTabsHistogram) {
+  SessionID kTabId1 = SessionID::FromSerializedValue(1);
+  SessionID kTabId2 = SessionID::FromSerializedValue(2);
+
   SkipFirstAmortizationRun();
 
   {
@@ -802,17 +806,17 @@
     base::HistogramTester histogram_tester;
     amortizer()->SetNextTrafficStats(true, 0, 0);
     amortizer()->AmortizeDataUse(
-        CreateDataUseWithTab(1, 50, 500),
-        ExpectDataUseCallback(CreateDataUseWithTab(1, 100, 1000)));
+        CreateDataUseWithTab(kTabId1, 50, 500),
+        ExpectDataUseCallback(CreateDataUseWithTab(kTabId1, 100, 1000)));
     amortizer()->AmortizeDataUse(
-        CreateDataUseWithTab(2, 100, 1000),
-        ExpectDataUseCallback(CreateDataUseWithTab(2, 200, 2000)));
+        CreateDataUseWithTab(kTabId2, 100, 1000),
+        ExpectDataUseCallback(CreateDataUseWithTab(kTabId2, 200, 2000)));
     amortizer()->AmortizeDataUse(
-        CreateDataUseWithTab(1, 50, 500),
-        ExpectDataUseCallback(CreateDataUseWithTab(1, 100, 1000)));
+        CreateDataUseWithTab(kTabId1, 50, 500),
+        ExpectDataUseCallback(CreateDataUseWithTab(kTabId1, 100, 1000)));
     amortizer()->AmortizeDataUse(
-        CreateDataUseWithTab(2, 100, 1000),
-        ExpectDataUseCallback(CreateDataUseWithTab(2, 200, 2000)));
+        CreateDataUseWithTab(kTabId2, 100, 1000),
+        ExpectDataUseCallback(CreateDataUseWithTab(kTabId2, 200, 2000)));
     amortizer()->SetNextTrafficStats(true, 600, 6000);
     AdvanceTime(kTrafficStatsQueryDelay);
     histogram_tester.ExpectUniqueSample(kConcurrentTabs, 2, 1);
@@ -827,9 +831,10 @@
     base::HistogramTester histogram_tester;
 
     for (int32_t i = 1; i <= total_tabs; ++i) {
+      SessionID tab_id = SessionID::FromSerializedValue(i);
       amortizer()->AmortizeDataUse(
-          CreateDataUseWithTab(i, 100, 1000),
-          ExpectDataUseCallback(CreateDataUseWithTab(i, 200, 2000)));
+          CreateDataUseWithTab(tab_id, 100, 1000),
+          ExpectDataUseCallback(CreateDataUseWithTab(tab_id, 200, 2000)));
     }
     amortizer()->AddTrafficStats(total_tabs * 200, total_tabs * 2000);
     AdvanceTime(kTrafficStatsQueryDelay);
diff --git a/components/data_usage/core/BUILD.gn b/components/data_usage/core/BUILD.gn
index 42829fe7..6ce7f98 100644
--- a/components/data_usage/core/BUILD.gn
+++ b/components/data_usage/core/BUILD.gn
@@ -13,6 +13,7 @@
   ]
   deps = [
     "//base",
+    "//components/sessions",
     "//net",
     "//url",
   ]
diff --git a/components/data_usage/core/DEPS b/components/data_usage/core/DEPS
index d97699df..f9ac0a4 100644
--- a/components/data_usage/core/DEPS
+++ b/components/data_usage/core/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/sessions",
   "+net",
   "+url",
 ]
diff --git a/components/data_usage/core/data_use.cc b/components/data_usage/core/data_use.cc
index 87942617..83998843 100644
--- a/components/data_usage/core/data_use.cc
+++ b/components/data_usage/core/data_use.cc
@@ -28,7 +28,7 @@
 DataUse::DataUse(const GURL& url,
                  const base::TimeTicks& request_start,
                  const GURL& site_for_cookies,
-                 int32_t tab_id,
+                 SessionID tab_id,
                  net::NetworkChangeNotifier::ConnectionType connection_type,
                  const std::string& mcc_mnc,
                  int64_t tx_bytes,
diff --git a/components/data_usage/core/data_use.h b/components/data_usage/core/data_use.h
index 27effe3..1762745f 100644
--- a/components/data_usage/core/data_use.h
+++ b/components/data_usage/core/data_use.h
@@ -11,6 +11,7 @@
 #include <utility>
 
 #include "base/time/time.h"
+#include "components/sessions/core/session_id.h"
 #include "net/base/network_change_notifier.h"
 #include "url/gurl.h"
 
@@ -25,7 +26,7 @@
   DataUse(const GURL& url,
           const base::TimeTicks& request_start,
           const GURL& site_for_cookies,
-          int32_t tab_id,
+          SessionID tab_id,
           net::NetworkChangeNotifier::ConnectionType connection_type,
           const std::string& mcc_mnc,
           int64_t tx_bytes,
@@ -50,7 +51,7 @@
   // be invalid(-1) for the mainframe request since tab cannot be retrieved yet.
   // Could be invalid(-1) if the data use does not belong to a tab, for example
   // chrome-services traffic.
-  int32_t tab_id;
+  SessionID tab_id;
 
   // content::GlobalRequestID of the mainframe request. This is populated only
   // when tab id cannot be retrieved of a mainframe request, and used to
diff --git a/components/data_usage/core/data_use_aggregator.cc b/components/data_usage/core/data_use_aggregator.cc
index 844c903..33facf8 100644
--- a/components/data_usage/core/data_use_aggregator.cc
+++ b/components/data_usage/core/data_use_aggregator.cc
@@ -55,10 +55,10 @@
   net::LoadTimingInfo load_timing_info;
   request->GetLoadTimingInfo(&load_timing_info);
 
-  std::unique_ptr<DataUse> data_use(
-      new DataUse(request->url(), load_timing_info.request_start,
-                  request->site_for_cookies(), -1 /* tab_id */,
-                  connection_type_, mcc_mnc_, tx_bytes, rx_bytes));
+  std::unique_ptr<DataUse> data_use(new DataUse(
+      request->url(), load_timing_info.request_start,
+      request->site_for_cookies(), /*tab_id=*/SessionID::InvalidValue(),
+      connection_type_, mcc_mnc_, tx_bytes, rx_bytes));
 
   if (!annotator_) {
     PassDataUseToAmortizer(std::move(data_use));
diff --git a/components/data_usage/core/data_use_aggregator_unittest.cc b/components/data_usage/core/data_use_aggregator_unittest.cc
index bb3bdc4..7a8bad9 100644
--- a/components/data_usage/core/data_use_aggregator_unittest.cc
+++ b/components/data_usage/core/data_use_aggregator_unittest.cc
@@ -90,7 +90,7 @@
 // predetermined fake tab ID.
 class FakeDataUseAnnotator : public DataUseAnnotator {
  public:
-  FakeDataUseAnnotator() : tab_id_(-1) {}
+  FakeDataUseAnnotator() : tab_id_(SessionID::InvalidValue()) {}
   ~FakeDataUseAnnotator() override {}
 
   void Annotate(
@@ -101,10 +101,10 @@
     callback.Run(std::move(data_use));
   }
 
-  void set_tab_id(int32_t tab_id) { tab_id_ = tab_id; }
+  void set_tab_id(SessionID tab_id) { tab_id_ = tab_id; }
 
  private:
-  int32_t tab_id_;
+  SessionID tab_id_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeDataUseAnnotator);
 };
@@ -135,15 +135,15 @@
   // The simulated context for the data usage of a net::URLRequest.
   struct DataUseContext {
     DataUseContext()
-        : tab_id(-1),
+        : tab_id(SessionID::InvalidValue()),
           connection_type(net::NetworkChangeNotifier::CONNECTION_UNKNOWN) {}
 
-    DataUseContext(int32_t tab_id,
+    DataUseContext(SessionID tab_id,
                    net::NetworkChangeNotifier::ConnectionType connection_type,
                    const std::string& mcc_mnc)
         : tab_id(tab_id), connection_type(connection_type), mcc_mnc(mcc_mnc) {}
 
-    int32_t tab_id;
+    SessionID tab_id;
     net::NetworkChangeNotifier::ConnectionType connection_type;
     std::string mcc_mnc;
   };
@@ -268,7 +268,7 @@
   std::unique_ptr<net::URLRequest> ExecuteRequest(
       const GURL& url,
       const GURL& site_for_cookies,
-      int32_t tab_id,
+      SessionID tab_id,
       net::NetworkChangeNotifier::ConnectionType connection_type,
       const std::string& mcc_mnc) {
     net::MockRead reads[] = {
@@ -344,7 +344,7 @@
 
     Initialize(std::move(annotator), std::move(amortizer));
 
-    const int32_t kFooTabId = 10;
+    const SessionID kFooTabId = SessionID::FromSerializedValue(10);
     const net::NetworkChangeNotifier::ConnectionType kFooConnectionType =
         net::NetworkChangeNotifier::CONNECTION_2G;
     const std::string kFooMccMnc = "foo_mcc_mnc";
@@ -352,7 +352,7 @@
         ExecuteRequest(GURL("http://foo.com"), GURL("http://foofirstparty.com"),
                        kFooTabId, kFooConnectionType, kFooMccMnc);
 
-    const int32_t kBarTabId = 20;
+    const SessionID kBarTabId = SessionID::FromSerializedValue(20);
     const net::NetworkChangeNotifier::ConnectionType kBarConnectionType =
         net::NetworkChangeNotifier::CONNECTION_WIFI;
     const std::string kBarMccMnc = "bar_mcc_mnc";
@@ -373,7 +373,7 @@
       if (test_case.expect_tab_ids)
         EXPECT_EQ(kFooTabId, data_use_it->tab_id);
       else
-        EXPECT_EQ(-1, data_use_it->tab_id);
+        EXPECT_FALSE(data_use_it->tab_id.is_valid());
 
       EXPECT_EQ(kFooConnectionType, data_use_it->connection_type);
       EXPECT_EQ(kFooMccMnc, data_use_it->mcc_mnc);
@@ -400,7 +400,7 @@
       if (test_case.expect_tab_ids)
         EXPECT_EQ(kBarTabId, data_use_it->tab_id);
       else
-        EXPECT_EQ(-1, data_use_it->tab_id);
+        EXPECT_FALSE(data_use_it->tab_id.is_valid());
 
       EXPECT_EQ(kBarConnectionType, data_use_it->connection_type);
       EXPECT_EQ(kBarMccMnc, data_use_it->mcc_mnc);
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 8b694811..3177d6e 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -1245,20 +1245,18 @@
 void ShellSurfaceBase::UpdateWidgetBounds() {
   DCHECK(widget_);
 
-  // Return early if the shell is currently managing the bounds of the widget.
-  // 1) When a window is either maximized/fullscreen/pinned, and the bounds
-  // are not controlled by a client.
   ash::wm::WindowState* window_state =
       ash::wm::GetWindowState(widget_->GetNativeWindow());
-  if (window_state->IsMaximizedOrFullscreenOrPinned() &&
-      !window_state->allow_set_bounds_direct()) {
-    return;
+  // Return early if the shell is currently managing the bounds of the widget.
+  if (!window_state->allow_set_bounds_direct()) {
+    // 1) When a window is either maximized/fullscreen/pinned.
+    if (window_state->IsMaximizedOrFullscreenOrPinned())
+      return;
+    // 2) When a window is being dragged by |resizer_|.
+    if (IsResizing())
+      return;
   }
 
-  // 2) When a window is being dragged by |resizer_|.
-  if (IsResizing())
-    return;
-
   // Return early if there is pending configure requests.
   if (!pending_configs_.empty() || scoped_configure_)
     return;
diff --git a/components/favicon/core/favicon_handler.cc b/components/favicon/core/favicon_handler.cc
index 1e7cd5b..52a8c144 100644
--- a/components/favicon/core/favicon_handler.cc
+++ b/components/favicon/core/favicon_handler.cc
@@ -199,7 +199,7 @@
   candidates_received_ = false;
   manifest_url_ = GURL();
   non_manifest_original_candidates_.clear();
-  candidates_.clear();
+  final_candidates_.reset();
   notification_icon_url_ = GURL();
   notification_icon_type_ = favicon_base::IconType::kInvalid;
   current_candidate_index_ = 0u;
@@ -235,8 +235,8 @@
     //   and hence get sorted last during prioritization). We stop immediately
     //   to avoid downloading them all, although we don't have the certainty
     //   that no better favicon is among them.
-    return current_candidate_index_ + 1 >= candidates_.size() ||
-           candidates_[current_candidate_index_ + 1].score <=
+    return current_candidate_index_ + 1 >= final_candidates_->size() ||
+           (*final_candidates_)[current_candidate_index_ + 1].score <=
                best_favicon_.candidate.score;
   } else {
     return best_favicon_.candidate.score == 1;
@@ -327,6 +327,7 @@
   candidates_received_ = true;
   error_other_than_404_found_ = false;
   non_manifest_original_candidates_ = candidates;
+  final_candidates_.reset();
   cancelable_task_tracker_for_candidates_.TryCancelAll();
   manifest_download_request_.Cancel();
   image_download_request_.Cancel();
@@ -380,7 +381,7 @@
   }
 
   if (has_expired_or_incomplete_result) {
-    manifest_download_request_.Reset(base::Bind(
+    manifest_download_request_.Reset(base::BindOnce(
         &FaviconHandler::OnDidDownloadManifest, base::Unretained(this)));
     delegate_->DownloadManifest(manifest_url_,
                                 manifest_download_request_.callback());
@@ -411,6 +412,8 @@
 
 void FaviconHandler::OnGotFinalIconURLCandidates(
     const std::vector<FaviconURL>& candidates) {
+  DCHECK(!final_candidates_);
+
   const std::vector<int> desired_pixel_sizes =
       GetDesiredPixelSizes(handler_type_);
 
@@ -426,7 +429,7 @@
   std::stable_sort(sorted_candidates.begin(), sorted_candidates.end(),
                    &FaviconCandidate::CompareScore);
 
-  candidates_ = std::move(sorted_candidates);
+  final_candidates_ = std::move(sorted_candidates);
 
   if (got_favicon_from_history_)
     OnGotInitialHistoryDataAndIconURLCandidates();
@@ -444,10 +447,11 @@
 }
 
 void FaviconHandler::OnGotInitialHistoryDataAndIconURLCandidates() {
-  DCHECK(candidates_received_);
+  DCHECK(final_candidates_);
   DCHECK(got_favicon_from_history_);
+  DCHECK_EQ(0U, current_candidate_index_);
 
-  if (candidates_.empty()) {
+  if (final_candidates_->empty()) {
     // The page lists no candidates that match our target |icon_types_|, so
     // check if any existing mappings should be deleted.
     MaybeDeleteFaviconMappings();
@@ -519,7 +523,8 @@
     }
   }
 
-  if (request_next_icon && current_candidate_index_ + 1 < candidates_.size()) {
+  if (request_next_icon &&
+      current_candidate_index_ + 1 < final_candidates_->size()) {
     // Process the next candidate.
     ++current_candidate_index_;
     DownloadCurrentCandidateOrAskFaviconService();
@@ -539,14 +544,14 @@
                      : favicon_base::IconType::kWebManifestIcon);
     }
     // Clear download related state.
-    current_candidate_index_ = candidates_.size();
+    current_candidate_index_ = final_candidates_->size();
     best_favicon_ = DownloadedFavicon();
   }
 }
 
 const std::vector<GURL> FaviconHandler::GetIconURLs() const {
   std::vector<GURL> icon_urls;
-  for (const FaviconCandidate& candidate : candidates_)
+  for (const FaviconCandidate& candidate : *final_candidates_)
     icon_urls.push_back(candidate.icon_url);
   return icon_urls;
 }
@@ -594,11 +599,15 @@
     NotifyFaviconUpdated(favicon_bitmap_results);
   }
 
-  if (candidates_received_)
+  if (final_candidates_)
     OnGotInitialHistoryDataAndIconURLCandidates();
 }
 
 void FaviconHandler::DownloadCurrentCandidateOrAskFaviconService() {
+  DCHECK(image_download_request_.IsCancelled());
+  DCHECK(manifest_download_request_.IsCancelled());
+  DCHECK(current_candidate());
+
   const GURL icon_url = current_candidate()->icon_url;
   const favicon_base::IconType icon_type = current_candidate()->icon_type;
   // If the icons listed in a manifest are being processed, skip the cache
@@ -670,8 +679,8 @@
     return;
   }
   image_download_request_.Reset(
-      base::Bind(&FaviconHandler::OnDidDownloadFavicon, base::Unretained(this),
-                 icon_type));
+      base::BindOnce(&FaviconHandler::OnDidDownloadFavicon,
+                     base::Unretained(this), icon_type));
   // A max bitmap size is specified to avoid receiving huge bitmaps in
   // OnDidDownloadFavicon(). See FaviconDriver::StartDownload()
   // for more details about the max bitmap size.
diff --git a/components/favicon/core/favicon_handler.h b/components/favicon/core/favicon_handler.h
index 0ee94a11..6a8ff2f 100644
--- a/components/favicon/core/favicon_handler.h
+++ b/components/favicon/core/favicon_handler.h
@@ -14,6 +14,7 @@
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/favicon/core/favicon_driver_observer.h"
 #include "components/favicon/core/favicon_url.h"
@@ -275,8 +276,9 @@
 
   // Return the current candidate if any.
   const FaviconCandidate* current_candidate() const {
-    return current_candidate_index_ < candidates_.size()
-               ? &candidates_[current_candidate_index_]
+    DCHECK(final_candidates_);
+    return current_candidate_index_ < final_candidates_->size()
+               ? &final_candidates_.value()[current_candidate_index_]
                : nullptr;
   }
 
@@ -318,11 +320,11 @@
   bool redownload_icons_;
 
   // Requests to the renderer to download a manifest.
-  base::CancelableCallback<Delegate::ManifestDownloadCallback::RunType>
+  base::CancelableOnceCallback<Delegate::ManifestDownloadCallback::RunType>
       manifest_download_request_;
 
   // Requests to the renderer to download favicons.
-  base::CancelableCallback<Delegate::ImageDownloadCallback::RunType>
+  base::CancelableOnceCallback<Delegate::ImageDownloadCallback::RunType>
       image_download_request_;
 
   // The combination of the supported icon types.
@@ -348,7 +350,8 @@
   std::vector<FaviconURL> non_manifest_original_candidates_;
 
   // The prioritized favicon candidates from the page back from the renderer.
-  std::vector<FaviconCandidate> candidates_;
+  // Populated by OnGotFinalIconURLCandidates().
+  base::Optional<std::vector<FaviconCandidate>> final_candidates_;
 
   // The icon URL and the icon type of the favicon in the most recent
   // FaviconDriver::OnFaviconAvailable() notification.
diff --git a/components/favicon/core/favicon_handler_unittest.cc b/components/favicon/core/favicon_handler_unittest.cc
index 318675d..3d7363c 100644
--- a/components/favicon/core/favicon_handler_unittest.cc
+++ b/components/favicon/core/favicon_handler_unittest.cc
@@ -425,7 +425,12 @@
     base::Closure bound_callback =
         base::Bind(callback, results_[page_or_icon_url]);
 
-    if (page_or_icon_url != manual_callback_url_) {
+    // In addition to checking the URL against |manual_callback_url_|, we also
+    // defer responses if there are already pending responses (i.e. a previous
+    // lookup matched |manual_callback_url_|), because requests to the history
+    // should be executed sequentially.
+    if (page_or_icon_url != manual_callback_url_ &&
+        !HasPendingManualCallback()) {
       return tracker->PostTask(base::ThreadTaskRunnerHandle::Get().get(),
                                FROM_HERE, bound_callback);
     }
@@ -1289,6 +1294,42 @@
   base::RunLoop().RunUntilIdle();
 }
 
+// Tests that there is not crash and SetFavicons() is called with the
+// appropriate icon URL in the following scenario:
+// - The database initially has a cached but expired icon for the page.
+// - Initial favicon candidates are received fast, before the history lookup
+//   completes.
+// - Before the history lookup completes, favicon candidates are updated via
+//   javascript to include a different set of icons.
+TEST_F(FaviconHandlerTest,
+       UpdateIconsViaJavascriptAfterFastCandidatesAndExpiredIcon) {
+  EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
+  EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL64x64, _, _));
+
+  // Initial database contains a cached by expired icon for |kPageURL|.
+  favicon_service_.fake()->Store(
+      kPageURL, kIconURL16x16,
+      CreateRawBitmapResult(kIconURL16x16, kTouchIcon, /*expired=*/true));
+
+  // Initial candidates are received before the history lookup for |kPageURL| is
+  // finished.
+  favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
+  std::unique_ptr<FaviconHandler> handler =
+      RunHandlerWithSimpleFaviconCandidates(URLVector{kIconURL16x16});
+  ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
+  // Update candidates, now containing a different set of icons.
+  handler->OnUpdateCandidates(
+      kPageURL, {FaviconURL(kIconURL64x64, kFavicon, kEmptySizes)},
+      /*manifest_url=*/GURL());
+  base::RunLoop().RunUntilIdle();
+  // Complete the history lookup for |kPageURL| now.
+  ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(favicon_service_.fake()->db_requests(), ElementsAre(kPageURL));
+  EXPECT_THAT(delegate_.downloads(), ElementsAre(kIconURL64x64));
+}
+
 // Test the favicon which is selected when the web page provides several
 // favicons and none of the favicons are cached in history.
 // The goal of this test is to be more of an integration test than
@@ -1835,6 +1876,90 @@
   EXPECT_THAT(delegate_.downloads(), IsEmpty());
 }
 
+// Believed to fix crbug.com/544560.
+// Tests that there is not crash and SetFavicons() is called with the
+// appropriate icon URL in the following scenario:
+// - The database initially has a cached but expired icon for the page.
+// - Initial favicon candidates are received fast, before the history lookup
+//   completes. There is no manifest URL initially.
+// - Before the history lookup completes, favicon candidates are updated via
+//   javascript to include a manifest URL.
+// - The manifest lists at least one icon.
+TEST_F(FaviconHandlerManifestsEnabledTest,
+       AddManifestWithIconsViaJavascriptAfterFastCandidatesAndExpiredIcon) {
+  EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
+  EXPECT_CALL(favicon_service_, SetFavicons(_, kManifestURL, _, _));
+
+  // Initial database contains a cached by expired icon for |kPageURL|.
+  favicon_service_.fake()->Store(
+      kPageURL, kIconURL16x16,
+      CreateRawBitmapResult(kIconURL16x16, kTouchIcon, /*expired=*/true));
+
+  // Manifest with icons.
+  const std::vector<favicon::FaviconURL> kManifestIcons = {
+      FaviconURL(kIconURL64x64, kWebManifestIcon, kEmptySizes),
+  };
+  delegate_.fake_manifest_downloader().Add(kManifestURL, kManifestIcons);
+
+  // Initial load does NOT contain a manifest. Regular candidates are received
+  // before the history lookup for |kPageURL| is finished.
+  favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
+  std::unique_ptr<FaviconHandler> handler =
+      RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
+                                              /*manifest_url=*/GURL());
+  ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
+  // Update candidates, now containing a manifest URL.
+  handler->OnUpdateCandidates(
+      kPageURL, {FaviconURL(kIconURL16x16, kTouchIcon, kEmptySizes)},
+      kManifestURL);
+  base::RunLoop().RunUntilIdle();
+  // Complete the history lookup for |kPageURL| now.
+  ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(favicon_service_.fake()->db_requests(),
+              ElementsAre(kPageURL, kManifestURL));
+  EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL64x64));
+}
+
+// Believed to fix crbug.com/544560.
+// Same as the test above with the difference that the manifest contains no
+// icons.
+TEST_F(FaviconHandlerManifestsEnabledTest,
+       AddManifestWithoutIconsViaJavascriptAfterFastCandidatesAndExpiredIcon) {
+  EXPECT_CALL(favicon_service_, DeleteFaviconMappings(_, _)).Times(0);
+  EXPECT_CALL(favicon_service_, SetFavicons(_, kIconURL16x16, _, _));
+
+  // Initial database contains a cached by expired icon for |kPageURL|.
+  favicon_service_.fake()->Store(
+      kPageURL, kIconURL16x16,
+      CreateRawBitmapResult(kIconURL16x16, kTouchIcon, /*expired=*/true));
+
+  // Manifest without icons.
+  delegate_.fake_manifest_downloader().Add(kManifestURL,
+                                           std::vector<favicon::FaviconURL>());
+
+  // Initial load does NOT contain a manifest. Regular candidates are received
+  // before the history lookup for |kPageURL| is finished.
+  favicon_service_.fake()->SetRunCallbackManuallyForUrl(kPageURL);
+  std::unique_ptr<FaviconHandler> handler =
+      RunHandlerWithSimpleTouchIconCandidates({kIconURL16x16},
+                                              /*manifest_url=*/GURL());
+  ASSERT_TRUE(favicon_service_.fake()->HasPendingManualCallback());
+  // Update candidates, now containing a manifest URL.
+  handler->OnUpdateCandidates(
+      kPageURL, {FaviconURL(kIconURL16x16, kTouchIcon, kEmptySizes)},
+      kManifestURL);
+  base::RunLoop().RunUntilIdle();
+  // Complete the history lookup for |kPageURL| now.
+  ASSERT_TRUE(favicon_service_.fake()->RunCallbackManually());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(favicon_service_.fake()->db_requests(),
+              ElementsAre(kPageURL, kManifestURL));
+  EXPECT_THAT(delegate_.downloads(), ElementsAre(kManifestURL, kIconURL16x16));
+}
+
 // Test that a favicon corresponding to a web manifest is reported when there is
 // data in the database for neither the page URL nor the manifest URL.
 TEST_F(FaviconHandlerManifestsEnabledTest, GetFaviconFromUnknownManifest) {
diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc
index 4800d933..c10a7e9 100644
--- a/components/omnibox/browser/base_search_provider.cc
+++ b/components/omnibox/browser/base_search_provider.cc
@@ -149,9 +149,20 @@
   // mode.  They also assume the caller knows what it's doing and we set
   // this match to look as if it was received/created synchronously.
   SearchSuggestionParser::SuggestResult suggest_result(
-      suggestion, type, 0, suggestion, base::string16(), base::string16(),
-      base::string16(), base::string16(), nullptr, std::string(), std::string(),
-      from_keyword_provider, 0, false, false, base::string16());
+      suggestion, type,
+      /*subtype_identifier=*/0,
+      /*match_contents=*/suggestion,
+      /*match_contents_prefix=*/base::string16(),
+      /*annotation=*/base::string16(),
+      /*answer_contents=*/base::string16(),
+      /*answer_type=*/base::string16(),
+      /*answer=*/nullptr,
+      /*suggest_query_params=*/std::string(),
+      /*deletion_url=*/std::string(), from_keyword_provider,
+      /*relevance=*/0,
+      /*relevance_from_server=*/false,
+      /*should_prefetch=*/false,
+      /*input_text=*/base::string16());
   suggest_result.set_received_after_last_keystroke(false);
   return CreateSearchSuggestion(nullptr, AutocompleteInput(),
                                 from_keyword_provider, suggest_result,
@@ -170,7 +181,7 @@
 
   const TemplateURL* template_url =
       match.GetTemplateURL(client_->GetTemplateURLService(), false);
-  // This may be NULL if the template corresponding to the keyword has been
+  // This may be nullptr if the template corresponding to the keyword has been
   // deleted or there is no keyword set.
   if (template_url != nullptr) {
     client_->DeleteMatchingURLsForKeywordFromHistory(template_url->id(),
diff --git a/components/omnibox/browser/base_search_provider_unittest.cc b/components/omnibox/browser/base_search_provider_unittest.cc
index ec6d575..19c9ce3 100644
--- a/components/omnibox/browser/base_search_provider_unittest.cc
+++ b/components/omnibox/browser/base_search_provider_unittest.cc
@@ -108,18 +108,39 @@
       .WillRepeatedly(Return(template_url.get()));
 
   SearchSuggestionParser::SuggestResult more_relevant(
-      query, AutocompleteMatchType::SEARCH_HISTORY, 0, query, base::string16(),
-      base::string16(), base::string16(), base::string16(), nullptr,
-      std::string(), std::string(), false, 1300, true, false, query);
+      query, AutocompleteMatchType::SEARCH_HISTORY,
+      /*subtype_identifier=*/0,
+      /*match_contents=*/query,
+      /*match_contents_prefix=*/base::string16(),
+      /*annotation=*/base::string16(),
+      /*answer_contents=*/base::string16(),
+      /*answer_type=*/base::string16(),
+      /*answer=*/nullptr,
+      /*suggest_query_params=*/std::string(),
+      /*deletion_url=*/std::string(),
+      /*from_keyword_provider=*/false,
+      /*relevance=*/1300,
+      /*relevance_from_server=*/true,
+      /*should_prefetch=*/false,
+      /*input_text=*/query);
   provider_->AddMatchToMap(
       more_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
       false, false, &map);
 
   SearchSuggestionParser::SuggestResult less_relevant(
-      query, AutocompleteMatchType::SEARCH_SUGGEST, 0, query, base::string16(),
-      base::string16(), answer_contents, answer_type,
-      SuggestionAnswer::copy(answer.get()), std::string(), std::string(), false,
-      850, true, false, query);
+      query, AutocompleteMatchType::SEARCH_SUGGEST,
+      /*subtype_identifier=*/0,
+      /*match_contents=*/query,
+      /*match_contents_prefix=*/base::string16(),
+      /*annotation=*/base::string16(), answer_contents, answer_type,
+      SuggestionAnswer::copy(answer.get()),
+      /*suggest_query_params=*/std::string(),
+      /*deletion_url=*/std::string(),
+      /*from_keyword_provider=*/false,
+      /*relevance=*/850,
+      /*relevance_from_server=*/true,
+      /*should_prefetch=*/false,
+      /*input_text=*/query);
   provider_->AddMatchToMap(
       less_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
       false, false, &map);
@@ -148,10 +169,19 @@
   std::unique_ptr<SuggestionAnswer> answer2(new SuggestionAnswer());
   answer2->set_type(8242);
   more_relevant = SearchSuggestionParser::SuggestResult(
-      query, AutocompleteMatchType::SEARCH_HISTORY, 0, query, base::string16(),
-      base::string16(), answer_contents2, answer_type2,
-      SuggestionAnswer::copy(answer2.get()), std::string(), std::string(),
-      false, 1300, true, false, query);
+      query, AutocompleteMatchType::SEARCH_HISTORY,
+      /*subtype_identifier=*/0,
+      /*match_contents_prefix=*/query,
+      /*annotation=*/base::string16(),
+      /*answer_contents=*/base::string16(), answer_contents2, answer_type2,
+      SuggestionAnswer::copy(answer2.get()),
+      /*suggest_query_params=*/std::string(),
+      /*deletion_url=*/std::string(),
+      /*from_keyword_provider=*/false,
+      /*relevance=*/1300,
+      /*relevance_from_server=*/true,
+      /*should_prefetch=*/false,
+      /*input_text=*/query);
   provider_->AddMatchToMap(
       more_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
       false, false, &map);
@@ -193,10 +223,21 @@
   base::string16 query = base::ASCIIToUTF16("angeles now");
   base::string16 suggestion = base::ASCIIToUTF16("weather los ") + query;
   SearchSuggestionParser::SuggestResult suggest_result(
-      suggestion, AutocompleteMatchType::SEARCH_SUGGEST_TAIL, 0, query,
-      base::ASCIIToUTF16("..."), base::string16(), base::string16(),
-      base::string16(), nullptr, std::string(), std::string(), false, 1300,
-      true, false, query);
+      suggestion, AutocompleteMatchType::SEARCH_SUGGEST_TAIL,
+      /*subtype_identifier=*/0,
+      /*match_contents=*/query,
+      /*match_contents_prefix=*/base::ASCIIToUTF16("..."),
+      /*annotation=*/base::string16(),
+      /*answer_contents=*/base::string16(),
+      /*answer_type=*/base::string16(),
+      /*answer=*/nullptr,
+      /*suggest_query_params=*/std::string(),
+      /*deletion_url=*/std::string(),
+      /*from_keyword_provider=*/false,
+      /*relevance=*/1300,
+      /*relevance_from_server=*/true,
+      /*should_prefetch=*/false,
+      /*input_text=*/query);
 
   TestBaseSearchProvider::MatchMap map;
   provider_->AddMatchToMap(suggest_result, std::string(),
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 524df8c..d657ed0 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -645,7 +645,7 @@
       popup_open,
       dropdown_ignored ? 0 : index,
       !pasted_text.empty(),
-      -1,  // don't yet know tab ID; set later if appropriate
+      SessionID::InvalidValue(), // don't know tab ID; set later if appropriate
       ClassifyPage(),
       elapsed_time_since_user_first_modified_omnibox,
       match.allowed_to_be_default_match ? match.inline_autocompletion.length() :
@@ -664,7 +664,7 @@
     // If we know the destination is being opened in the current tab,
     // we can easily get the tab ID.  (If it's being opened in a new
     // tab, we don't know the tab ID yet.)
-    log.tab_id = client_->GetSessionID().id();
+    log.tab_id = client_->GetSessionID();
   }
   autocomplete_controller()->AddProvidersInfo(&log.providers_info);
   client_->OnURLOpenedFromOmnibox(&log);
diff --git a/components/omnibox/browser/omnibox_edit_model_unittest.cc b/components/omnibox/browser/omnibox_edit_model_unittest.cc
index 0ebd51a..82402773 100644
--- a/components/omnibox/browser/omnibox_edit_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_edit_model_unittest.cc
@@ -146,7 +146,8 @@
   };
 
   for (size_t i = 0; i < arraysize(input); ++i) {
-    toolbar_model()->set_text(base::ASCIIToUTF16(input[i].url_for_editing));
+    toolbar_model()->set_formatted_full_url(
+        base::ASCIIToUTF16(input[i].url_for_editing));
     model()->ResetDisplayUrls();
 
     model()->SetInputInProgress(input[i].is_match_selected_in_popup);
diff --git a/components/omnibox/browser/omnibox_log.cc b/components/omnibox/browser/omnibox_log.cc
index f14ae3c..00c30abd 100644
--- a/components/omnibox/browser/omnibox_log.cc
+++ b/components/omnibox/browser/omnibox_log.cc
@@ -11,7 +11,7 @@
     bool is_popup_open,
     size_t selected_index,
     bool is_paste_and_go,
-    SessionID::id_type tab_id,
+    SessionID tab_id,
     metrics::OmniboxEventProto::PageClassification current_page_classification,
     base::TimeDelta elapsed_time_since_user_first_modified_omnibox,
     size_t completed_length,
diff --git a/components/omnibox/browser/omnibox_log.h b/components/omnibox/browser/omnibox_log.h
index 7145040b..b281bc5 100644
--- a/components/omnibox/browser/omnibox_log.h
+++ b/components/omnibox/browser/omnibox_log.h
@@ -25,7 +25,7 @@
              bool is_popup_open,
              size_t selected_index,
              bool is_paste_and_go,
-             SessionID::id_type tab_id,
+             SessionID tab_id,
              metrics::OmniboxEventProto::PageClassification
                  current_page_classification,
              base::TimeDelta elapsed_time_since_user_first_modified_omnibox,
@@ -55,9 +55,9 @@
   // (The codebase refers to both these types as paste-and-go.)
   bool is_paste_and_go;
 
-  // ID of the tab the selected autocomplete suggestion was opened in.
-  // Set to -1 if we haven't yet determined the destination tab.
-  SessionID::id_type tab_id;
+  // ID of the tab the selected autocomplete suggestion was opened in. Set to
+  // SessionID::InvalidValue() if we haven't yet determined the destination tab.
+  SessionID tab_id;
 
   // The type of page (e.g., new tab page, regular web page) that the
   // user was viewing before going somewhere with the omnibox.
diff --git a/components/omnibox/browser/omnibox_metrics_provider.cc b/components/omnibox/browser/omnibox_metrics_provider.cc
index 70e50c7..367c6b8 100644
--- a/components/omnibox/browser/omnibox_metrics_provider.cc
+++ b/components/omnibox/browser/omnibox_metrics_provider.cc
@@ -122,9 +122,9 @@
 
   OmniboxEventProto* omnibox_event = omnibox_events_cache.add_omnibox_event();
   omnibox_event->set_time_sec(metrics::MetricsLog::GetCurrentTime());
-  if (log.tab_id != -1) {
+  if (log.tab_id.is_valid()) {
     // If we know what tab the autocomplete URL was opened in, log it.
-    omnibox_event->set_tab_id(log.tab_id);
+    omnibox_event->set_tab_id(log.tab_id.id());
   }
   omnibox_event->set_typed_length(log.text.length());
   omnibox_event->set_just_deleted_text(log.just_deleted_text);
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h
index 7b4b8ff..8047b3b1 100644
--- a/components/prefs/pref_service.h
+++ b/components/prefs/pref_service.h
@@ -358,7 +358,8 @@
   // Pref Stores and profile that we passed to the PrefValueStore.
   const scoped_refptr<PersistentPrefStore> user_pref_store_;
 
-  // Callback to call when a read error occurs.
+  // Callback to call when a read error occurs. Always invoked on the sequence
+  // this PrefService was created own.
   const base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
       read_error_callback_;
 
diff --git a/components/prefs/pref_service_factory.h b/components/prefs/pref_service_factory.h
index 1f59c6a..c4579fb 100644
--- a/components/prefs/pref_service_factory.h
+++ b/components/prefs/pref_service_factory.h
@@ -52,8 +52,9 @@
     recommended_prefs_.swap(prefs);
   }
 
-  // Sets up error callback for the PrefService.  A do-nothing default
-  // is provided if this is not called.
+  // Sets up error callback for the PrefService.  A do-nothing default is
+  // provided if this is not called. This callback is always invoked (async or
+  // not) on the sequence on which Create is invoked.
   void set_read_error_callback(
       base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
           read_error_callback) {
diff --git a/components/safe_browsing/base_ping_manager.cc b/components/safe_browsing/base_ping_manager.cc
index 2bff3f32..4174206 100644
--- a/components/safe_browsing/base_ping_manager.cc
+++ b/components/safe_browsing/base_ping_manager.cc
@@ -6,18 +6,15 @@
 
 #include <utility>
 
-#include "base/base64.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/values.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
-#include "net/log/net_log_source_type.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request_context.h"
@@ -28,34 +25,6 @@
 using content::BrowserThread;
 
 namespace {
-// Returns a dictionary with "url"=|url-spec| and "data"=|payload| for
-// netlogging the start phase of a ping.
-std::unique_ptr<base::Value> NetLogPingStartCallback(
-    const net::NetLogWithSource& net_log,
-    const GURL& url,
-    const std::string& payload,
-    net::NetLogCaptureMode) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetString("url", url.spec());
-  event_params->SetString("payload", payload);
-  net_log.source().AddToEventParameters(event_params.get());
-  return std::move(event_params);
-}
-
-// Returns a dictionary with "url"=|url-spec|, "status"=|status| and
-// "error"=|error| for netlogging the end phase of a ping.
-std::unique_ptr<base::Value> NetLogPingEndCallback(
-    const net::NetLogWithSource& net_log,
-    const net::URLRequestStatus& status,
-    net::NetLogCaptureMode) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetInteger("status", status.status());
-  event_params->SetInteger("error", status.error());
-  net_log.source().AddToEventParameters(event_params.get());
-  return std::move(event_params);
-}
 
 net::NetworkTrafficAnnotationTag kTrafficAnnotation =
     net::DefineNetworkTrafficAnnotation("safe_browsing_extended_reporting",
@@ -116,12 +85,6 @@
       url_prefix_(config.url_prefix) {
   DCHECK(!url_prefix_.empty());
 
-  if (request_context_getter) {
-    net_log_ = net::NetLogWithSource::Make(
-        request_context_getter->GetURLRequestContext()->net_log(),
-        net::NetLogSourceType::SAFE_BROWSING);
-  }
-
   version_ = ProtocolManagerHelper::Version();
 }
 
@@ -131,9 +94,6 @@
 
 // All SafeBrowsing request responses are handled here.
 void BasePingManager::OnURLFetchComplete(const net::URLFetcher* source) {
-  net_log_.EndEvent(
-      net::NetLogEventType::SAFE_BROWSING_PING,
-      base::Bind(&NetLogPingEndCallback, net_log_, source->GetStatus()));
   auto it =
       std::find_if(safebrowsing_reports_.begin(), safebrowsing_reports_.end(),
                    [source](const std::unique_ptr<net::URLFetcher>& ptr) {
@@ -157,16 +117,8 @@
       report, data_use_measurement::DataUseUserData::SAFE_BROWSING);
   report_ptr->SetLoadFlags(net::LOAD_DISABLE_CACHE);
   report_ptr->SetRequestContext(request_context_getter_.get());
-  std::string post_data_base64;
-  if (!hit_report.post_data.empty()) {
+  if (!hit_report.post_data.empty())
     report_ptr->SetUploadData("text/plain", hit_report.post_data);
-    base::Base64Encode(hit_report.post_data, &post_data_base64);
-  }
-
-  net_log_.BeginEvent(
-      net::NetLogEventType::SAFE_BROWSING_PING,
-      base::Bind(&NetLogPingStartCallback, net_log_,
-                 report_ptr->GetOriginalURL(), post_data_base64));
 
   report->Start();
   safebrowsing_reports_.insert(std::move(report_ptr));
@@ -185,12 +137,6 @@
   // Don't try too hard to send reports on failures.
   fetcher->SetAutomaticallyRetryOn5xx(false);
 
-  std::string report_base64;
-  base::Base64Encode(report, &report_base64);
-  net_log_.BeginEvent(net::NetLogEventType::SAFE_BROWSING_PING,
-                      base::Bind(&NetLogPingStartCallback, net_log_,
-                                 fetcher->GetOriginalURL(), report_base64));
-
   fetcher->Start();
   safebrowsing_reports_.insert(std::move(fetcher));
 }
diff --git a/components/safe_browsing/base_ping_manager.h b/components/safe_browsing/base_ping_manager.h
index 5b64c30..16169bcd 100644
--- a/components/safe_browsing/base_ping_manager.h
+++ b/components/safe_browsing/base_ping_manager.h
@@ -18,7 +18,6 @@
 #include "components/safe_browsing/db/hit_report.h"
 #include "components/safe_browsing/db/util.h"
 #include "content/public/browser/permission_type.h"
-#include "net/log/net_log_with_source.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "url/gurl.h"
 
@@ -87,8 +86,6 @@
   // We add both "hit" and "detail" fetchers in this set.
   Reports safebrowsing_reports_;
 
-  net::NetLogWithSource net_log_;
-
   DISALLOW_COPY_AND_ASSIGN(BasePingManager);
 };
 
diff --git a/components/safe_browsing/base_ping_manager_unittest.cc b/components/safe_browsing/base_ping_manager_unittest.cc
index 7a197ff..2dfcd5d 100644
--- a/components/safe_browsing/base_ping_manager_unittest.cc
+++ b/components/safe_browsing/base_ping_manager_unittest.cc
@@ -12,10 +12,6 @@
 #include "base/values.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
-#include "net/log/net_log.h"
-#include "net/log/net_log_source_type.h"
-#include "net/log/test_net_log.h"
-#include "net/log/test_net_log_entry.h"
 #include "net/url_request/report_sender.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,10 +29,7 @@
 
 class BasePingManagerTest : public testing::Test {
  public:
-  BasePingManagerTest() : net_log_(new net::TestNetLog()) {
-    net_log_with_source_ = net::NetLogWithSource::Make(
-        net_log_.get(), net::NetLogSourceType::SAFE_BROWSING);
-  }
+  BasePingManagerTest() {}
 
  protected:
   void SetUp() override {
@@ -51,14 +44,11 @@
     config.url_prefix = kUrlPrefix;
     ping_manager_.reset(new BasePingManager(nullptr, config));
     ping_manager_->version_ = kAppVer;
-    ping_manager_->net_log_ = net_log_with_source_;
   }
 
   BasePingManager* ping_manager() { return ping_manager_.get(); }
 
   std::string key_param_;
-  std::unique_ptr<net::TestNetLog> net_log_;
-  net::NetLogWithSource net_log_with_source_;
   net::TestURLFetcherFactory fetcher_factory_;
   std::unique_ptr<BasePingManager> ping_manager_;
 };
@@ -206,126 +196,4 @@
       ping_manager()->ThreatDetailsUrl().spec());
 }
 
-TEST_F(BasePingManagerTest, TestReportThreatDetails) {
-  const std::string kThreatDetailsReportString = "Threat Details Report String";
-  std::string encoded_threat_report = "";
-  base::Base64Encode(kThreatDetailsReportString, &encoded_threat_report);
-  std::string expected_threat_details_url =
-      ping_manager()->ThreatDetailsUrl().spec();
-  const int kRequestErrorCode = -123;
-
-  // Start the report.
-  ping_manager()->ReportThreatDetails(kThreatDetailsReportString);
-
-  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  // Set some error response data on the fetcher to make things interesting.
-  fetcher->set_status(
-      net::URLRequestStatus(net::URLRequestStatus::FAILED, kRequestErrorCode));
-  // Tell the test fetcher to invoke the fetch callback.
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-
-  // We expect two net log entries: one when the ping starts, one when it ends.
-  net::TestNetLogEntry::List entries;
-  net_log_->GetEntries(&entries);
-  ASSERT_EQ(2u, entries.size());
-
-  // Check for expected log entries for the begin phase.
-  const net::TestNetLogEntry& start_entry = entries[0];
-  ASSERT_EQ(3u, start_entry.params->size());
-
-  std::string string_value;
-  EXPECT_TRUE(start_entry.GetStringValue("url", &string_value));
-  EXPECT_EQ(expected_threat_details_url, string_value);
-
-  EXPECT_TRUE(start_entry.GetStringValue("payload", &string_value));
-  EXPECT_EQ(encoded_threat_report, string_value);
-
-  // We don't really care what the source_dependency value is, just making sure
-  // it's there.
-  EXPECT_TRUE(start_entry.params->HasKey("source_dependency"));
-
-  // Check for expected log entries for the end phase.
-  const net::TestNetLogEntry& end_entry = entries[1];
-  ASSERT_EQ(3u, end_entry.params->size());
-
-  int int_value;
-  EXPECT_TRUE(end_entry.GetIntegerValue("status", &int_value));
-  EXPECT_EQ(net::URLRequestStatus::FAILED, int_value);
-
-  EXPECT_TRUE(end_entry.GetIntegerValue("error", &int_value));
-  EXPECT_EQ(kRequestErrorCode, int_value);
-
-  // We don't really care what the source_dependency value is, just making sure
-  // it's there.
-  EXPECT_TRUE(end_entry.params->HasKey("source_dependency"));
-}
-
-TEST_F(BasePingManagerTest, TestReportSafeBrowsingHit) {
-  const std::string kHitReportPostData = "Hit Report POST Data";
-  std::string encoded_post_data = "";
-  base::Base64Encode(kHitReportPostData, &encoded_post_data);
-
-  HitReport hp;
-  hp.malicious_url = GURL("http://malicious.url.com");
-  hp.page_url = GURL("http://page.url.com");
-  hp.referrer_url = GURL("http://referrer.url.com");
-  hp.threat_type = SB_THREAT_TYPE_URL_CLIENT_SIDE_MALWARE;
-  hp.threat_source = ThreatSource::LOCAL_PVER4;
-  hp.extended_reporting_level = SBER_LEVEL_OFF;
-  hp.is_metrics_reporting_active = false;
-  hp.is_subresource = true;
-  hp.population_id = "foo bar";
-  hp.post_data = kHitReportPostData;
-  std::string expected_hit_report_url =
-      ping_manager()->SafeBrowsingHitUrl(hp).spec();
-  const int kRequestErrorCode = -321;
-
-  // Start the report.
-  ping_manager()->ReportSafeBrowsingHit(hp);
-
-  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  // Set some error response data on the fetcher to make things interesting.
-  fetcher->set_status(
-      net::URLRequestStatus(net::URLRequestStatus::FAILED, kRequestErrorCode));
-  // Tell the test fetcher to invoke the fetch callback.
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-
-  // We expect two net log entries: one when the ping starts, one when it ends.
-  net::TestNetLogEntry::List entries;
-  net_log_->GetEntries(&entries);
-  ASSERT_EQ(2u, entries.size());
-
-  // Check for expected log entries for the begin phase.
-  const net::TestNetLogEntry& start_entry = entries[0];
-  ASSERT_EQ(3u, start_entry.params->size());
-
-  std::string string_value;
-  EXPECT_TRUE(start_entry.GetStringValue("url", &string_value));
-  EXPECT_EQ(expected_hit_report_url, string_value);
-
-  EXPECT_TRUE(start_entry.GetStringValue("payload", &string_value));
-  EXPECT_EQ(encoded_post_data, string_value);
-
-  // We don't really care what the source_dependency value is, just making sure
-  // it's there.
-  EXPECT_TRUE(start_entry.params->HasKey("source_dependency"));
-
-  // Check for expected log entries for the end phase.
-  const net::TestNetLogEntry& end_entry = entries[1];
-  ASSERT_EQ(3u, end_entry.params->size());
-
-  int int_value;
-  EXPECT_TRUE(end_entry.GetIntegerValue("status", &int_value));
-  EXPECT_EQ(net::URLRequestStatus::FAILED, int_value);
-
-  EXPECT_TRUE(end_entry.GetIntegerValue("error", &int_value));
-  EXPECT_EQ(kRequestErrorCode, int_value);
-
-  // We don't really care what the source_dependency value is, just making sure
-  // it's there.
-  EXPECT_TRUE(end_entry.params->HasKey("source_dependency"));
-}
-
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/BUILD.gn b/components/safe_browsing/password_protection/BUILD.gn
index e34a2e1..f898a91 100644
--- a/components/safe_browsing/password_protection/BUILD.gn
+++ b/components/safe_browsing/password_protection/BUILD.gn
@@ -29,6 +29,7 @@
       "//components/safe_browsing/db:database_manager",
       "//components/safe_browsing/db:v4_protocol_manager_util",
       "//components/safe_browsing/db:whitelist_checker_client",
+      "//components/sessions",
       "//content/public/browser:browser",
       "//net:net",
       "//third_party/protobuf:protobuf_lite",
diff --git a/components/safe_browsing/password_protection/DEPS b/components/safe_browsing/password_protection/DEPS
index c09e58fd..41ce05c 100644
--- a/components/safe_browsing/password_protection/DEPS
+++ b/components/safe_browsing/password_protection/DEPS
@@ -2,6 +2,7 @@
   "+components/content_settings/core/browser",
   "+components/history/core/browser",
   "+components/password_manager/core/browser/password_reuse_detector.h",
+  "+components/sessions",
   "+components/sync_preferences/testing_pref_service_syncable.h",
   "+content/public/test",
   "+net",
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index 13be5a6..903784f 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -149,7 +149,7 @@
   main_frame->set_url(main_frame_url_.spec());
   main_frame->set_frame_index(0 /* main frame */);
   password_protection_service_->FillReferrerChain(
-      main_frame_url_, -1 /* tab id not available */, main_frame);
+      main_frame_url_, SessionID::InvalidValue(), main_frame);
   bool clicked_through_interstitial =
       password_protection_service_->UserClickedThroughSBInterstitial(
           web_contents_);
diff --git a/components/safe_browsing/password_protection/password_protection_service.h b/components/safe_browsing/password_protection/password_protection_service.h
index 57bc6de..b4ae0a3 100644
--- a/components/safe_browsing/password_protection/password_protection_service.h
+++ b/components/safe_browsing/password_protection/password_protection_service.h
@@ -21,6 +21,7 @@
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "components/safe_browsing/proto/csd.pb.h"
+#include "components/sessions/core/session_id.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 
@@ -282,7 +283,8 @@
   // info into |frame|.
   virtual void FillReferrerChain(
       const GURL& event_url,
-      int event_tab_id,  // -1 if tab id is not available.
+      SessionID
+          event_tab_id,  // SessionID::InvalidValue() if tab not available.
       LoginReputationClientRequest::Frame* frame) = 0;
 
   void FillUserPopulation(
diff --git a/components/safe_browsing/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
index e5da260..3db8e9e 100644
--- a/components/safe_browsing/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/password_protection/password_protection_service_unittest.cc
@@ -143,7 +143,9 @@
   }
 
   MOCK_METHOD3(FillReferrerChain,
-               void(const GURL&, int, LoginReputationClientRequest::Frame*));
+               void(const GURL&,
+                    SessionID,
+                    LoginReputationClientRequest::Frame*));
   MOCK_METHOD1(MaybeLogPasswordReuseDetectedEvent, void(content::WebContents*));
   MOCK_METHOD2(ShowModalWarning,
                void(content::WebContents*, const std::string&));
diff --git a/components/services/heap_profiling/BUILD.gn b/components/services/heap_profiling/BUILD.gn
new file mode 100644
index 0000000..16f128c
--- /dev/null
+++ b/components/services/heap_profiling/BUILD.gn
@@ -0,0 +1,76 @@
+# 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("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
+
+static_library("heap_profiling") {
+  sources = [
+    "address.h",
+    "allocation_event.cc",
+    "allocation_event.h",
+    "allocation_tracker.cc",
+    "allocation_tracker.h",
+    "backtrace.cc",
+    "backtrace.h",
+    "backtrace_storage.cc",
+    "backtrace_storage.h",
+    "connection_manager.cc",
+    "connection_manager.h",
+    "heap_profiling_service.cc",
+    "heap_profiling_service.h",
+    "json_exporter.cc",
+    "json_exporter.h",
+    "receiver.h",
+    "receiver_pipe.cc",
+    "receiver_pipe.h",
+    "receiver_pipe_posix.cc",
+    "receiver_pipe_posix.h",
+    "receiver_pipe_win.cc",
+    "receiver_pipe_win.h",
+    "stream_parser.cc",
+    "stream_parser.h",
+    "stream_receiver.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/services/heap_profiling/public/cpp",
+    "//mojo/edk",
+    "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
+    "//third_party/zlib",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "backtrace_storage_unittest.cc",
+    "json_exporter_unittest.cc",
+    "stream_parser_unittest.cc",
+  ]
+  deps = [
+    ":heap_profiling",
+    "//base",
+    "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
+    "//testing/gtest",
+  ]
+}
+
+service_manifest("manifest") {
+  name = "heap_profiling"
+  source = "heap_profiling_manifest.json"
+}
+
+fuzzer_test("profiling_fuzzer") {
+  sources = [
+    "stream_fuzzer.cc",
+  ]
+  deps = [
+    ":heap_profiling",
+  ]
+  libfuzzer_options = [ "max_len = 64000" ]
+  dict = "stream_fuzzer.dict"
+}
diff --git a/chrome/profiling/DEPS b/components/services/heap_profiling/DEPS
similarity index 70%
rename from chrome/profiling/DEPS
rename to components/services/heap_profiling/DEPS
index b45205cc..6973709 100644
--- a/chrome/profiling/DEPS
+++ b/components/services/heap_profiling/DEPS
@@ -1,8 +1,6 @@
 include_rules = [
   "+components/services/heap_profiling/public",
-  "+content/public/child",
   "+mojo/edk/embedder",
   "+services/resource_coordinator/public",
-  "+services/service_manager/public/cpp",
   "+third_party/zlib/zlib.h",
 ]
diff --git a/components/services/heap_profiling/OWNERS b/components/services/heap_profiling/OWNERS
index 987c0c88..80cbc1b 100644
--- a/components/services/heap_profiling/OWNERS
+++ b/components/services/heap_profiling/OWNERS
@@ -1 +1,4 @@
 erikchen@chromium.org
+
+per-file heap_profiling_manifest.json=set noparent
+per-file heap_profiling_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chrome/profiling/address.h b/components/services/heap_profiling/address.h
similarity index 91%
rename from chrome/profiling/address.h
rename to components/services/heap_profiling/address.h
index 9a78e1d..4b1b9e4 100644
--- a/chrome/profiling/address.h
+++ b/components/services/heap_profiling/address.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_ADDRESS_H_
-#define CHROME_PROFILING_ADDRESS_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_ADDRESS_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_ADDRESS_H_
 
 #include <stdint.h>
 
@@ -62,4 +62,4 @@
 
 }  // namespace std
 
-#endif  // CHROME_PROFILING_ADDRESS_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_ADDRESS_H_
diff --git a/chrome/profiling/allocation_event.cc b/components/services/heap_profiling/allocation_event.cc
similarity index 92%
rename from chrome/profiling/allocation_event.cc
rename to components/services/heap_profiling/allocation_event.cc
index d8d3c95..a1f283eb 100644
--- a/chrome/profiling/allocation_event.cc
+++ b/components/services/heap_profiling/allocation_event.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/allocation_event.h"
+#include "components/services/heap_profiling/allocation_event.h"
 
 namespace profiling {
 
diff --git a/chrome/profiling/allocation_event.h b/components/services/heap_profiling/allocation_event.h
similarity index 91%
rename from chrome/profiling/allocation_event.h
rename to components/services/heap_profiling/allocation_event.h
index b9225b1..b923d769 100644
--- a/chrome/profiling/allocation_event.h
+++ b/components/services/heap_profiling/allocation_event.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_ALLOCATION_EVENT_H_
-#define CHROME_PROFILING_ALLOCATION_EVENT_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_ALLOCATION_EVENT_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_ALLOCATION_EVENT_H_
 
 #include <functional>
 #include <map>
 #include <unordered_set>
 
-#include "chrome/profiling/address.h"
-#include "chrome/profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/address.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
 #include "components/services/heap_profiling/public/cpp/stream.h"
 
 namespace profiling {
@@ -100,4 +100,4 @@
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_ALLOCATION_EVENT_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_ALLOCATION_EVENT_H_
diff --git a/chrome/profiling/allocation_tracker.cc b/components/services/heap_profiling/allocation_tracker.cc
similarity index 96%
rename from chrome/profiling/allocation_tracker.cc
rename to components/services/heap_profiling/allocation_tracker.cc
index 2f7914c..b29624a 100644
--- a/chrome/profiling/allocation_tracker.cc
+++ b/components/services/heap_profiling/allocation_tracker.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/allocation_tracker.h"
+#include "components/services/heap_profiling/allocation_tracker.h"
 
 #include "base/callback.h"
 #include "base/json/string_escape.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
 
 namespace profiling {
 
diff --git a/chrome/profiling/allocation_tracker.h b/components/services/heap_profiling/allocation_tracker.h
similarity index 89%
rename from chrome/profiling/allocation_tracker.h
rename to components/services/heap_profiling/allocation_tracker.h
index bdb25e0..26604fd3 100644
--- a/chrome/profiling/allocation_tracker.h
+++ b/components/services/heap_profiling/allocation_tracker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_ALLOCATION_TRACKER_H_
-#define CHROME_PROFILING_ALLOCATION_TRACKER_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_ALLOCATION_TRACKER_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_ALLOCATION_TRACKER_H_
 
 #include <map>
 #include <unordered_map>
@@ -12,9 +12,9 @@
 #include "base/macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/synchronization/lock.h"
-#include "chrome/profiling/allocation_event.h"
-#include "chrome/profiling/backtrace_storage.h"
-#include "chrome/profiling/memlog_receiver.h"
+#include "components/services/heap_profiling/allocation_event.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/receiver.h"
 
 namespace profiling {
 
@@ -22,7 +22,7 @@
 // allocation register and needs to be merged/deduped.
 //
 // This class is not threadsafe except as noted.
-class AllocationTracker : public MemlogReceiver {
+class AllocationTracker : public Receiver {
  public:
   using CompleteCallback = base::OnceClosure;
   using ContextMap = std::map<std::string, int>;
@@ -95,4 +95,4 @@
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_ALLOCATION_TRACKER_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_ALLOCATION_TRACKER_H_
diff --git a/chrome/profiling/backtrace.cc b/components/services/heap_profiling/backtrace.cc
similarity index 90%
rename from chrome/profiling/backtrace.cc
rename to components/services/heap_profiling/backtrace.cc
index 406f6a4..6f9e24b 100644
--- a/chrome/profiling/backtrace.cc
+++ b/components/services/heap_profiling/backtrace.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/backtrace.h"
+#include "components/services/heap_profiling/backtrace.h"
 
 #include <string.h>
 
 #include <algorithm>
 
 #include "base/hash.h"
-#include "chrome/profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
 
 namespace profiling {
 
diff --git a/chrome/profiling/backtrace.h b/components/services/heap_profiling/backtrace.h
similarity index 90%
rename from chrome/profiling/backtrace.h
rename to components/services/heap_profiling/backtrace.h
index c724786..10b8a31 100644
--- a/chrome/profiling/backtrace.h
+++ b/components/services/heap_profiling/backtrace.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_BACKTRACE_H_
-#define CHROME_PROFILING_BACKTRACE_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_BACKTRACE_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_BACKTRACE_H_
 
 #include <functional>
 #include <vector>
 
 #include "base/macros.h"
-#include "chrome/profiling/address.h"
+#include "components/services/heap_profiling/address.h"
 
 namespace profiling {
 
@@ -75,4 +75,4 @@
 
 }  // namespace std
 
-#endif  // CHROME_PROFILING_BACKTRACE_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_BACKTRACE_H_
diff --git a/chrome/profiling/backtrace_storage.cc b/components/services/heap_profiling/backtrace_storage.cc
similarity index 96%
rename from chrome/profiling/backtrace_storage.cc
rename to components/services/heap_profiling/backtrace_storage.cc
index c23e8b4c..789de6ba 100644
--- a/chrome/profiling/backtrace_storage.cc
+++ b/components/services/heap_profiling/backtrace_storage.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
 
 #include "base/logging.h"
-#include "chrome/profiling/backtrace.h"
+#include "components/services/heap_profiling/backtrace.h"
 
 namespace profiling {
 
diff --git a/chrome/profiling/backtrace_storage.h b/components/services/heap_profiling/backtrace_storage.h
similarity index 92%
rename from chrome/profiling/backtrace_storage.h
rename to components/services/heap_profiling/backtrace_storage.h
index d4c65ed..540f214a 100644
--- a/chrome/profiling/backtrace_storage.h
+++ b/components/services/heap_profiling/backtrace_storage.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_BACKTRACE_STORAGE_H_
-#define CHROME_PROFILING_BACKTRACE_STORAGE_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_BACKTRACE_STORAGE_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_BACKTRACE_STORAGE_H_
 
 #include <unordered_set>
 #include <vector>
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
-#include "chrome/profiling/backtrace.h"
+#include "components/services/heap_profiling/backtrace.h"
 
 namespace profiling {
 
@@ -104,4 +104,4 @@
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_BACKTRACE_STORAGE_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_BACKTRACE_STORAGE_H_
diff --git a/chrome/profiling/backtrace_storage_unittest.cc b/components/services/heap_profiling/backtrace_storage_unittest.cc
similarity index 93%
rename from chrome/profiling/backtrace_storage_unittest.cc
rename to components/services/heap_profiling/backtrace_storage_unittest.cc
index 40e54c1f..4ed9828 100644
--- a/chrome/profiling/backtrace_storage_unittest.cc
+++ b/components/services/heap_profiling/backtrace_storage_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
 
 #include <vector>
 
diff --git a/chrome/profiling/memlog_connection_manager.cc b/components/services/heap_profiling/connection_manager.cc
similarity index 82%
rename from chrome/profiling/memlog_connection_manager.cc
rename to components/services/heap_profiling/connection_manager.cc
index f3404e3..5f90017 100644
--- a/chrome/profiling/memlog_connection_manager.cc
+++ b/components/services/heap_profiling/connection_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/memlog_connection_manager.h"
+#include "components/services/heap_profiling/connection_manager.h"
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
@@ -11,11 +11,11 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread.h"
-#include "chrome/profiling/allocation_tracker.h"
-#include "chrome/profiling/json_exporter.h"
-#include "chrome/profiling/memlog_receiver_pipe.h"
-#include "chrome/profiling/memlog_stream_parser.h"
+#include "components/services/heap_profiling/allocation_tracker.h"
+#include "components/services/heap_profiling/json_exporter.h"
 #include "components/services/heap_profiling/public/cpp/client.h"
+#include "components/services/heap_profiling/receiver_pipe.h"
+#include "components/services/heap_profiling/stream_parser.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "third_party/zlib/zlib.h"
@@ -31,10 +31,10 @@
 const size_t kMinCountThreshold = 1024;
 }  // namespace
 
-MemlogConnectionManager::DumpArgs::DumpArgs() = default;
-MemlogConnectionManager::DumpArgs::DumpArgs(DumpArgs&& other) noexcept
+ConnectionManager::DumpArgs::DumpArgs() = default;
+ConnectionManager::DumpArgs::DumpArgs(DumpArgs&& other) noexcept
     : backtrace_storage_lock(std::move(other.backtrace_storage_lock)) {}
-MemlogConnectionManager::DumpArgs::~DumpArgs() = default;
+ConnectionManager::DumpArgs::~DumpArgs() = default;
 
 // Tracking information for DumpProcessForTracing(). This struct is
 // refcounted since there will be many background thread calls (one for each
@@ -43,8 +43,8 @@
 //
 // This class is not threadsafe, its members must only be accessed on the
 // I/O thread.
-struct MemlogConnectionManager::DumpProcessesForTracingTracking
-    : public MemlogConnectionManager::DumpArgs,
+struct ConnectionManager::DumpProcessesForTracingTracking
+    : public ConnectionManager::DumpArgs,
       public base::RefCountedThreadSafe<DumpProcessesForTracingTracking> {
   DumpProcessesForTracingTracking() = default;
 
@@ -66,12 +66,12 @@
   virtual ~DumpProcessesForTracingTracking() = default;
 };
 
-struct MemlogConnectionManager::Connection {
+struct ConnectionManager::Connection {
   Connection(AllocationTracker::CompleteCallback complete_cb,
              BacktraceStorage* backtrace_storage,
              base::ProcessId pid,
              mojom::ProfilingClientPtr client,
-             scoped_refptr<MemlogReceiverPipe> p,
+             scoped_refptr<ReceiverPipe> p,
              mojom::ProcessType process_type,
              uint32_t sampling_rate,
              mojom::StackMode stack_mode)
@@ -99,8 +99,8 @@
   base::Thread thread;
 
   mojom::ProfilingClientPtr client;
-  scoped_refptr<MemlogReceiverPipe> pipe;
-  scoped_refptr<MemlogStreamParser> parser;
+  scoped_refptr<ReceiverPipe> pipe;
+  scoped_refptr<StreamParser> parser;
   mojom::ProcessType process_type;
   mojom::StackMode stack_mode;
 
@@ -117,21 +117,20 @@
   uint32_t sampling_rate = 1;
 };
 
-MemlogConnectionManager::MemlogConnectionManager()
+ConnectionManager::ConnectionManager()
     : blocking_thread_("Blocking thread"), weak_factory_(this) {
   blocking_thread_.Start();
-  metrics_timer_.Start(FROM_HERE, base::TimeDelta::FromHours(24),
-                       base::Bind(&MemlogConnectionManager::ReportMetrics,
-                                  base::Unretained(this)));
+  metrics_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromHours(24),
+      base::Bind(&ConnectionManager::ReportMetrics, base::Unretained(this)));
 }
-MemlogConnectionManager::~MemlogConnectionManager() = default;
+ConnectionManager::~ConnectionManager() = default;
 
-void MemlogConnectionManager::OnNewConnection(
-    base::ProcessId pid,
-    mojom::ProfilingClientPtr client,
-    mojo::ScopedHandle receiver_pipe_end,
-    mojom::ProcessType process_type,
-    mojom::ProfilingParamsPtr params) {
+void ConnectionManager::OnNewConnection(base::ProcessId pid,
+                                        mojom::ProfilingClientPtr client,
+                                        mojo::ScopedHandle receiver_pipe_end,
+                                        mojom::ProcessType process_type,
+                                        mojom::ProfilingParamsPtr params) {
   base::AutoLock lock(connections_lock_);
 
   // Attempting to start profiling on an already profiled processs should have
@@ -142,7 +141,7 @@
   // It's theoretically possible that we started profiling a process, the
   // profiling was stopped [e.g. by hitting the 10-s timeout], and then we tried
   // to start profiling again. The ProfilingClient will refuse to start again.
-  // But the MemlogConnectionManager will not be able to distinguish this
+  // But the ConnectionManager will not be able to distinguish this
   // never-started ProfilingClient from a brand new ProfilingClient that happens
   // to share the same pid. This is a rare condition which should only happen
   // when the user is attempting to manually start profiling for processes, so
@@ -151,14 +150,14 @@
   base::PlatformFile receiver_handle;
   CHECK_EQ(MOJO_RESULT_OK, mojo::UnwrapPlatformFile(
                                std::move(receiver_pipe_end), &receiver_handle));
-  scoped_refptr<MemlogReceiverPipe> new_pipe =
-      new MemlogReceiverPipe(mojo::edk::ScopedPlatformHandle(
+  scoped_refptr<ReceiverPipe> new_pipe =
+      new ReceiverPipe(mojo::edk::ScopedPlatformHandle(
           mojo::edk::PlatformHandle(receiver_handle)));
 
   // The allocation tracker will call this on a background thread, so thunk
   // back to the current thread with weak pointers.
   AllocationTracker::CompleteCallback complete_cb =
-      base::BindOnce(&MemlogConnectionManager::OnConnectionCompleteThunk,
+      base::BindOnce(&ConnectionManager::OnConnectionCompleteThunk,
                      base::MessageLoop::current()->task_runner(),
                      weak_factory_.GetWeakPtr(), pid);
 
@@ -170,12 +169,11 @@
   options.message_loop_type = base::MessageLoop::TYPE_IO;
   connection->thread.StartWithOptions(options);
 
-  connection->parser = new MemlogStreamParser(&connection->tracker);
+  connection->parser = new StreamParser(&connection->tracker);
   new_pipe->SetReceiver(connection->thread.task_runner(), connection->parser);
 
   connection->thread.task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&MemlogReceiverPipe::StartReadingOnIOThread, new_pipe));
+      FROM_HERE, base::Bind(&ReceiverPipe::StartReadingOnIOThread, new_pipe));
 
   // Request the client start sending us data.
   connection->client->StartProfiling(std::move(params));
@@ -183,7 +181,7 @@
   connections_[pid] = std::move(connection);  // Transfers ownership.
 }
 
-std::vector<base::ProcessId> MemlogConnectionManager::GetConnectionPids() {
+std::vector<base::ProcessId> ConnectionManager::GetConnectionPids() {
   base::AutoLock lock(connections_lock_);
   std::vector<base::ProcessId> results;
   results.reserve(connections_.size());
@@ -194,7 +192,7 @@
 }
 
 std::vector<base::ProcessId>
-MemlogConnectionManager::GetConnectionPidsThatNeedVmRegions() {
+ConnectionManager::GetConnectionPidsThatNeedVmRegions() {
   base::AutoLock lock(connections_lock_);
   std::vector<base::ProcessId> results;
   results.reserve(connections_.size());
@@ -205,14 +203,14 @@
   return results;
 }
 
-void MemlogConnectionManager::OnConnectionComplete(base::ProcessId pid) {
+void ConnectionManager::OnConnectionComplete(base::ProcessId pid) {
   base::AutoLock lock(connections_lock_);
   auto found = connections_.find(pid);
   CHECK(found != connections_.end());
   connections_.erase(found);
 }
 
-void MemlogConnectionManager::ReportMetrics() {
+void ConnectionManager::ReportMetrics() {
   base::AutoLock lock(connections_lock_);
   for (auto& pair : connections_) {
     UMA_HISTOGRAM_ENUMERATION(
@@ -223,16 +221,16 @@
 }
 
 // static
-void MemlogConnectionManager::OnConnectionCompleteThunk(
+void ConnectionManager::OnConnectionCompleteThunk(
     scoped_refptr<base::SequencedTaskRunner> task_runner,
-    base::WeakPtr<MemlogConnectionManager> connection_manager,
+    base::WeakPtr<ConnectionManager> connection_manager,
     base::ProcessId pid) {
-  task_runner->PostTask(
-      FROM_HERE, base::BindOnce(&MemlogConnectionManager::OnConnectionComplete,
-                                connection_manager, pid));
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(&ConnectionManager::OnConnectionComplete,
+                                       connection_manager, pid));
 }
 
-void MemlogConnectionManager::DumpProcessesForTracing(
+void ConnectionManager::DumpProcessesForTracing(
     bool keep_small_allocations,
     bool strip_path_from_mapped_files,
     DumpProcessesForTracingCallback callback,
@@ -267,7 +265,7 @@
     // need to thunk back to the I/O thread.
     connection->tracker.SnapshotOnBarrier(
         barrier_id, task_runner,
-        base::BindOnce(&MemlogConnectionManager::DoDumpOneProcessForTracing,
+        base::BindOnce(&ConnectionManager::DoDumpOneProcessForTracing,
                        weak_factory_.GetWeakPtr(), tracking, pid,
                        connection->process_type, keep_small_allocations,
                        strip_path_from_mapped_files,
@@ -276,7 +274,7 @@
   }
 }
 
-void MemlogConnectionManager::DoDumpOneProcessForTracing(
+void ConnectionManager::DoDumpOneProcessForTracing(
     scoped_refptr<DumpProcessesForTracingTracking> tracking,
     base::ProcessId pid,
     mojom::ProcessType process_type,
diff --git a/chrome/profiling/memlog_connection_manager.h b/components/services/heap_profiling/connection_manager.h
similarity index 85%
rename from chrome/profiling/memlog_connection_manager.h
rename to components/services/heap_profiling/connection_manager.h
index 40931b05..33886cd 100644
--- a/chrome/profiling/memlog_connection_manager.h
+++ b/components/services/heap_profiling/connection_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_MEMLOG_CONNECTION_MANAGER_H_
-#define CHROME_PROFILING_MEMLOG_CONNECTION_MANAGER_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_CONNECTION_MANAGER_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_CONNECTION_MANAGER_H_
 
 #include <string>
 #include <vector>
@@ -17,9 +17,9 @@
 #include "base/threading/thread.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/profiling/allocation_event.h"
-#include "chrome/profiling/allocation_tracker.h"
-#include "chrome/profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/allocation_event.h"
+#include "components/services/heap_profiling/allocation_tracker.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
 #include "components/services/heap_profiling/public/mojom/heap_profiling_service.mojom.h"
 #include "mojo/edk/embedder/scoped_platform_handle.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
@@ -39,17 +39,17 @@
 // Manages all connections and logging for each process. Pipes are supplied by
 // the pipe server and this class will connect them to a parser and logger.
 //
-// Note |backtrace_storage| must outlive MemlogConnectionManager.
+// Note |backtrace_storage| must outlive ConnectionManager.
 //
 // This object is constructed on the UI thread, but the rest of the usage
 // (including deletion) is on the IO thread.
-class MemlogConnectionManager {
+class ConnectionManager {
   using DumpProcessesForTracingCallback = memory_instrumentation::mojom::
       HeapProfiler::DumpProcessesForTracingCallback;
 
  public:
-  MemlogConnectionManager();
-  ~MemlogConnectionManager();
+  ConnectionManager();
+  ~ConnectionManager();
 
   // Shared types for the dump-type-specific args structures.
   struct DumpArgs {
@@ -58,7 +58,7 @@
     ~DumpArgs();
 
    private:
-    friend MemlogConnectionManager;
+    friend ConnectionManager;
 
     // This lock keeps the backtrace atoms alive throughout the dumping
     // process. It will be initialized by DumpProcess.
@@ -112,7 +112,7 @@
   // These thunks post the request back to the given thread.
   static void OnConnectionCompleteThunk(
       scoped_refptr<base::SequencedTaskRunner> main_loop,
-      base::WeakPtr<MemlogConnectionManager> connection_manager,
+      base::WeakPtr<ConnectionManager> connection_manager,
       base::ProcessId process_id);
 
   BacktraceStorage backtrace_storage_;
@@ -134,16 +134,15 @@
   // To avoid deadlock, synchronous calls to the browser are made on a dedicated
   // thread that does nothing else. Both the IO thread and connection-specific
   // threads could potentially be processing messages from the browser process,
-  // which in turn could be blocked on sending more messages over the memlog
-  // pipe.
+  // which in turn could be blocked on sending more messages over the pipe.
   base::Thread blocking_thread_;
 
   // Must be last.
-  base::WeakPtrFactory<MemlogConnectionManager> weak_factory_;
+  base::WeakPtrFactory<ConnectionManager> weak_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(MemlogConnectionManager);
+  DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
 };
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_MEMLOG_CONNECTION_MANAGER_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_CONNECTION_MANAGER_H_
diff --git a/chrome/profiling/profiling_manifest.json b/components/services/heap_profiling/heap_profiling_manifest.json
similarity index 85%
rename from chrome/profiling/profiling_manifest.json
rename to components/services/heap_profiling/heap_profiling_manifest.json
index 5eb6f0ec..014a40dd 100644
--- a/chrome/profiling/profiling_manifest.json
+++ b/components/services/heap_profiling/heap_profiling_manifest.json
@@ -1,6 +1,6 @@
 {
-  "name": "profiling",
-  "display_name": "Profiling Service",
+  "name": "heap_profiling",
+  "display_name": "Heap Profiling Service",
   "sandbox_type": "profiling",
   "interface_provider_specs": {
     "service_manager:connector": {
diff --git a/chrome/profiling/profiling_service.cc b/components/services/heap_profiling/heap_profiling_service.cc
similarity index 63%
rename from chrome/profiling/profiling_service.cc
rename to components/services/heap_profiling/heap_profiling_service.cc
index a6f0f01..c86ecb5 100644
--- a/chrome/profiling/profiling_service.cc
+++ b/components/services/heap_profiling/heap_profiling_service.cc
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/profiling_service.h"
+#include "components/services/heap_profiling/heap_profiling_service.h"
 
 #include "base/logging.h"
-#include "content/public/common/service_manager_connection.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
@@ -14,61 +13,64 @@
 
 namespace profiling {
 
-ProfilingService::ProfilingService()
+HeapProfilingService::HeapProfilingService()
     : binding_(this), heap_profiler_binding_(this), weak_factory_(this) {}
 
-ProfilingService::~ProfilingService() {}
+HeapProfilingService::~HeapProfilingService() {}
 
-std::unique_ptr<service_manager::Service> ProfilingService::CreateService() {
-  return std::make_unique<ProfilingService>();
+std::unique_ptr<service_manager::Service>
+HeapProfilingService::CreateService() {
+  return std::make_unique<HeapProfilingService>();
 }
 
-void ProfilingService::OnStart() {
+void HeapProfilingService::OnStart() {
+  registry_.AddInterface(
+      base::Bind(&HeapProfilingService::OnProfilingServiceRequest,
+                 base::Unretained(this)));
   registry_.AddInterface(base::Bind(
-      &ProfilingService::OnProfilingServiceRequest, base::Unretained(this)));
-  registry_.AddInterface(base::Bind(&ProfilingService::OnHeapProfilerRequest,
-                                    base::Unretained(this)));
+      &HeapProfilingService::OnHeapProfilerRequest, base::Unretained(this)));
 }
 
-void ProfilingService::OnBindInterface(
+void HeapProfilingService::OnBindInterface(
     const service_manager::BindSourceInfo& source_info,
     const std::string& interface_name,
     mojo::ScopedMessagePipeHandle interface_pipe) {
   registry_.BindInterface(interface_name, std::move(interface_pipe));
 }
 
-void ProfilingService::OnProfilingServiceRequest(
+void HeapProfilingService::OnProfilingServiceRequest(
     mojom::ProfilingServiceRequest request) {
   binding_.Bind(std::move(request));
 }
 
-void ProfilingService::OnHeapProfilerRequest(
+void HeapProfilingService::OnHeapProfilerRequest(
     memory_instrumentation::mojom::HeapProfilerRequest request) {
   heap_profiler_binding_.Bind(std::move(request));
 }
 
-void ProfilingService::AddProfilingClient(
+void HeapProfilingService::AddProfilingClient(
     base::ProcessId pid,
     mojom::ProfilingClientPtr client,
-    mojo::ScopedHandle memlog_pipe_receiver,
+    mojo::ScopedHandle pipe_receiver,
     mojom::ProcessType process_type,
     mojom::ProfilingParamsPtr params) {
   if (params->sampling_rate == 0)
     params->sampling_rate = 1;
   connection_manager_.OnNewConnection(pid, std::move(client),
-                                      std::move(memlog_pipe_receiver),
-                                      process_type, std::move(params));
+                                      std::move(pipe_receiver), process_type,
+                                      std::move(params));
 }
 
-void ProfilingService::SetKeepSmallAllocations(bool keep_small_allocations) {
+void HeapProfilingService::SetKeepSmallAllocations(
+    bool keep_small_allocations) {
   keep_small_allocations_ = keep_small_allocations;
 }
 
-void ProfilingService::GetProfiledPids(GetProfiledPidsCallback callback) {
+void HeapProfilingService::GetProfiledPids(GetProfiledPidsCallback callback) {
   std::move(callback).Run(connection_manager_.GetConnectionPids());
 }
 
-void ProfilingService::DumpProcessesForTracing(
+void HeapProfilingService::DumpProcessesForTracing(
     bool strip_path_from_mapped_files,
     const DumpProcessesForTracingCallback& callback) {
   if (!helper_) {
@@ -86,15 +88,14 @@
     // Need a memory map to make sense of the dump. The dump will be triggered
     // in the memory map global dump callback.
     helper_->GetVmRegionsForHeapProfiler(
-        pids,
-        base::Bind(
-            &ProfilingService::OnGetVmRegionsCompleteForDumpProcessesForTracing,
-            weak_factory_.GetWeakPtr(), strip_path_from_mapped_files,
-            callback));
+        pids, base::Bind(&HeapProfilingService::
+                             OnGetVmRegionsCompleteForDumpProcessesForTracing,
+                         weak_factory_.GetWeakPtr(),
+                         strip_path_from_mapped_files, callback));
   }
 }
 
-void ProfilingService::OnGetVmRegionsCompleteForDumpProcessesForTracing(
+void HeapProfilingService::OnGetVmRegionsCompleteForDumpProcessesForTracing(
     bool strip_path_from_mapped_files,
     const DumpProcessesForTracingCallback& callback,
     VmRegions vm_regions) {
diff --git a/chrome/profiling/profiling_service.h b/components/services/heap_profiling/heap_profiling_service.h
similarity index 78%
rename from chrome/profiling/profiling_service.h
rename to components/services/heap_profiling/heap_profiling_service.h
index 952c6bb..3d73b661 100644
--- a/chrome/profiling/profiling_service.h
+++ b/components/services/heap_profiling/heap_profiling_service.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_PROFILING_SERVICE_H_
-#define CHROME_PROFILING_PROFILING_SERVICE_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_HEAP_PROFILING_SERVICE_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_HEAP_PROFILING_SERVICE_H_
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/profiling/memlog_connection_manager.h"
+#include "components/services/heap_profiling/connection_manager.h"
 #include "components/services/heap_profiling/public/mojom/heap_profiling_service.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
@@ -15,23 +15,22 @@
 
 namespace profiling {
 
-class MemlogImpl;
-
 // Service implementation for Profiling. This will be called in the profiling
 // process (which is a sandboxed utility process created on demand by the
 // ServiceManager) to set manage the global state as well as the bound
 // interface.
 //
 // This class lives in the I/O thread of the Utility process.
-class ProfilingService : public service_manager::Service,
-                         public mojom::ProfilingService,
-                         public memory_instrumentation::mojom::HeapProfiler {
+class HeapProfilingService
+    : public service_manager::Service,
+      public mojom::ProfilingService,
+      public memory_instrumentation::mojom::HeapProfiler {
   using DumpProcessesForTracingCallback = memory_instrumentation::mojom::
       HeapProfiler::DumpProcessesForTracingCallback;
 
  public:
-  ProfilingService();
-  ~ProfilingService() override;
+  HeapProfilingService();
+  ~HeapProfilingService() override;
 
   // Factory method for creating the service. Used by the ServiceManager piping
   // to instantiate this thing.
@@ -46,7 +45,7 @@
   // ProfilingService implementation.
   void AddProfilingClient(base::ProcessId pid,
                           mojom::ProfilingClientPtr client,
-                          mojo::ScopedHandle memlog_pipe_receiver,
+                          mojo::ScopedHandle pipe_receiver,
                           mojom::ProcessType process_type,
                           mojom::ProfilingParamsPtr params) override;
   void SetKeepSmallAllocations(bool keep_small_allocations) override;
@@ -58,8 +57,7 @@
       const DumpProcessesForTracingCallback& callback) override;
 
  private:
-  void OnProfilingServiceRequest(
-      mojom::ProfilingServiceRequest request);
+  void OnProfilingServiceRequest(mojom::ProfilingServiceRequest request);
   void OnHeapProfilerRequest(
       memory_instrumentation::mojom::HeapProfilerRequest request);
 
@@ -75,14 +73,14 @@
       heap_profiler_binding_;
 
   memory_instrumentation::mojom::HeapProfilerHelperPtr helper_;
-  MemlogConnectionManager connection_manager_;
+  ConnectionManager connection_manager_;
 
   bool keep_small_allocations_ = false;
 
   // Must be last.
-  base::WeakPtrFactory<ProfilingService> weak_factory_;
+  base::WeakPtrFactory<HeapProfilingService> weak_factory_;
 };
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_PROFILING_SERVICE_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_HEAP_PROFILING_SERVICE_H_
diff --git a/chrome/profiling/json_exporter.cc b/components/services/heap_profiling/json_exporter.cc
similarity index 99%
rename from chrome/profiling/json_exporter.cc
rename to components/services/heap_profiling/json_exporter.cc
index b401734..e80bd2ab0 100644
--- a/chrome/profiling/json_exporter.cc
+++ b/components/services/heap_profiling/json_exporter.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/json_exporter.h"
+#include "components/services/heap_profiling/json_exporter.h"
 
 #include <map>
 
diff --git a/chrome/profiling/json_exporter.h b/components/services/heap_profiling/json_exporter.h
similarity index 91%
rename from chrome/profiling/json_exporter.h
rename to components/services/heap_profiling/json_exporter.h
index d370626..fb4b700b 100644
--- a/chrome/profiling/json_exporter.h
+++ b/components/services/heap_profiling/json_exporter.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_JSON_EXPORTER_H_
-#define CHROME_PROFILING_JSON_EXPORTER_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_JSON_EXPORTER_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_JSON_EXPORTER_H_
 
 #include <iosfwd>
 #include <vector>
 
 #include "base/values.h"
-#include "chrome/profiling/allocation_event.h"
+#include "components/services/heap_profiling/allocation_event.h"
 #include "components/services/heap_profiling/public/cpp/stream.h"
 #include "components/services/heap_profiling/public/mojom/heap_profiling_service.mojom.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
@@ -71,4 +71,4 @@
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_JSON_EXPORTER_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_JSON_EXPORTER_H_
diff --git a/chrome/profiling/json_exporter_unittest.cc b/components/services/heap_profiling/json_exporter_unittest.cc
similarity index 98%
rename from chrome/profiling/json_exporter_unittest.cc
rename to components/services/heap_profiling/json_exporter_unittest.cc
index 2551a74e..1d958ca 100644
--- a/chrome/profiling/json_exporter_unittest.cc
+++ b/components/services/heap_profiling/json_exporter_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/json_exporter.h"
+#include "components/services/heap_profiling/json_exporter.h"
 
 #include <sstream>
 
@@ -13,7 +13,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/profiling/backtrace_storage.h"
+#include "components/services/heap_profiling/backtrace_storage.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/services/heap_profiling/public/mojom/constants.mojom b/components/services/heap_profiling/public/mojom/constants.mojom
index 6527af9..bd58c2e3 100644
--- a/components/services/heap_profiling/public/mojom/constants.mojom
+++ b/components/services/heap_profiling/public/mojom/constants.mojom
@@ -4,4 +4,4 @@
 
 module profiling.mojom;
 
-const string kServiceName = "profiling";
+const string kServiceName = "heap_profiling";
diff --git a/chrome/profiling/memlog_receiver.h b/components/services/heap_profiling/receiver.h
similarity index 73%
rename from chrome/profiling/memlog_receiver.h
rename to components/services/heap_profiling/receiver.h
index 9e906e8..6061e98 100644
--- a/chrome/profiling/memlog_receiver.h
+++ b/components/services/heap_profiling/receiver.h
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_MEMLOG_RECEIVER_H_
-#define CHROME_PROFILING_MEMLOG_RECEIVER_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_H_
 
 #include <vector>
 
 #include "base/memory/ref_counted.h"
-#include "chrome/profiling/address.h"
+#include "components/services/heap_profiling/address.h"
 #include "components/services/heap_profiling/public/cpp/stream.h"
 
 namespace profiling {
 
 // A log receiver is a sink for parsed allocation events. See also
-// MemlogStreamReceiver which is for the unparsed data blocks.
-class MemlogReceiver {
+// StreamReceiver which is for the unparsed data blocks.
+class Receiver {
  public:
-  virtual ~MemlogReceiver() {}
+  virtual ~Receiver() {}
 
   virtual void OnHeader(const StreamHeader& header) = 0;
   virtual void OnAlloc(const AllocPacket& alloc_packet,
@@ -32,4 +32,4 @@
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_MEMLOG_RECEIVER_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_H_
diff --git a/components/services/heap_profiling/receiver_pipe.cc b/components/services/heap_profiling/receiver_pipe.cc
new file mode 100644
index 0000000..d7d9dfc7
--- /dev/null
+++ b/components/services/heap_profiling/receiver_pipe.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 "components/services/heap_profiling/receiver_pipe.h"
+
+#include "base/bind.h"
+#include "base/task_runner.h"
+#include "components/services/heap_profiling/stream_receiver.h"
+
+namespace profiling {
+
+ReceiverPipeBase::ReceiverPipeBase(mojo::edk::ScopedPlatformHandle handle)
+    : handle_(std::move(handle)) {}
+
+ReceiverPipeBase::~ReceiverPipeBase() = default;
+
+void ReceiverPipeBase::SetReceiver(scoped_refptr<base::TaskRunner> task_runner,
+                                   scoped_refptr<StreamReceiver> receiver) {
+  receiver_task_runner_ = std::move(task_runner);
+  receiver_ = receiver;
+}
+
+void ReceiverPipeBase::ReportError() {
+  handle_.reset();
+}
+
+void ReceiverPipeBase::OnStreamDataThunk(
+    scoped_refptr<base::TaskRunner> pipe_task_runner,
+    std::unique_ptr<char[]> data,
+    size_t size) {
+  if (!receiver_->OnStreamData(std::move(data), size)) {
+    pipe_task_runner->PostTask(
+        FROM_HERE, base::BindOnce(&ReceiverPipeBase::ReportError, this));
+  }
+}
+
+}  // namespace profiling
diff --git a/chrome/profiling/memlog_receiver_pipe.h b/components/services/heap_profiling/receiver_pipe.h
similarity index 64%
rename from chrome/profiling/memlog_receiver_pipe.h
rename to components/services/heap_profiling/receiver_pipe.h
index f51b0c8e..c91c21d1 100644
--- a/chrome/profiling/memlog_receiver_pipe.h
+++ b/components/services/heap_profiling/receiver_pipe.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_H_
-#define CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_H_
 
 #include "base/memory/ref_counted.h"
 #include "build/build_config.h"
@@ -15,23 +15,22 @@
 
 namespace profiling {
 
-class MemlogStreamReceiver;
+class StreamReceiver;
 
 // Base class for the platform-specific receiver pipes. Since there is only
 // ever one actual implementation of this in the system, those implementations
-// are called "MemlogReceiverPipe" and the common functions are not
+// are called "ReceiverPipe" and the common functions are not
 // virtual. This class is just for the shared implementation.
-class MemlogReceiverPipeBase
-    : public base::RefCountedThreadSafe<MemlogReceiverPipeBase> {
+class ReceiverPipeBase : public base::RefCountedThreadSafe<ReceiverPipeBase> {
  public:
   void SetReceiver(scoped_refptr<base::TaskRunner> task_runner,
-                   scoped_refptr<MemlogStreamReceiver> receiver);
+                   scoped_refptr<StreamReceiver> receiver);
 
  protected:
-  friend class base::RefCountedThreadSafe<MemlogReceiverPipeBase>;
+  friend class base::RefCountedThreadSafe<ReceiverPipeBase>;
 
-  explicit MemlogReceiverPipeBase(mojo::edk::ScopedPlatformHandle handle);
-  virtual ~MemlogReceiverPipeBase();
+  explicit ReceiverPipeBase(mojo::edk::ScopedPlatformHandle handle);
+  virtual ~ReceiverPipeBase();
 
   // Callback that indicates an error has occurred and the connection should
   // be closed. May be called more than once in an error condition.
@@ -45,7 +44,7 @@
                          size_t size);
 
   scoped_refptr<base::TaskRunner> receiver_task_runner_;
-  scoped_refptr<MemlogStreamReceiver> receiver_;
+  scoped_refptr<StreamReceiver> receiver_;
 
   mojo::edk::ScopedPlatformHandle handle_;
 };
@@ -54,9 +53,9 @@
 
 // Define the platform-specific specialization.
 #if defined(OS_WIN)
-#include "chrome/profiling/memlog_receiver_pipe_win.h"
+#include "components/services/heap_profiling/receiver_pipe_win.h"
 #else
-#include "chrome/profiling/memlog_receiver_pipe_posix.h"
+#include "components/services/heap_profiling/receiver_pipe_posix.h"
 #endif
 
-#endif  // CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_H_
diff --git a/chrome/profiling/memlog_receiver_pipe_posix.cc b/components/services/heap_profiling/receiver_pipe_posix.cc
similarity index 64%
rename from chrome/profiling/memlog_receiver_pipe_posix.cc
rename to components/services/heap_profiling/receiver_pipe_posix.cc
index fa4a99b..11f84352 100644
--- a/chrome/profiling/memlog_receiver_pipe_posix.cc
+++ b/components/services/heap_profiling/receiver_pipe_posix.cc
@@ -2,36 +2,36 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/memlog_receiver_pipe_posix.h"
+#include "components/services/heap_profiling/receiver_pipe_posix.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
-#include "chrome/profiling/memlog_receiver_pipe.h"
-#include "chrome/profiling/memlog_stream_receiver.h"
 #include "components/services/heap_profiling/public/cpp/sender_pipe.h"
+#include "components/services/heap_profiling/receiver_pipe.h"
+#include "components/services/heap_profiling/stream_receiver.h"
 #include "mojo/edk/embedder/platform_channel_utils_posix.h"
 #include "mojo/edk/embedder/platform_handle.h"
 
 namespace profiling {
 
-MemlogReceiverPipe::MemlogReceiverPipe(mojo::edk::ScopedPlatformHandle handle)
-    : MemlogReceiverPipeBase(std::move(handle)),
+ReceiverPipe::ReceiverPipe(mojo::edk::ScopedPlatformHandle handle)
+    : ReceiverPipeBase(std::move(handle)),
       controller_(FROM_HERE),
       read_buffer_(new char[SenderPipe::kPipeSize]) {}
 
-MemlogReceiverPipe::~MemlogReceiverPipe() {}
+ReceiverPipe::~ReceiverPipe() {}
 
-void MemlogReceiverPipe::StartReadingOnIOThread() {
+void ReceiverPipe::StartReadingOnIOThread() {
   base::MessageLoopForIO::current()->WatchFileDescriptor(
       handle_.get().handle, true, base::MessageLoopForIO::WATCH_READ,
       &controller_, this);
   OnFileCanReadWithoutBlocking(handle_.get().handle);
 }
 
-void MemlogReceiverPipe::OnFileCanReadWithoutBlocking(int fd) {
+void ReceiverPipe::OnFileCanReadWithoutBlocking(int fd) {
   ssize_t bytes_read = 0;
   do {
     base::circular_deque<mojo::edk::PlatformHandle> dummy_for_receive;
@@ -40,11 +40,10 @@
         read(handle_.get().handle, read_buffer_.get(), SenderPipe::kPipeSize));
     if (bytes_read > 0) {
       receiver_task_runner_->PostTask(
-          FROM_HERE,
-          base::BindOnce(&MemlogReceiverPipe::OnStreamDataThunk, this,
-                         base::MessageLoop::current()->task_runner(),
-                         std::move(read_buffer_),
-                         static_cast<size_t>(bytes_read)));
+          FROM_HERE, base::BindOnce(&ReceiverPipe::OnStreamDataThunk, this,
+                                    base::MessageLoop::current()->task_runner(),
+                                    std::move(read_buffer_),
+                                    static_cast<size_t>(bytes_read)));
       read_buffer_.reset(new char[SenderPipe::kPipeSize]);
       return;
     } else if (bytes_read == 0) {
@@ -53,7 +52,7 @@
       DCHECK(receiver_task_runner_);
       receiver_task_runner_->PostTask(
           FROM_HERE,
-          base::BindOnce(&MemlogStreamReceiver::OnStreamComplete, receiver_));
+          base::BindOnce(&StreamReceiver::OnStreamComplete, receiver_));
       return;
     } else {
       if (errno != EAGAIN && errno != EWOULDBLOCK) {
@@ -62,13 +61,13 @@
         DCHECK(receiver_task_runner_);
         receiver_task_runner_->PostTask(
             FROM_HERE,
-            base::BindOnce(&MemlogStreamReceiver::OnStreamComplete, receiver_));
+            base::BindOnce(&StreamReceiver::OnStreamComplete, receiver_));
       }
     }
   } while (bytes_read > 0);
 }
 
-void MemlogReceiverPipe::OnFileCanWriteWithoutBlocking(int fd) {
+void ReceiverPipe::OnFileCanWriteWithoutBlocking(int fd) {
   NOTREACHED();
 }
 
diff --git a/components/services/heap_profiling/receiver_pipe_posix.h b/components/services/heap_profiling/receiver_pipe_posix.h
new file mode 100644
index 0000000..e9b5b36
--- /dev/null
+++ b/components/services/heap_profiling/receiver_pipe_posix.h
@@ -0,0 +1,41 @@
+// 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_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_POSIX_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_POSIX_H_
+
+#include <string>
+
+#include "base/files/platform_file.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "build/build_config.h"
+#include "components/services/heap_profiling/receiver_pipe.h"
+
+namespace profiling {
+
+class ReceiverPipe : public ReceiverPipeBase,
+                     public base::MessageLoopForIO::Watcher {
+ public:
+  explicit ReceiverPipe(mojo::edk::ScopedPlatformHandle handle);
+
+  // Must be called on the IO thread.
+  void StartReadingOnIOThread();
+
+ private:
+  ~ReceiverPipe() override;
+
+  // MessageLoopForIO::Watcher implementation.
+  void OnFileCanReadWithoutBlocking(int fd) override;
+  void OnFileCanWriteWithoutBlocking(int fd) override;
+
+  base::MessageLoopForIO::FileDescriptorWatcher controller_;
+  std::unique_ptr<char[]> read_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReceiverPipe);
+};
+
+}  // namespace profiling
+
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_POSIX_H_
diff --git a/chrome/profiling/memlog_receiver_pipe_win.cc b/components/services/heap_profiling/receiver_pipe_win.cc
similarity index 72%
rename from chrome/profiling/memlog_receiver_pipe_win.cc
rename to components/services/heap_profiling/receiver_pipe_win.cc
index 478b4cc..3c27de0 100644
--- a/chrome/profiling/memlog_receiver_pipe_win.cc
+++ b/components/services/heap_profiling/receiver_pipe_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/memlog_receiver_pipe_win.h"
+#include "components/services/heap_profiling/receiver_pipe_win.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
@@ -10,28 +10,27 @@
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread.h"
-#include "chrome/profiling/memlog_receiver_pipe.h"
-#include "chrome/profiling/memlog_stream_receiver.h"
 #include "components/services/heap_profiling/public/cpp/sender_pipe.h"
+#include "components/services/heap_profiling/receiver_pipe.h"
+#include "components/services/heap_profiling/stream_receiver.h"
 
 namespace profiling {
 
-MemlogReceiverPipe::MemlogReceiverPipe(mojo::edk::ScopedPlatformHandle handle)
-    : MemlogReceiverPipeBase(std::move(handle)),
+ReceiverPipe::ReceiverPipe(mojo::edk::ScopedPlatformHandle handle)
+    : ReceiverPipeBase(std::move(handle)),
       read_buffer_(new char[SenderPipe::kPipeSize]) {
   ZeroOverlapped();
   base::MessageLoopForIO::current()->RegisterIOHandler(handle_.get().handle,
                                                        this);
 }
 
-MemlogReceiverPipe::~MemlogReceiverPipe() {
-}
+ReceiverPipe::~ReceiverPipe() {}
 
-void MemlogReceiverPipe::StartReadingOnIOThread() {
+void ReceiverPipe::StartReadingOnIOThread() {
   ReadUntilBlocking();
 }
 
-void MemlogReceiverPipe::ReadUntilBlocking() {
+void ReceiverPipe::ReadUntilBlocking() {
   // TODO(brettw) note that the IO completion callback will always be issued,
   // even for sync returns of ReadFile. If there is a lot of data ready to be
   // read, it would be nice to process them all in this loop rather than having
@@ -50,31 +49,30 @@
       if (receiver_) {
         receiver_task_runner_->PostTask(
             FROM_HERE,
-            base::BindOnce(&MemlogStreamReceiver::OnStreamComplete, receiver_));
+            base::BindOnce(&StreamReceiver::OnStreamComplete, receiver_));
       }
       return;
     }
   }
 }
 
-void MemlogReceiverPipe::ZeroOverlapped() {
+void ReceiverPipe::ZeroOverlapped() {
   memset(&context_.overlapped, 0, sizeof(OVERLAPPED));
 }
 
-void MemlogReceiverPipe::OnIOCompleted(
-    base::MessagePumpForIO::IOContext* context,
-    DWORD bytes_transfered,
-    DWORD error) {
+void ReceiverPipe::OnIOCompleted(base::MessagePumpForIO::IOContext* context,
+                                 DWORD bytes_transfered,
+                                 DWORD error) {
   // Note: any crashes with this on the stack are likely a result of destroying
   // a relevant class while there is I/O pending.
   DCHECK(read_outstanding_);
   // Clear |read_outstanding_| but retain the reference to keep ourself alive
   // until this function returns.
-  scoped_refptr<MemlogReceiverPipe> self(std::move(read_outstanding_));
+  scoped_refptr<ReceiverPipe> self(std::move(read_outstanding_));
 
   if (bytes_transfered && receiver_) {
     receiver_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&MemlogReceiverPipe::OnStreamDataThunk, this,
+        FROM_HERE, base::BindOnce(&ReceiverPipe::OnStreamDataThunk, this,
                                   base::MessageLoop::current()->task_runner(),
                                   std::move(read_buffer_),
                                   static_cast<size_t>(bytes_transfered)));
diff --git a/chrome/profiling/memlog_receiver_pipe_win.h b/components/services/heap_profiling/receiver_pipe_win.h
similarity index 64%
rename from chrome/profiling/memlog_receiver_pipe_win.h
rename to components/services/heap_profiling/receiver_pipe_win.h
index 064f5418..75d2aa9 100644
--- a/chrome/profiling/memlog_receiver_pipe_win.h
+++ b/components/services/heap_profiling/receiver_pipe_win.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_WIN_H_
-#define CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_WIN_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_WIN_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_WIN_H_
 
 #include <windows.h>
 
@@ -14,20 +14,20 @@
 #include "base/message_loop/message_pump_win.h"
 #include "base/strings/string16.h"
 #include "build/build_config.h"
-#include "chrome/profiling/memlog_receiver_pipe.h"
+#include "components/services/heap_profiling/receiver_pipe.h"
 
 namespace profiling {
 
-class MemlogReceiverPipe : public MemlogReceiverPipeBase,
-                           public base::MessagePumpForIO::IOHandler {
+class ReceiverPipe : public ReceiverPipeBase,
+                     public base::MessagePumpForIO::IOHandler {
  public:
-  explicit MemlogReceiverPipe(mojo::edk::ScopedPlatformHandle handle);
+  explicit ReceiverPipe(mojo::edk::ScopedPlatformHandle handle);
 
   // Must be called on the IO thread.
   void StartReadingOnIOThread();
 
  private:
-  ~MemlogReceiverPipe() override;
+  ~ReceiverPipe() override;
 
   void ReadUntilBlocking();
   void ZeroOverlapped();
@@ -41,13 +41,13 @@
 
   // Used to keep |this| live while awaiting IO completion, which is required
   // to avoid premature destruction during shutdown.
-  scoped_refptr<MemlogReceiverPipe> read_outstanding_;
+  scoped_refptr<ReceiverPipe> read_outstanding_;
 
   std::unique_ptr<char[]> read_buffer_;
 
-  DISALLOW_COPY_AND_ASSIGN(MemlogReceiverPipe);
+  DISALLOW_COPY_AND_ASSIGN(ReceiverPipe);
 };
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_MEMLOG_RECEIVER_PIPE_WIN_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_RECEIVER_PIPE_WIN_H_
diff --git a/chrome/profiling/memlog_stream_fuzzer.cc b/components/services/heap_profiling/stream_fuzzer.cc
similarity index 76%
rename from chrome/profiling/memlog_stream_fuzzer.cc
rename to components/services/heap_profiling/stream_fuzzer.cc
index b277a7e5..d8aa4964 100644
--- a/chrome/profiling/memlog_stream_fuzzer.cc
+++ b/components/services/heap_profiling/stream_fuzzer.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/memlog_receiver.h"
-#include "chrome/profiling/memlog_stream_parser.h"
+#include "components/services/heap_profiling/receiver.h"
+#include "components/services/heap_profiling/stream_parser.h"
 
 #include <utility>
 
 namespace profiling {
 namespace {
 
-class DummyMemlogReceiver : public profiling::MemlogReceiver {
+class DummyReceiver : public profiling::Receiver {
   void OnHeader(const StreamHeader& header) override {}
   void OnAlloc(const AllocPacket& alloc_packet,
                std::vector<Address>&& stack,
@@ -27,9 +27,9 @@
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  profiling::DummyMemlogReceiver receiver;
-  scoped_refptr<profiling::MemlogStreamParser> parser(
-      new profiling::MemlogStreamParser(&receiver));
+  profiling::DummyReceiver receiver;
+  scoped_refptr<profiling::StreamParser> parser(
+      new profiling::StreamParser(&receiver));
   std::unique_ptr<char[]> stream_data(new char[size]);
   memcpy(stream_data.get(), data, size);
   parser->OnStreamData(std::move(stream_data), size);
diff --git a/chrome/profiling/memlog_stream_fuzzer.dict b/components/services/heap_profiling/stream_fuzzer.dict
similarity index 68%
rename from chrome/profiling/memlog_stream_fuzzer.dict
rename to components/services/heap_profiling/stream_fuzzer.dict
index 1460b85..7accea7 100644
--- a/chrome/profiling/memlog_stream_fuzzer.dict
+++ b/components/services/heap_profiling/stream_fuzzer.dict
@@ -1,4 +1,4 @@
-# These values are obtained from chrome/common/profiling//memlog_stream.h.
+# These values are obtained from components/services/heap_profiling/stream.h.
 stream_signature="\xF6\x10\x3B\x71"
 alloc_packet="\xF6\x10\x3B\x72"
 free_packet="\xF6\x10\x3B\x73"
diff --git a/chrome/profiling/memlog_stream_parser.cc b/components/services/heap_profiling/stream_parser.cc
similarity index 80%
rename from chrome/profiling/memlog_stream_parser.cc
rename to components/services/heap_profiling/stream_parser.cc
index 034a453..d4e63d6 100644
--- a/chrome/profiling/memlog_stream_parser.cc
+++ b/components/services/heap_profiling/stream_parser.cc
@@ -2,36 +2,35 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/memlog_stream_parser.h"
+#include "components/services/heap_profiling/stream_parser.h"
 
 #include <algorithm>
 
 #include "base/containers/stack_container.h"
 #include "base/strings/stringprintf.h"
-#include "chrome/profiling/address.h"
-#include "chrome/profiling/backtrace.h"
+#include "components/services/heap_profiling/address.h"
+#include "components/services/heap_profiling/backtrace.h"
 #include "components/services/heap_profiling/public/cpp/stream.h"
 
 namespace profiling {
 
-MemlogStreamParser::Block::Block(std::unique_ptr<char[]> d, size_t s)
+StreamParser::Block::Block(std::unique_ptr<char[]> d, size_t s)
     : data(std::move(d)), size(s) {}
 
-MemlogStreamParser::Block::Block(Block&& other) noexcept = default;
+StreamParser::Block::Block(Block&& other) noexcept = default;
 
-MemlogStreamParser::Block::~Block() = default;
+StreamParser::Block::~Block() = default;
 
-MemlogStreamParser::MemlogStreamParser(MemlogReceiver* receiver)
-    : receiver_(receiver) {}
+StreamParser::StreamParser(Receiver* receiver) : receiver_(receiver) {}
 
-MemlogStreamParser::~MemlogStreamParser() {}
+StreamParser::~StreamParser() {}
 
-void MemlogStreamParser::DisconnectReceivers() {
+void StreamParser::DisconnectReceivers() {
   base::AutoLock lock(lock_);
   receiver_ = nullptr;
 }
 
-bool MemlogStreamParser::OnStreamData(std::unique_ptr<char[]> data, size_t sz) {
+bool StreamParser::OnStreamData(std::unique_ptr<char[]> data, size_t sz) {
   base::AutoLock l(lock_);
   if (!receiver_ || error_)
     return false;
@@ -84,13 +83,13 @@
   }
 }
 
-void MemlogStreamParser::OnStreamComplete() {
+void StreamParser::OnStreamComplete() {
   base::AutoLock l(lock_);
   if (receiver_)
     receiver_->OnComplete();
 }
 
-bool MemlogStreamParser::AreBytesAvailable(size_t count) const {
+bool StreamParser::AreBytesAvailable(size_t count) const {
   size_t used = 0;
   size_t current_block_offset = block_zero_offset_;
   for (auto it = blocks_.begin(); it != blocks_.end() && used < count; ++it) {
@@ -100,7 +99,7 @@
   return used >= count;
 }
 
-bool MemlogStreamParser::PeekBytes(size_t count, void* dest) const {
+bool StreamParser::PeekBytes(size_t count, void* dest) const {
   char* dest_char = static_cast<char*>(dest);
   size_t used = 0;
 
@@ -118,14 +117,14 @@
   return used == count;
 }
 
-bool MemlogStreamParser::ReadBytes(size_t count, void* dest) {
+bool StreamParser::ReadBytes(size_t count, void* dest) {
   if (!PeekBytes(count, dest))
     return false;
   ConsumeBytes(count);
   return true;
 }
 
-void MemlogStreamParser::ConsumeBytes(size_t count) {
+void StreamParser::ConsumeBytes(size_t count) {
   DCHECK(AreBytesAvailable(count));
   while (count > 0) {
     size_t bytes_left_in_block = blocks_.front().size - block_zero_offset_;
@@ -142,7 +141,7 @@
   }
 }
 
-MemlogStreamParser::ReadStatus MemlogStreamParser::ParseHeader() {
+StreamParser::ReadStatus StreamParser::ParseHeader() {
   StreamHeader header;
   if (!ReadBytes(sizeof(StreamHeader), &header))
     return READ_NO_DATA;
@@ -155,7 +154,7 @@
   return READ_OK;
 }
 
-MemlogStreamParser::ReadStatus MemlogStreamParser::ParseAlloc() {
+StreamParser::ReadStatus StreamParser::ParseAlloc() {
   // Read the packet. Can't commit the read until the stack is read and
   // that has to be done below.
   AllocPacket alloc_packet;
@@ -194,7 +193,7 @@
   return READ_OK;
 }
 
-MemlogStreamParser::ReadStatus MemlogStreamParser::ParseFree() {
+StreamParser::ReadStatus StreamParser::ParseFree() {
   FreePacket free_packet;
   if (!ReadBytes(sizeof(FreePacket), &free_packet))
     return READ_NO_DATA;
@@ -203,7 +202,7 @@
   return READ_OK;
 }
 
-MemlogStreamParser::ReadStatus MemlogStreamParser::ParseBarrier() {
+StreamParser::ReadStatus StreamParser::ParseBarrier() {
   BarrierPacket barrier_packet;
   if (!ReadBytes(sizeof(BarrierPacket), &barrier_packet))
     return READ_NO_DATA;
@@ -212,7 +211,7 @@
   return READ_OK;
 }
 
-MemlogStreamParser::ReadStatus MemlogStreamParser::ParseStringMapping() {
+StreamParser::ReadStatus StreamParser::ParseStringMapping() {
   StringMappingPacket string_mapping_packet;
   if (!PeekBytes(sizeof(StringMappingPacket), &string_mapping_packet))
     return READ_NO_DATA;
@@ -235,8 +234,8 @@
   return READ_OK;
 }
 
-void MemlogStreamParser::SetErrorState() {
-  LOG(ERROR) << "MemlogStreamParser parsing error";
+void StreamParser::SetErrorState() {
+  LOG(ERROR) << "StreamParser parsing error";
   error_ = true;
   receiver_->OnComplete();
 }
diff --git a/chrome/profiling/memlog_stream_parser.h b/components/services/heap_profiling/stream_parser.h
similarity index 82%
rename from chrome/profiling/memlog_stream_parser.h
rename to components/services/heap_profiling/stream_parser.h
index 12137982..bd27260 100644
--- a/chrome/profiling/memlog_stream_parser.h
+++ b/components/services/heap_profiling/stream_parser.h
@@ -2,24 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_MEMLOG_STREAM_PARSER_H_
-#define CHROME_PROFILING_MEMLOG_STREAM_PARSER_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_STREAM_PARSER_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_STREAM_PARSER_H_
 
 #include "base/callback.h"
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
-#include "chrome/profiling/memlog_receiver.h"
-#include "chrome/profiling/memlog_stream_receiver.h"
+#include "components/services/heap_profiling/receiver.h"
+#include "components/services/heap_profiling/stream_receiver.h"
 
 namespace profiling {
 
 // Parses a memory stream. Refcounted via StreamReceiver.
-class MemlogStreamParser : public MemlogStreamReceiver {
+class StreamParser : public StreamReceiver {
  public:
   // Both receivers must either outlive this class or live until
   // DisconnectReceivers is called.
-  explicit MemlogStreamParser(MemlogReceiver* receiver);
+  explicit StreamParser(Receiver* receiver);
 
   // For tear-down, resets both receivers so they will not be called.
   void DisconnectReceivers();
@@ -49,7 +49,7 @@
     READ_NO_DATA  // Not enough data, try again when we get more
   };
 
-  ~MemlogStreamParser() override;
+  ~StreamParser() override;
 
   // Returns true if the given number of bytes are available now.
   bool AreBytesAvailable(size_t count) const;
@@ -69,7 +69,7 @@
   void SetErrorState();
 
   // Not owned by this class.
-  MemlogReceiver* receiver_;
+  Receiver* receiver_;
 
   base::circular_deque<Block> blocks_;
 
@@ -84,9 +84,9 @@
   // the memory dumper.
   base::Lock lock_;
 
-  DISALLOW_COPY_AND_ASSIGN(MemlogStreamParser);
+  DISALLOW_COPY_AND_ASSIGN(StreamParser);
 };
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_MEMLOG_STREAM_PARSER_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_STREAM_PARSER_H_
diff --git a/chrome/profiling/memlog_stream_parser_unittest.cc b/components/services/heap_profiling/stream_parser_unittest.cc
similarity index 85%
rename from chrome/profiling/memlog_stream_parser_unittest.cc
rename to components/services/heap_profiling/stream_parser_unittest.cc
index 255ac63..57330c5 100644
--- a/chrome/profiling/memlog_stream_parser_unittest.cc
+++ b/components/services/heap_profiling/stream_parser_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/profiling/memlog_stream_parser.h"
+#include "components/services/heap_profiling/stream_parser.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -10,7 +10,7 @@
 
 namespace {
 
-void SendData(const scoped_refptr<MemlogStreamParser>& parser,
+void SendData(const scoped_refptr<StreamParser>& parser,
               const void* data,
               size_t size) {
   std::unique_ptr<char[]> heap(new char[size]);
@@ -18,12 +18,12 @@
   parser->OnStreamData(std::move(heap), size);
 }
 
-void SendHeader(scoped_refptr<MemlogStreamParser>& parser) {
+void SendHeader(scoped_refptr<StreamParser>& parser) {
   StreamHeader header;
   SendData(parser, &header, sizeof(StreamHeader));
 }
 
-class TestReceiver : public MemlogReceiver {
+class TestReceiver : public Receiver {
  public:
   TestReceiver() {
     // Make our saved header invalid so we can't confuse the locally
@@ -113,9 +113,9 @@
 
 }  // namespace
 
-TEST(MemlogStreamParser, NormalHeader) {
+TEST(StreamParser, NormalHeader) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
 
   // Should work to send in two packets.
   StreamHeader header;
@@ -131,9 +131,9 @@
   EXPECT_TRUE(receiver.got_complete());
 }
 
-TEST(MemlogStreamParser, BadHeader) {
+TEST(StreamParser, BadHeader) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
 
   StreamHeader header;
   header.signature = 15;
@@ -143,9 +143,9 @@
   EXPECT_TRUE(parser->has_error());
 }
 
-TEST(MemlogStreamParser, GoodAlloc) {
+TEST(StreamParser, GoodAlloc) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
   SendHeader(parser);
 
   constexpr size_t kStackSize = 4;
@@ -180,9 +180,9 @@
   EXPECT_EQ(stack[3], receiver.last_alloc_stack()[3].value);
 }
 
-TEST(MemlogStreamParser, AllocBigStack) {
+TEST(StreamParser, AllocBigStack) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
   SendHeader(parser);
 
   AllocPacket alloc;
@@ -200,9 +200,9 @@
   EXPECT_TRUE(receiver.got_complete());
 }
 
-TEST(MemlogStreamParser, AllocBigContext) {
+TEST(StreamParser, AllocBigContext) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
   SendHeader(parser);
 
   AllocPacket alloc;
@@ -220,9 +220,9 @@
   EXPECT_TRUE(receiver.got_complete());
 }
 
-TEST(MemlogStreamParser, GoodFree) {
+TEST(StreamParser, GoodFree) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
   SendHeader(parser);
 
   FreePacket fr;
@@ -234,9 +234,9 @@
   EXPECT_EQ(fr.address, receiver.last_free().address);
 }
 
-TEST(MemlogStreamParser, Barrier) {
+TEST(StreamParser, Barrier) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
   SendHeader(parser);
 
   constexpr uint32_t barrier_id = 0x12345678;
@@ -250,9 +250,9 @@
   EXPECT_EQ(barrier_id, receiver.last_barrier().barrier_id);
 }
 
-TEST(MemlogStreamParser, StringMapping) {
+TEST(StreamParser, StringMapping) {
   TestReceiver receiver;
-  scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
+  scoped_refptr<StreamParser> parser(new StreamParser(&receiver));
   SendHeader(parser);
 
   const std::string kDummyText = "kDummyText";
diff --git a/chrome/profiling/memlog_stream_receiver.h b/components/services/heap_profiling/stream_receiver.h
similarity index 67%
rename from chrome/profiling/memlog_stream_receiver.h
rename to components/services/heap_profiling/stream_receiver.h
index daedd5e..c1d0e1fe 100644
--- a/chrome/profiling/memlog_stream_receiver.h
+++ b/components/services/heap_profiling/stream_receiver.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_PROFILING_MEMLOG_STREAM_RECEIVER_H_
-#define CHROME_PROFILING_MEMLOG_STREAM_RECEIVER_H_
+#ifndef COMPONENTS_SERVICES_HEAP_PROFILING_STREAM_RECEIVER_H_
+#define COMPONENTS_SERVICES_HEAP_PROFILING_STREAM_RECEIVER_H_
 
 #include <memory>
 
@@ -13,10 +13,9 @@
 namespace profiling {
 
 // A stream receiver is a sink for unparsed bytes. See also LogReceiver.
-class MemlogStreamReceiver
-    : public base::RefCountedThreadSafe<MemlogStreamReceiver> {
+class StreamReceiver : public base::RefCountedThreadSafe<StreamReceiver> {
  public:
-  MemlogStreamReceiver() {}
+  StreamReceiver() {}
 
   // Returns true on success, false on unrecoverable error. The implementation
   // should be able to handle calls after an error has been reported (some
@@ -27,10 +26,10 @@
   virtual void OnStreamComplete() = 0;
 
  protected:
-  friend class base::RefCountedThreadSafe<MemlogStreamReceiver>;
-  virtual ~MemlogStreamReceiver() {}
+  friend class base::RefCountedThreadSafe<StreamReceiver>;
+  virtual ~StreamReceiver() {}
 };
 
 }  // namespace profiling
 
-#endif  // CHROME_PROFILING_MEMLOG_STREAM_RECEIVER_H_
+#endif  // COMPONENTS_SERVICES_HEAP_PROFILING_STREAM_RECEIVER_H_
diff --git a/components/sessions/core/session_id.h b/components/sessions/core/session_id.h
index f31f206..fbe9fee 100644
--- a/components/sessions/core/session_id.h
+++ b/components/sessions/core/session_id.h
@@ -46,6 +46,10 @@
   // Sets underlying type (deprecated: use FromSerializedValue() instead).
   void set_id(id_type id) { id_ = id; }
 
+  struct Hasher {
+    inline std::size_t operator()(SessionID id) const { return id.id(); }
+  };
+
  private:
   explicit constexpr SessionID(id_type id) : id_(id) {}
 
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index e65dd5f..10e60ec 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -561,6 +561,7 @@
     "//components/version_info",
     "//crypto",
     "//google_apis",
+    "//services/identity/public/cpp",
     "//sql",
     "//third_party/cacheinvalidation",
     "//third_party/crc32c",
diff --git a/components/sync/driver/DEPS b/components/sync/driver/DEPS
index 72a2c53..296536a 100644
--- a/components/sync/driver/DEPS
+++ b/components/sync/driver/DEPS
@@ -19,4 +19,5 @@
   "+google/cacheinvalidation",
   "+net",
   "+policy",
+  "+services/identity/public/cpp",
 ]
diff --git a/components/sync/driver/signin_manager_wrapper.cc b/components/sync/driver/signin_manager_wrapper.cc
index bf96127..d1d02e0 100644
--- a/components/sync/driver/signin_manager_wrapper.cc
+++ b/components/sync/driver/signin_manager_wrapper.cc
@@ -7,21 +7,27 @@
 #include "components/signin/core/browser/signin_manager_base.h"
 #include "google_apis/gaia/gaia_constants.h"
 
-SigninManagerWrapper::SigninManagerWrapper(SigninManagerBase* original)
-    : original_(original) {}
+SigninManagerWrapper::SigninManagerWrapper(
+    identity::IdentityManager* identity_manager,
+    SigninManagerBase* signin_manager)
+    : identity_manager_(identity_manager), signin_manager_(signin_manager) {}
 
 SigninManagerWrapper::~SigninManagerWrapper() {}
 
-SigninManagerBase* SigninManagerWrapper::GetOriginal() {
-  return original_;
+identity::IdentityManager* SigninManagerWrapper::GetIdentityManager() {
+  return identity_manager_;
+}
+
+SigninManagerBase* SigninManagerWrapper::GetSigninManager() {
+  return signin_manager_;
 }
 
 std::string SigninManagerWrapper::GetEffectiveUsername() const {
-  return original_->GetAuthenticatedAccountInfo().email;
+  return signin_manager_->GetAuthenticatedAccountInfo().email;
 }
 
 std::string SigninManagerWrapper::GetAccountIdToUse() const {
-  return original_->GetAuthenticatedAccountId();
+  return signin_manager_->GetAuthenticatedAccountId();
 }
 
 std::string SigninManagerWrapper::GetSyncScopeToUse() const {
diff --git a/components/sync/driver/signin_manager_wrapper.h b/components/sync/driver/signin_manager_wrapper.h
index cb1398d..43e22486 100644
--- a/components/sync/driver/signin_manager_wrapper.h
+++ b/components/sync/driver/signin_manager_wrapper.h
@@ -11,13 +11,18 @@
 
 class SigninManagerBase;
 
+namespace identity {
+class IdentityManager;
+}
+
 // Wraps SigninManager so subclasses can support different ways of getting
 // account information if necessary. Currently exists for supervised users;
 // the subclass SupervisedUserSigninManagerWrapper may be merged back into
 // this class once supervised users are componentized.
 class SigninManagerWrapper {
  public:
-  explicit SigninManagerWrapper(SigninManagerBase* original);
+  explicit SigninManagerWrapper(identity::IdentityManager* identity_manager,
+                                SigninManagerBase* signin_manager);
   virtual ~SigninManagerWrapper();
 
   // Get the email address to use for this account.
@@ -29,11 +34,15 @@
   // Get the OAuth2 scope to use for this account.
   virtual std::string GetSyncScopeToUse() const;
 
+  // Return the original IdentityManager object that was passed in.
+  identity::IdentityManager* GetIdentityManager();
+
   // Return the original SigninManagerBase object that was passed in.
-  SigninManagerBase* GetOriginal();
+  SigninManagerBase* GetSigninManager();
 
  private:
-  SigninManagerBase* original_;
+  identity::IdentityManager* identity_manager_;
+  SigninManagerBase* signin_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(SigninManagerWrapper);
 };
diff --git a/components/sync/driver/sync_service_base.cc b/components/sync/driver/sync_service_base.cc
index 511e8fb..acd7836 100644
--- a/components/sync/driver/sync_service_base.cc
+++ b/components/sync/driver/sync_service_base.cc
@@ -85,7 +85,7 @@
 
 AccountInfo SyncServiceBase::GetAuthenticatedAccountInfo() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return signin_ ? signin_->GetOriginal()->GetAuthenticatedAccountInfo()
+  return signin_ ? signin_->GetSigninManager()->GetAuthenticatedAccountInfo()
                  : AccountInfo();
 }
 
diff --git a/components/toolbar/test_toolbar_model.cc b/components/toolbar/test_toolbar_model.cc
index a465092..86ccbeb 100644
--- a/components/toolbar/test_toolbar_model.cc
+++ b/components/toolbar/test_toolbar_model.cc
@@ -19,11 +19,11 @@
 TestToolbarModel::~TestToolbarModel() {}
 
 base::string16 TestToolbarModel::GetFormattedFullURL() const {
-  return text_;
+  return formatted_full_url_;
 }
 
 base::string16 TestToolbarModel::GetURLForDisplay() const {
-  return text_;
+  return url_for_display_;
 }
 
 GURL TestToolbarModel::GetURL() const {
diff --git a/components/toolbar/test_toolbar_model.h b/components/toolbar/test_toolbar_model.h
index 639332d..e7be4534 100644
--- a/components/toolbar/test_toolbar_model.h
+++ b/components/toolbar/test_toolbar_model.h
@@ -34,7 +34,12 @@
   bool ShouldDisplayURL() const override;
   bool IsOfflinePage() const override;
 
-  void set_text(const base::string16& text) { text_ = text; }
+  void set_formatted_full_url(const base::string16& url) {
+    formatted_full_url_ = url;
+  }
+  void set_url_for_display(const base::string16& url) {
+    url_for_display_ = url;
+  }
   void set_url(const GURL& url) { url_ = url; }
   void set_security_level(security_state::SecurityLevel security_level) {
     security_level_ = security_level;
@@ -49,7 +54,8 @@
   void set_offline_page(bool offline_page) { offline_page_ = offline_page; }
 
  private:
-  base::string16 text_;
+  base::string16 formatted_full_url_;
+  base::string16 url_for_display_;
   GURL url_;
   security_state::SecurityLevel security_level_ = security_state::NONE;
   const gfx::VectorIcon* icon_ = nullptr;
diff --git a/components/ukm/ukm_service.cc b/components/ukm/ukm_service.cc
index 31bb4a2f..2368c4b8 100644
--- a/components/ukm/ukm_service.cc
+++ b/components/ukm/ukm_service.cc
@@ -60,8 +60,10 @@
 }  // namespace
 
 UkmService::UkmService(PrefService* pref_service,
-                       metrics::MetricsServiceClient* client)
+                       metrics::MetricsServiceClient* client,
+                       bool restrict_to_whitelist_entries)
     : pref_service_(pref_service),
+      restrict_to_whitelist_entries_(restrict_to_whitelist_entries),
       client_id_(0),
       session_id_(0),
       report_count_(0),
@@ -251,4 +253,8 @@
   reporting_service_.ukm_log_store()->StoreLog(serialized_log);
 }
 
+bool UkmService::ShouldRestrictToWhitelistedEntries() const {
+  return restrict_to_whitelist_entries_;
+}
+
 }  // namespace ukm
diff --git a/components/ukm/ukm_service.h b/components/ukm/ukm_service.h
index 4c5b6807..0beed526 100644
--- a/components/ukm/ukm_service.h
+++ b/components/ukm/ukm_service.h
@@ -42,7 +42,9 @@
   // Constructs a UkmService.
   // Calling code is responsible for ensuring that the lifetime of
   // |pref_service| is longer than the lifetime of UkmService.
-  UkmService(PrefService* pref_service, metrics::MetricsServiceClient* client);
+  UkmService(PrefService* pref_service,
+             metrics::MetricsServiceClient* client,
+             bool restrict_to_whitelist_entries);
   ~UkmService() override;
 
   // Initializes the UKM service.
@@ -111,9 +113,15 @@
   // Called by log_uploader_ when the an upload is completed.
   void OnLogUploadComplete(int response_code);
 
+  // ukm::UkmRecorderImpl:
+  bool ShouldRestrictToWhitelistedEntries() const override;
+
   // A weak pointer to the PrefService used to read and write preferences.
   PrefService* pref_service_;
 
+  // If true, only whitelisted Entries should be recorded.
+  bool restrict_to_whitelist_entries_;
+
   // The UKM client id stored in prefs.
   uint64_t client_id_;
 
diff --git a/components/ukm/ukm_service_unittest.cc b/components/ukm/ukm_service_unittest.cc
index f5efded..1acb64a 100644
--- a/components/ukm/ukm_service_unittest.cc
+++ b/components/ukm/ukm_service_unittest.cc
@@ -171,7 +171,8 @@
 }  // namespace
 
 TEST_F(UkmServiceTest, EnableDisableSchedule) {
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   EXPECT_FALSE(task_runner_->HasPendingTask());
   service.Initialize();
   EXPECT_FALSE(task_runner_->HasPendingTask());
@@ -188,7 +189,8 @@
   ScopedUkmFeatureParams params(base::FeatureList::OVERRIDE_ENABLE_FEATURE,
                                 {{"WhitelistEntries", "PageLoad"}});
 
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(GetPersistedLogCount(), 0);
   service.Initialize();
@@ -214,7 +216,8 @@
 }
 
 TEST_F(UkmServiceTest, SourceSerialization) {
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(GetPersistedLogCount(), 0);
   service.Initialize();
@@ -245,7 +248,8 @@
   ScopedUkmFeatureParams params(base::FeatureList::OVERRIDE_ENABLE_FEATURE,
                                 {{"WhitelistEntries", "foo,bar"}});
 
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(0, GetPersistedLogCount());
   service.Initialize();
@@ -313,7 +317,8 @@
   ScopedUkmFeatureParams params(base::FeatureList::OVERRIDE_ENABLE_FEATURE,
                                 {{"WhitelistEntries", "PageLoad"}});
 
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   ASSERT_EQ(0, GetPersistedLogCount());
   service.Initialize();
@@ -336,7 +341,8 @@
   ScopedUkmFeatureParams params(base::FeatureList::OVERRIDE_ENABLE_FEATURE,
                                 {{"WhitelistEntries", "PageLoad"}});
 
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
 
   metrics::TestMetricsProvider* provider = new metrics::TestMetricsProvider();
@@ -371,7 +377,8 @@
 }
 
 TEST_F(UkmServiceTest, LogsRotation) {
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(GetPersistedLogCount(), 0);
   service.Initialize();
@@ -416,7 +423,8 @@
   ScopedUkmFeatureParams params(base::FeatureList::OVERRIDE_ENABLE_FEATURE,
                                 {{"WhitelistEntries", "PageLoad"}});
 
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(GetPersistedLogCount(), 0);
   service.Initialize();
@@ -477,7 +485,8 @@
         {{"RecordInitialUrl", should_record_initial_url ? "true" : "false"}});
 
     ClearPrefs();
-    UkmService service(&prefs_, &client_);
+    UkmService service(&prefs_, &client_,
+                       true /* restrict_to_whitelisted_entries */);
     TestRecordingHelper recorder(&service);
     EXPECT_EQ(GetPersistedLogCount(), 0);
     service.Initialize();
@@ -518,7 +527,8 @@
          {"WhitelistEntries", "FakeEntry"}});
 
     ClearPrefs();
-    UkmService service(&prefs_, &client_);
+    UkmService service(&prefs_, &client_,
+                       true /* restrict_to_whitelisted_entries */);
     TestRecordingHelper recorder(&service);
     EXPECT_EQ(GetPersistedLogCount(), 0);
     service.Initialize();
@@ -560,7 +570,8 @@
 
 TEST_F(UkmServiceTest, RecordSessionId) {
   ClearPrefs();
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(0, GetPersistedLogCount());
   service.Initialize();
@@ -586,7 +597,8 @@
                                 {{"MaxSources", "2"}});
 
   ClearPrefs();
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(0, GetPersistedLogCount());
   service.Initialize();
@@ -611,7 +623,8 @@
 }
 
 TEST_F(UkmServiceTest, PurgeMidUpload) {
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(GetPersistedLogCount(), 0);
   service.Initialize();
@@ -638,7 +651,8 @@
                                 {{"WhitelistEntries", "EntryA,EntryB"}});
 
   ClearPrefs();
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(0, GetPersistedLogCount());
   service.Initialize();
@@ -684,7 +698,8 @@
 }
 
 TEST_F(UkmServiceTest, SourceURLLength) {
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(0, GetPersistedLogCount());
   service.Initialize();
@@ -720,7 +735,8 @@
           restrict_to_whitelisted_source_ids ? "true" : "false"}});
 
     ClearPrefs();
-    UkmService service(&prefs_, &client_);
+    UkmService service(&prefs_, &client_,
+                       true /* restrict_to_whitelisted_entries */);
     TestRecordingHelper recorder(&service);
     EXPECT_EQ(0, GetPersistedLogCount());
     service.Initialize();
@@ -838,7 +854,8 @@
 
   for (const auto& test : test_cases) {
     ClearPrefs();
-    UkmService service(&prefs_, &client_);
+    UkmService service(&prefs_, &client_,
+                       true /* restrict_to_whitelisted_entries */);
     TestRecordingHelper recorder(&service);
 
     ASSERT_EQ(GetPersistedLogCount(), 0);
@@ -923,7 +940,8 @@
 
   for (const auto& test : test_cases) {
     ClearPrefs();
-    UkmService service(&prefs_, &client_);
+    UkmService service(&prefs_, &client_,
+                       true /* restrict_to_whitelisted_entries */);
     TestRecordingHelper recorder(&service);
 
     EXPECT_EQ(GetPersistedLogCount(), 0);
@@ -1014,7 +1032,8 @@
 
   base::FieldTrialList field_trial_list(nullptr /* entropy_provider */);
   ScopedUkmFeatureParams params(base::FeatureList::OVERRIDE_ENABLE_FEATURE, {});
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   service.SetIsWebstoreExtensionCallback(
       base::BindRepeating(&TestIsWebstoreExtension));
@@ -1071,7 +1090,8 @@
 
   base::FieldTrialList field_trial_list(nullptr /* entropy_provider */);
   ScopedUkmFeatureParams params(base::FeatureList::OVERRIDE_ENABLE_FEATURE, {});
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
 
   EXPECT_EQ(GetPersistedLogCount(), 0);
@@ -1108,7 +1128,8 @@
 }
 
 TEST_F(UkmServiceTest, SanitizeUrlAuthParams) {
-  UkmService service(&prefs_, &client_);
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */);
   TestRecordingHelper recorder(&service);
   EXPECT_EQ(0, GetPersistedLogCount());
   service.Initialize();
@@ -1146,7 +1167,8 @@
   for (const auto& test : test_cases) {
     ClearPrefs();
 
-    UkmService service(&prefs_, &client_);
+    UkmService service(&prefs_, &client_,
+                       true /* restrict_to_whitelisted_entries */);
     TestRecordingHelper recorder(&service);
     service.SetIsWebstoreExtensionCallback(
         base::BindRepeating(&TestIsWebstoreExtension));
diff --git a/components/webrtc_logging/browser/log_cleanup.cc b/components/webrtc_logging/browser/log_cleanup.cc
index 2f3fc805..184357d 100644
--- a/components/webrtc_logging/browser/log_cleanup.cc
+++ b/components/webrtc_logging/browser/log_cleanup.cc
@@ -25,16 +25,21 @@
 // Remove any empty entries from the log list. One line is one log entry, see
 // WebRtcLogUploader::AddLocallyStoredLogInfoToUploadListFile for more
 // information about the format.
-void RemoveEmptyEntriesInLogList(std::string* log_list) {
-  static const char kEmptyLine[] = ",,\n";
+void RemoveEmptyEntriesFromLogList(std::string* log_list) {
+  // TODO(crbug.com/826253): Make this more robust to errors; corrupt entries
+  // should also be removed. (Better to move away from a .csv altogether.)
+  static const char kEmptyLineStart[] = ",,,";  // And a timestamp after it.
   size_t pos = 0;
   do {
-    pos = log_list->find(kEmptyLine, pos);
+    pos = log_list->find(kEmptyLineStart, pos);
     if (pos == std::string::npos)
       break;
-    DCHECK(pos == 0 || (*log_list)[pos - 1] == '\n');
-    log_list->erase(pos, arraysize(kEmptyLine) - 1);
-  } while (true);
+    const size_t line_end = log_list->find("\n", pos);
+    DCHECK(line_end == std::string::npos || pos < line_end);
+    const size_t delete_len =
+        line_end == std::string::npos ? std::string::npos : line_end - pos + 1;
+    log_list->erase(pos, delete_len);
+  } while (pos < log_list->size());
 }
 
 }  // namespace
@@ -62,10 +67,18 @@
   std::string log_list;
   const bool update_log_list = base::PathExists(log_list_path);
   if (update_log_list) {
-    bool read_ok = base::ReadFileToString(log_list_path, &log_list);
-    DPCHECK(read_ok);
+    constexpr size_t kMaxIndexSizeBytes = 1000000;  // Intentional overshot.
+    const bool read_ok = base::ReadFileToStringWithMaxSize(
+        log_list_path, &log_list, kMaxIndexSizeBytes);
+    if (!read_ok) {
+      // If the maximum size was exceeded, updating it will corrupt it. However,
+      // the size would not be exceeded unless the user edits it manually.
+      LOG(ERROR) << "Couldn't read WebRTC textual logs list (" << log_list_path
+                 << ").";
+    }
   }
 
+  // Delete relevant logs files (and their associated entries in the index).
   base::FileEnumerator log_files(log_dir, false, base::FileEnumerator::FILES);
   bool delete_ok = true;
   for (base::FilePath name = log_files.Next(); !name.empty();
@@ -73,6 +86,8 @@
     if (name == log_list_path)
       continue;
     base::FileEnumerator::FileInfo file_info(log_files.GetInfo());
+    // TODO(crbug.com/827167): Handle mismatch between timestamps of the .gz
+    // file and the .meta file, as well as with the index.
     base::TimeDelta file_age = now - file_info.GetLastModifiedTime();
     if (file_age > time_to_keep_logs ||
         (!delete_begin_time.is_max() &&
@@ -93,7 +108,11 @@
   if (!delete_ok)
     LOG(WARNING) << "Could not delete all old WebRTC logs.";
 
-  RemoveEmptyEntriesInLogList(&log_list);
+  // TODO(crbug.com/826254): Purge index file separately, too, to ensure
+  // entries for logs whose files were manually removed, are also subject
+  // to expiry and browsing data clearing.
+
+  RemoveEmptyEntriesFromLogList(&log_list);
 
   if (update_log_list) {
     int written = base::WriteFile(log_list_path, &log_list[0], log_list.size());
diff --git a/components/webrtc_logging/browser/log_cleanup.h b/components/webrtc_logging/browser/log_cleanup.h
index dc8fdd9..704d9d5 100644
--- a/components/webrtc_logging/browser/log_cleanup.h
+++ b/components/webrtc_logging/browser/log_cleanup.h
@@ -12,14 +12,19 @@
 
 namespace webrtc_logging {
 
-// Deletes logs files older that 5 days. Updates the log file list. Must be
-// called on the FILE thread.
+// Deletes logs files older that 5 days. Updates the log file list.
+// Must be called on a task runner that's allowed to block.
+// TODO(crbug.com/826221): Only call on the same task runner as where writing
+// is done.
 void DeleteOldWebRtcLogFiles(const base::FilePath& log_dir);
 
 // Deletes logs files older that 5 days and logs younger than
 // |delete_begin_time|. Updates the log file list. If |delete_begin_time| is
 // base::time::Max(), no recent logs will be deleted, and the function is
-// equal to DeleteOldWebRtcLogFiles(). Must be called on the FILE thread.
+// equal to DeleteOldWebRtcLogFiles().
+// Must be called on a task runner that's allowed to block.
+// TODO(crbug.com/826221): Only call on the same task runner as where writing
+// is done.
 void DeleteOldAndRecentWebRtcLogFiles(const base::FilePath& log_dir,
                                       const base::Time& delete_begin_time);
 
diff --git a/components/webrtc_logging/browser/log_cleanup_unittest.cc b/components/webrtc_logging/browser/log_cleanup_unittest.cc
index 5d6ec15..1de8bdd 100644
--- a/components/webrtc_logging/browser/log_cleanup_unittest.cc
+++ b/components/webrtc_logging/browser/log_cleanup_unittest.cc
@@ -73,4 +73,6 @@
   VerifyFiles(1);
 }
 
+// TODO(crbug.com/826251): Write tests for the index file.
+
 }  // namespace webrtc_logging
diff --git a/content/browser/blob_storage/blob_dispatcher_host.cc b/content/browser/blob_storage/blob_dispatcher_host.cc
index 4b81c8b..9de9c3f 100644
--- a/content/browser/blob_storage/blob_dispatcher_host.cc
+++ b/content/browser/blob_storage/blob_dispatcher_host.cc
@@ -90,25 +90,6 @@
   public_blob_urls_.insert(public_url);
 }
 
-namespace {
-
-void RevokePublicBlobURLHelperIO(
-    scoped_refptr<ChromeBlobStorageContext> context,
-    const GURL& public_url) {
-  context->context()->RevokePublicBlobURL(public_url);
-}
-
-void RevokePublicBlobURLHelperUI(
-    scoped_refptr<ChromeBlobStorageContext> context,
-    const GURL& public_url) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                          base::BindOnce(&RevokePublicBlobURLHelperIO,
-                                         std::move(context), public_url));
-}
-
-}  // namespace
-
 void BlobDispatcherHost::OnRevokePublicBlobURL(const GURL& public_url) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!public_url.is_valid()) {
@@ -123,10 +104,8 @@
                               BDH_TRACING_ENUM_LAST);
     return;
   }
+  context()->RevokePublicBlobURL(public_url);
   public_blob_urls_.erase(public_url);
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::BindOnce(&RevokePublicBlobURLHelperUI,
-                                         blob_storage_context_, public_url));
 }
 
 storage::BlobStorageContext* BlobDispatcherHost::context() {
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 3379b8a..bf9f57f 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -262,6 +262,7 @@
       *base::CommandLine::ForCurrentProcess();
   static const char* const kForwardSwitches[] = {
       service_manager::switches::kDisableInProcessStackTraces,
+      switches::kDisableBackgroundTasks,
       switches::kDisableLogging,
       switches::kEnableLogging,
       switches::kIPCConnectionTimeout,
diff --git a/content/browser/devtools/browser_devtools_agent_host.cc b/content/browser/devtools/browser_devtools_agent_host.cc
index ea9984b..bd0c470 100644
--- a/content/browser/devtools/browser_devtools_agent_host.cc
+++ b/content/browser/devtools/browser_devtools_agent_host.cc
@@ -66,10 +66,8 @@
   session->AddHandler(base::WrapUnique(new protocol::SystemInfoHandler()));
   session->AddHandler(base::WrapUnique(new protocol::TetheringHandler(
       socket_callback_, tethering_task_runner_)));
-  session->AddHandler(base::WrapUnique(new protocol::TracingHandler(
-      protocol::TracingHandler::Browser,
-      FrameTreeNode::kFrameTreeNodeInvalidId,
-      GetIOContext())));
+  session->AddHandler(
+      base::WrapUnique(new protocol::TracingHandler(nullptr, GetIOContext())));
   return true;
 }
 
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index 96336dc8..f1d2104 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -28,6 +28,9 @@
 #include "content/browser/devtools/devtools_frame_trace_recorder_for_viz.h"
 #include "content/browser/devtools/devtools_io_context.h"
 #include "content/browser/devtools/devtools_session.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/tracing/tracing_controller_impl.h"
@@ -144,15 +147,56 @@
   base::WeakPtr<TracingHandler> tracing_handler_;
 };
 
+std::string GetProcessHostHex(RenderProcessHost* host) {
+  return base::StringPrintf("0x%" PRIxPTR, reinterpret_cast<uintptr_t>(host));
+}
+
+void SendProcessReadyInBrowserEvent(const base::UnguessableToken& frame_token,
+                                    RenderProcessHost* host) {
+  auto data = std::make_unique<base::trace_event::TracedValue>();
+  data->SetString("frame", frame_token.ToString());
+  data->SetString("processPseudoId", GetProcessHostHex(host));
+  data->SetInteger("processId",
+                   static_cast<int>(base::GetProcId(host->GetHandle())));
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
+                       "ProcessReadyInBrowser", TRACE_EVENT_SCOPE_THREAD,
+                       "data", std::move(data));
+}
+
+void FillFrameData(base::trace_event::TracedValue* data,
+                   FrameTreeNode* node,
+                   RenderFrameHostImpl* frame_host,
+                   const GURL& url) {
+  url::Replacements<char> strip_fragment;
+  strip_fragment.ClearRef();
+  data->SetString("frame", node->devtools_frame_token().ToString());
+  data->SetString("url", url.ReplaceComponents(strip_fragment).spec());
+  data->SetString("name", node->frame_name());
+  if (node->parent())
+    data->SetString("parent",
+                    node->parent()->devtools_frame_token().ToString());
+  if (frame_host) {
+    RenderProcessHost* process_host = frame_host->GetProcess();
+    base::ProcessId process_id = base::GetProcId(process_host->GetHandle());
+    if (process_id == base::kNullProcessId) {
+      data->SetString("processPseudoId", GetProcessHostHex(process_host));
+      frame_host->GetProcess()->PostTaskWhenProcessIsReady(
+          base::BindOnce(&SendProcessReadyInBrowserEvent,
+                         node->devtools_frame_token(), process_host));
+    } else {
+      // Cast process id to int to be compatible with tracing.
+      data->SetInteger("processId", static_cast<int>(process_id));
+    }
+  }
+}
+
 }  // namespace
 
-TracingHandler::TracingHandler(TracingHandler::Target target,
-                               int frame_tree_node_id,
+TracingHandler::TracingHandler(FrameTreeNode* frame_tree_node_,
                                DevToolsIOContext* io_context)
     : DevToolsDomainHandler(Tracing::Metainfo::domainName),
-      target_(target),
       io_context_(io_context),
-      frame_tree_node_id_(frame_tree_node_id),
+      frame_tree_node_(frame_tree_node_),
       did_initiate_recording_(false),
       return_as_stream_(false),
       gzip_compression_(false),
@@ -343,7 +387,7 @@
 
   // If inspected target is a render process Tracing.start will be handled by
   // tracing agent in the renderer.
-  if (target_ == Renderer)
+  if (frame_tree_node_)
     callback->fallThrough();
 
   TracingController::GetInstance()->StartTracing(
@@ -380,7 +424,7 @@
   }
   // If inspected target is a render process Tracing.end will be handled by
   // tracing agent in the renderer.
-  if (target_ == Renderer)
+  if (frame_tree_node_)
     callback->fallThrough();
   else
     callback->sendSuccess();
@@ -396,10 +440,9 @@
 
 void TracingHandler::OnRecordingEnabled(
     std::unique_ptr<StartCallback> callback) {
-  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
-                       "TracingStartedInBrowser", TRACE_EVENT_SCOPE_THREAD,
-                       "frameTreeNodeId", frame_tree_node_id_);
-  if (target_ != Renderer)
+  EmitFrameTree();
+
+  if (!frame_tree_node_)
     callback->sendSuccess();
 
   bool screenshot_enabled;
@@ -489,6 +532,53 @@
   return TracingController::GetInstance()->IsTracing();
 }
 
+void TracingHandler::EmitFrameTree() {
+  auto data = std::make_unique<base::trace_event::TracedValue>();
+  if (frame_tree_node_) {
+    data->SetInteger("frameTreeNodeId", frame_tree_node_->frame_tree_node_id());
+    data->SetBoolean("persistentIds", true);
+    data->BeginArray("frames");
+    FrameTree::NodeRange subtree =
+        frame_tree_node_->frame_tree()->SubtreeNodes(frame_tree_node_);
+    for (FrameTreeNode* node : subtree) {
+      data->BeginDictionary();
+      FillFrameData(data.get(), node, node->current_frame_host(),
+                    node->current_url());
+      data->EndDictionary();
+    }
+    data->EndArray();
+  }
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
+                       "TracingStartedInBrowser", TRACE_EVENT_SCOPE_THREAD,
+                       "data", std::move(data));
+}
+
+void TracingHandler::ReadyToCommitNavigation(
+    NavigationHandleImpl* navigation_handle) {
+  if (!did_initiate_recording_)
+    return;
+  auto data = std::make_unique<base::trace_event::TracedValue>();
+  FillFrameData(data.get(), navigation_handle->frame_tree_node(),
+                navigation_handle->GetRenderFrameHost(),
+                navigation_handle->GetURL());
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
+                       "FrameCommittedInBrowser", TRACE_EVENT_SCOPE_THREAD,
+                       "data", std::move(data));
+}
+
+void TracingHandler::FrameDeleted(RenderFrameHostImpl* frame_host) {
+  if (!did_initiate_recording_)
+    return;
+  auto data = std::make_unique<base::trace_event::TracedValue>();
+  data->SetString(
+      "frame",
+      frame_host->frame_tree_node()->devtools_frame_token().ToString());
+  TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
+                       "FrameDeletedInBrowser", TRACE_EVENT_SCOPE_THREAD,
+                       "data", std::move(data));
+}
+
+// static
 bool TracingHandler::IsStartupTracingActive() {
   return ::tracing::TraceConfigFile::GetInstance()->IsEnabled();
 }
diff --git a/content/browser/devtools/protocol/tracing_handler.h b/content/browser/devtools/protocol/tracing_handler.h
index 8515e3a..8383129 100644
--- a/content/browser/devtools/protocol/tracing_handler.h
+++ b/content/browser/devtools/protocol/tracing_handler.h
@@ -31,14 +31,14 @@
 class DevToolsAgentHostImpl;
 class DevToolsFrameTraceRecorderForViz;
 class DevToolsIOContext;
+class FrameTreeNode;
+class NavigationHandleImpl;
 
 namespace protocol {
 
 class TracingHandler : public DevToolsDomainHandler, public Tracing::Backend {
  public:
-  enum Target { Browser, Renderer };
-  CONTENT_EXPORT TracingHandler(Target target,
-                                int frame_tree_node_id,
+  CONTENT_EXPORT TracingHandler(FrameTreeNode* frame_tree_node,
                                 DevToolsIOContext* io_context);
   CONTENT_EXPORT ~TracingHandler() override;
 
@@ -69,6 +69,8 @@
   Response RecordClockSyncMarker(const std::string& sync_id) override;
 
   bool did_initiate_recording() { return did_initiate_recording_; }
+  void ReadyToCommitNavigation(NavigationHandleImpl* navigation_handle);
+  void FrameDeleted(RenderFrameHostImpl* frame_host);
 
  private:
   friend class TracingHandlerTest;
@@ -102,17 +104,17 @@
       const scoped_refptr<TracingController::TraceDataEndpoint>& endpoint,
       const std::string& agent_label);
   bool IsTracing() const;
+  void EmitFrameTree();
   static bool IsStartupTracingActive();
   CONTENT_EXPORT static base::trace_event::TraceConfig
       GetTraceConfigFromDevToolsConfig(
           const base::DictionaryValue& devtools_config);
 
   std::unique_ptr<base::Timer> buffer_usage_poll_timer_;
-  Target target_;
 
   std::unique_ptr<Tracing::Frontend> frontend_;
   DevToolsIOContext* io_context_;
-  int frame_tree_node_id_;
+  FrameTreeNode* frame_tree_node_;
   bool did_initiate_recording_;
   bool return_as_stream_;
   bool gzip_compression_;
diff --git a/content/browser/devtools/protocol/tracing_handler_unittest.cc b/content/browser/devtools/protocol/tracing_handler_unittest.cc
index 5af1a8d..a3927429 100644
--- a/content/browser/devtools/protocol/tracing_handler_unittest.cc
+++ b/content/browser/devtools/protocol/tracing_handler_unittest.cc
@@ -72,8 +72,7 @@
 class TracingHandlerTest : public testing::Test {
  public:
   void SetUp() override {
-    tracing_handler_.reset(
-        new TracingHandler(TracingHandler::Browser, 0, nullptr));
+    tracing_handler_.reset(new TracingHandler(nullptr, nullptr));
   }
 
   void TearDown() override { tracing_handler_.reset(); }
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index f2171cb3..3fd83a1 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -392,13 +392,13 @@
     session->AddHandler(base::WrapUnique(
         new protocol::TargetHandler(false /* browser_only */)));
   }
-  session->AddHandler(base::WrapUnique(new protocol::TracingHandler(
-      protocol::TracingHandler::Renderer,
-      frame_tree_node_ ? frame_tree_node_->frame_tree_node_id() : 0,
-      GetIOContext())));
   session->AddHandler(
       base::WrapUnique(new protocol::PageHandler(emulation_handler)));
   session->AddHandler(base::WrapUnique(new protocol::SecurityHandler()));
+  if (!frame_tree_node_ || !frame_tree_node_->parent()) {
+    session->AddHandler(base::WrapUnique(
+        new protocol::TracingHandler(frame_tree_node_, GetIOContext())));
+  }
 
   if (EnsureAgent())
     session->AttachToAgent(agent_ptr_);
@@ -466,6 +466,9 @@
     NavigationHandle* navigation_handle) {
   NavigationHandleImpl* handle =
       static_cast<NavigationHandleImpl*>(navigation_handle);
+  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this))
+    tracing->ReadyToCommitNavigation(handle);
+
   if (handle->frame_tree_node() != frame_tree_node_) {
     if (ShouldForceCreation() && handle->GetRenderFrameHost() &&
         handle->GetRenderFrameHost()->IsCrossProcessSubframe()) {
@@ -611,9 +614,12 @@
 }
 
 void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) {
-  if (static_cast<RenderFrameHostImpl*>(rfh)->frame_tree_node() ==
-      frame_tree_node_) {
-    DestroyOnRenderFrameGone();  // |this| may be deleted at this point.
+  RenderFrameHostImpl* host = static_cast<RenderFrameHostImpl*>(rfh);
+  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this))
+    tracing->FrameDeleted(host);
+  if (host->frame_tree_node() == frame_tree_node_) {
+    DestroyOnRenderFrameGone();
+    // |this| may be deleted at this point.
   }
 }
 
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index a0eabb72..4fcc36fb 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -2901,8 +2901,8 @@
   ASSERT_TRUE(server.ShutdownAndWaitUntilComplete());
 }
 
-// A request for a non-existent resource should result in an aborted navigation,
-// and the old site staying current.
+// A request for a non-existent same-origin resource should result in a
+// DownloadItem that's created in an interrupted state.
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeServerError) {
   GURL download_url =
       embedded_test_server()->GetURL("/download/does-not-exist");
@@ -2910,15 +2910,16 @@
       std::string("/download/download-attribute.html?target=") +
       download_url.spec());
 
-  auto observer = std::make_unique<content::TestNavigationObserver>(
-      shell()->web_contents(), 2);
-  NavigateToURL(shell(), document_url);
-  observer->Wait();
-  EXPECT_FALSE(observer->last_navigation_succeeded());
+  download::DownloadItem* download =
+      StartDownloadAndReturnItem(shell(), document_url);
+  WaitForInterrupt(download);
+
+  EXPECT_EQ(download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
+            download->GetLastReason());
 }
 
-// A request that fails before it gets a response from the server should also
-// result in the old page staying current.
+// A cross-origin request that fails before it gets a response from the server
+// should result in the old page staying current.
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeNetworkError) {
   SetupErrorInjectionDownloads();
   GURL url = TestDownloadHttpResponse::GetNextURLForDownload();
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 0c8cec82..6077595 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -39,8 +39,7 @@
       drop_current_tap_ending_event_(false),
       allow_current_double_tap_event_(true),
       force_enable_zoom_(false),
-      allowed_touch_action_(cc::kTouchActionAuto),
-      white_listed_touch_action_(cc::kTouchActionAuto) {}
+      allowed_touch_action_(cc::kTouchActionAuto) {}
 
 bool TouchActionFilter::FilterGestureEvent(WebGestureEvent* gesture_event) {
   if (gesture_event->SourceDevice() != blink::kWebGestureDeviceTouchscreen)
@@ -192,8 +191,11 @@
   // Report how often the effective touch action computed by blink is or is
   // not equivalent to the whitelisted touch action computed by the
   // compositor.
-  UMA_HISTOGRAM_BOOLEAN("TouchAction.EquivalentEffectiveAndWhiteListed",
-                        allowed_touch_action_ == white_listed_touch_action_);
+  if (white_listed_touch_action_.has_value()) {
+    UMA_HISTOGRAM_BOOLEAN(
+        "TouchAction.EquivalentEffectiveAndWhiteListed",
+        allowed_touch_action_ == white_listed_touch_action_.value());
+  }
   ResetTouchAction();
 }
 
@@ -201,22 +203,19 @@
   // Note that resetting the action mid-sequence is tolerated. Gestures that had
   // their begin event(s) suppressed will be suppressed until the next sequence.
   allowed_touch_action_ = cc::kTouchActionAuto;
-  white_listed_touch_action_ = cc::kTouchActionAuto;
+  white_listed_touch_action_.reset();
 }
 
 void TouchActionFilter::OnSetWhiteListedTouchAction(
     cc::TouchAction white_listed_touch_action) {
-  // For multiple fingers, we take the intersection of the touch actions for all
-  // fingers that have gone down during this action.  In the majority of
-  // real-world scenarios the touch action for all fingers will be the same.
-  // This is left as implementation because of the relationship of gestures
-  // (which are off limits for the spec).  We believe the following are
-  // desirable properties of this choice:
-  // 1. Not sensitive to finger touch order.  Behavior of putting two fingers
-  //    down "at once" will be deterministic.
-  // 2. Only subtractive - eg. can't trigger scrolling on an element that
-  //    otherwise has scrolling disabling by the addition of a finger.
-  white_listed_touch_action_ &= white_listed_touch_action;
+  // We use '&' here to account for the multiple-finger case, which is the same
+  // as OnSetTouchAction.
+  if (white_listed_touch_action_.has_value()) {
+    white_listed_touch_action_ =
+        white_listed_touch_action_.value() & white_listed_touch_action;
+  } else {
+    white_listed_touch_action_ = white_listed_touch_action;
+  }
 }
 
 bool TouchActionFilter::ShouldSuppressManipulation(
diff --git a/content/browser/renderer_host/input/touch_action_filter.h b/content/browser/renderer_host/input/touch_action_filter.h
index d2c0061..fd6d5e6 100644
--- a/content/browser/renderer_host/input/touch_action_filter.h
+++ b/content/browser/renderer_host/input/touch_action_filter.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_ACTION_FILTER_H_
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "cc/input/touch_action.h"
 #include "content/common/content_export.h"
 
@@ -80,7 +81,7 @@
   cc::TouchAction allowed_touch_action_;
 
   // Whitelisted touch action received from the compositor.
-  cc::TouchAction white_listed_touch_action_;
+  base::Optional<cc::TouchAction> white_listed_touch_action_;
 
   DISALLOW_COPY_AND_ASSIGN(TouchActionFilter);
 };
diff --git a/content/browser/renderer_host/overscroll_controller.cc b/content/browser/renderer_host/overscroll_controller.cc
index 527809b..6a09f92 100644
--- a/content/browser/renderer_host/overscroll_controller.cc
+++ b/content/browser/renderer_host/overscroll_controller.cc
@@ -85,6 +85,9 @@
 }
 
 bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
+  if (event.GetType() == blink::WebInputEvent::kGestureScrollBegin)
+    ignore_following_inertial_events_ = false;
+
   if (!ShouldProcessEvent(event))
     return false;
 
@@ -99,6 +102,18 @@
     return false;
   }
 
+  // Consume the scroll-update events if they are from a inertial scroll (fling)
+  // event that completed an overscroll gesture.
+  if (ignore_following_inertial_events_ &&
+      event.GetType() == blink::WebInputEvent::kGestureScrollUpdate) {
+    const blink::WebGestureEvent& gesture =
+        static_cast<const blink::WebGestureEvent&>(event);
+    if (gesture.data.scroll_update.inertial_phase ==
+        blink::WebGestureEvent::kMomentumPhase) {
+      return true;
+    }
+  }
+
   bool reset_scroll_state = false;
   if (scroll_state_ != ScrollState::NONE || overscroll_delta_x_ ||
       overscroll_delta_y_) {
@@ -199,7 +214,7 @@
   ResetScrollState();
 }
 
-bool OverscrollController::DispatchEventCompletesAction (
+bool OverscrollController::DispatchEventCompletesAction(
     const blink::WebInputEvent& event) const {
   if (overscroll_mode_ == OVERSCROLL_NONE)
     return false;
@@ -209,9 +224,23 @@
   // after the threshold.
   if (event.GetType() != blink::WebInputEvent::kMouseMove &&
       event.GetType() != blink::WebInputEvent::kGestureScrollEnd &&
-      event.GetType() != blink::WebInputEvent::kGestureFlingStart)
+      event.GetType() != blink::WebInputEvent::kGestureFlingStart &&
+      event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
     return false;
 
+  // Complete the overscroll gesture for inertial scroll (fling) event from
+  // touchpad.
+  if (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate) {
+    if (overscroll_source_ != OverscrollSource::TOUCHPAD)
+      return false;
+    DCHECK(IsGestureEventFromTouchpad(event));
+    const blink::WebGestureEvent gesture_event =
+        static_cast<const blink::WebGestureEvent&>(event);
+    if (gesture_event.data.scroll_update.inertial_phase !=
+        blink::WebGestureEvent::kMomentumPhase)
+      return false;
+  }
+
   if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd &&
       overscroll_source_ == OverscrollSource::TOUCHPAD) {
     DCHECK(IsGestureEventFromTouchpad(event));
@@ -469,6 +498,7 @@
 }
 
 void OverscrollController::CompleteAction() {
+  ignore_following_inertial_events_ = true;
   if (delegate_)
     delegate_->OnOverscrollComplete(overscroll_mode_);
   Reset();
diff --git a/content/browser/renderer_host/overscroll_controller.h b/content/browser/renderer_host/overscroll_controller.h
index f32f835..8bb2c25 100644
--- a/content/browser/renderer_host/overscroll_controller.h
+++ b/content/browser/renderer_host/overscroll_controller.h
@@ -97,8 +97,7 @@
 
   // Returns true if the event indicates that the in-progress overscroll gesture
   // can now be completed.
-  bool DispatchEventCompletesAction(
-      const blink::WebInputEvent& event) const;
+  bool DispatchEventCompletesAction(const blink::WebInputEvent& event) const;
 
   // Returns true to indicate that dispatching the event should reset the
   // overscroll gesture status.
@@ -158,6 +157,11 @@
 
   bool wheel_scroll_latching_enabled_;
 
+  // A inertial scroll (fling) event may complete an overscroll gesture and
+  // navigate to a new page, but the inertial scroll can continue to generate
+  // scroll-update events. These events need to be ignored.
+  bool ignore_following_inertial_events_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(OverscrollController);
 };
 
diff --git a/content/browser/renderer_host/overscroll_controller_unittest.cc b/content/browser/renderer_host/overscroll_controller_unittest.cc
index db2f3d4..cef55e38 100644
--- a/content/browser/renderer_host/overscroll_controller_unittest.cc
+++ b/content/browser/renderer_host/overscroll_controller_unittest.cc
@@ -11,6 +11,7 @@
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/test/test_overscroll_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebInputEvent.h"
 
 namespace content {
 
@@ -39,15 +40,31 @@
     return controller_->WillHandleEvent(*current_event_);
   }
 
+  // Creates and sends a gesture event to the overscroll controller. Returns
+  // |true| if the event is consumed by the overscroll controller.
+  bool SimulateGestureEvent(blink::WebInputEvent::Type type,
+                            blink::WebGestureDevice source_device) {
+    DCHECK(!current_event_);
+    current_event_ = std::make_unique<blink::WebGestureEvent>(
+        SyntheticWebGestureEventBuilder::Build(type, source_device));
+    return controller_->WillHandleEvent(*current_event_);
+  }
+
   // Creates and sends a gesture-scroll-update event to the overscroll
   // controller. Returns |true| if the event is consumed by the overscroll
   // controller.
   bool SimulateGestureScrollUpdate(float dx,
                                    float dy,
-                                   blink::WebGestureDevice device) {
+                                   blink::WebGestureDevice device,
+                                   bool inertial_update) {
     DCHECK(!current_event_);
-    current_event_ = std::make_unique<blink::WebGestureEvent>(
+    auto event = std::make_unique<blink::WebGestureEvent>(
         SyntheticWebGestureEventBuilder::BuildScrollUpdate(dx, dy, 0, device));
+    if (inertial_update) {
+      event->data.scroll_update.inertial_phase =
+          blink::WebGestureEvent::kMomentumPhase;
+    }
+    current_event_ = std::move(event);
     return controller_->WillHandleEvent(*current_event_);
   }
 
@@ -88,8 +105,8 @@
   // passing the start threshold, no overscroll should happen.
   EXPECT_FALSE(SimulateMouseWheel(10, 0));
   SimulateAck(false);
-  EXPECT_FALSE(
-      SimulateGestureScrollUpdate(10, 0, blink::kWebGestureDeviceTouchpad));
+  EXPECT_FALSE(SimulateGestureScrollUpdate(
+      10, 0, blink::kWebGestureDeviceTouchpad, false));
   SimulateAck(false);
   EXPECT_EQ(OVERSCROLL_NONE, controller_mode());
   EXPECT_EQ(OverscrollSource::NONE, controller_source());
@@ -111,8 +128,8 @@
   // marked as processed.
   EXPECT_FALSE(SimulateMouseWheel(100, 0));
   SimulateAck(false);
-  EXPECT_FALSE(
-      SimulateGestureScrollUpdate(100, 0, blink::kWebGestureDeviceTouchpad));
+  EXPECT_FALSE(SimulateGestureScrollUpdate(
+      100, 0, blink::kWebGestureDeviceTouchpad, false));
   SimulateAck(false);
   EXPECT_EQ(OVERSCROLL_NONE, controller_mode());
   EXPECT_EQ(OverscrollSource::NONE, controller_source());
@@ -120,4 +137,34 @@
   EXPECT_EQ(OVERSCROLL_NONE, delegate()->completed_mode());
 }
 
+// Verifying the inertial scroll event completes overscroll. After that we will
+// ignore the following inertial scroll events until new sequence start.
+TEST_F(OverscrollControllerTest,
+       InertialGestureScrollUpdateCompletesOverscroll) {
+  EXPECT_FALSE(SimulateGestureEvent(blink::WebInputEvent::kGestureScrollBegin,
+                                    blink::kWebGestureDeviceTouchpad));
+  SimulateAck(false);
+
+  EXPECT_FALSE(SimulateGestureScrollUpdate(
+      200, 0, blink::kWebGestureDeviceTouchpad, false));
+  SimulateAck(false);
+  EXPECT_EQ(OVERSCROLL_EAST, controller_mode());
+  EXPECT_EQ(OverscrollSource::TOUCHPAD, controller_source());
+  EXPECT_EQ(OVERSCROLL_EAST, delegate()->current_mode());
+  EXPECT_EQ(OVERSCROLL_NONE, delegate()->completed_mode());
+
+  // Inertial update event complete the overscroll action.
+  EXPECT_FALSE(SimulateGestureScrollUpdate(
+      100, 0, blink::kWebGestureDeviceTouchpad, true));
+  SimulateAck(false);
+  EXPECT_EQ(OVERSCROLL_EAST, controller_mode());
+  EXPECT_EQ(OverscrollSource::TOUCHPAD, controller_source());
+  EXPECT_EQ(OVERSCROLL_EAST, delegate()->current_mode());
+  EXPECT_EQ(OVERSCROLL_EAST, delegate()->completed_mode());
+
+  // Next Inertial update event would be consumed by overscroll controller.
+  EXPECT_TRUE(SimulateGestureScrollUpdate(
+      100, 0, blink::kWebGestureDeviceTouchpad, true));
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index d1eed97..6e9c3c18 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2633,6 +2633,7 @@
     switches::kDisable2dCanvasImageChromium,
     switches::kDisableAcceleratedJpegDecoding,
     switches::kDisableAcceleratedVideoDecode,
+    switches::kDisableBackgroundTasks,
     switches::kDisableBackgroundTimerThrottling,
     switches::kDisableBreakpad,
     switches::kDisableCompositorUkmForTests,
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 7a3d9700..185e633 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -45,12 +45,14 @@
 using webauth::mojom::MakeCredentialAuthenticatorResponsePtr;
 using webauth::mojom::PublicKeyCredentialCreationOptions;
 using webauth::mojom::PublicKeyCredentialCreationOptionsPtr;
+using webauth::mojom::PublicKeyCredentialDescriptor;
 using webauth::mojom::PublicKeyCredentialParameters;
 using webauth::mojom::PublicKeyCredentialParametersPtr;
 using webauth::mojom::PublicKeyCredentialRequestOptions;
 using webauth::mojom::PublicKeyCredentialRequestOptionsPtr;
 using webauth::mojom::PublicKeyCredentialRpEntity;
 using webauth::mojom::PublicKeyCredentialRpEntityPtr;
+using webauth::mojom::PublicKeyCredentialType;
 using webauth::mojom::PublicKeyCredentialUserEntity;
 using webauth::mojom::PublicKeyCredentialUserEntityPtr;
 
@@ -653,6 +655,44 @@
   EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status());
 }
 
+TEST_F(AuthenticatorImplTest, OversizedCredentialId) {
+  device::test::ScopedVirtualFidoDevice virtual_device_;
+  TestServiceManagerContext smc_;
+  // 255 is the maximum size of a U2F credential ID. We also test one greater
+  // (256) to ensure that nothing untoward happens.
+  const std::vector<size_t> kSizes = {255, 256};
+
+  for (size_t size : kSizes) {
+    SCOPED_TRACE(size);
+
+    SimulateNavigation(GURL(kTestOrigin1));
+    AuthenticatorPtr authenticator = ConnectToAuthenticator();
+    PublicKeyCredentialRequestOptionsPtr options =
+        GetTestPublicKeyCredentialRequestOptions();
+    auto credential = PublicKeyCredentialDescriptor::New();
+    credential->type = PublicKeyCredentialType::PUBLIC_KEY;
+    credential->id.resize(size);
+
+    const bool should_be_valid = size < 256;
+    if (should_be_valid) {
+      ASSERT_TRUE(virtual_device_.mutable_state()->InjectRegistration(
+          credential->id, kTestRelyingPartyId));
+    }
+
+    options->allow_credentials.emplace_back(std::move(credential));
+
+    TestGetAssertionCallback cb;
+    authenticator->GetAssertion(std::move(options), cb.callback());
+    cb.WaitForCallback();
+
+    if (should_be_valid) {
+      EXPECT_EQ(AuthenticatorStatus::SUCCESS, cb.status());
+    } else {
+      EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status());
+    }
+  }
+}
+
 enum class IndividualAttestation {
   REQUESTED,
   NOT_REQUESTED,
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
index 0faca8a..c646bab 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
@@ -35,7 +35,6 @@
 import org.chromium.device.gamepad.GamepadList;
 import org.chromium.ui.base.EventForwarder;
 import org.chromium.ui.base.ViewAndroidDelegate;
-import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.display.DisplayAndroid;
 import org.chromium.ui.display.DisplayAndroid.DisplayAndroidObserver;
@@ -73,15 +72,20 @@
     // A ViewAndroidDelegate that delegates to the current container view.
     private ViewAndroidDelegate mViewAndroidDelegate;
 
-    // TODO(mthiesse): Clean up the focus code here, this boolean doesn't actually reflect view
-    // focus. It reflects a combination of both view focus and resumed state.
+    // Whether the container view has view-level focus.
     private Boolean mHasViewFocus;
 
-    // This is used in place of window focus, as we can't actually use window focus due to issues
-    // where content expects to be focused while a popup steals window focus.
+    // This is used in place of window focus on the container view, as we can't actually use window
+    // focus due to issues where content expects to be focused while a popup steals window focus.
     // See https://crbug.com/686232 for more context.
     private boolean mPaused;
 
+    // Whether we consider this CVC to have input focus. This is computed through mHasViewFocus and
+    // mPaused. See the comments on mPaused for how this doesn't exactly match Android's notion of
+    // input focus and why we need to do this.
+    private Boolean mHasInputFocus;
+    private boolean mHideKeyboardOnBlur;
+
     // The list of observers that are notified when ContentViewCore changes its WindowAndroid.
     private final ObserverList<WindowAndroidChangedObserver> mWindowAndroidChangedObservers =
             new ObserverList<>();
@@ -389,14 +393,16 @@
 
     @Override
     public void onPause() {
+        if (mPaused) return;
         mPaused = true;
-        onFocusChanged(false, true);
+        onFocusChanged();
     }
 
     @Override
     public void onResume() {
+        if (!mPaused) return;
         mPaused = false;
-        onFocusChanged(ViewUtils.hasFocus(getContainerView()), true);
+        onFocusChanged();
     }
 
     @Override
@@ -408,12 +414,20 @@
     }
 
     @Override
-    public void onFocusChanged(boolean gainFocus, boolean hideKeyboardOnBlur) {
+    public void onViewFocusChanged(boolean gainFocus) {
         if (mHasViewFocus != null && mHasViewFocus == gainFocus) return;
-        // TODO(mthiesse): Clean this up. mHasViewFocus isn't view focus really, it's a combination
-        // of view focus and resumed state.
-        if (gainFocus && mPaused) return;
         mHasViewFocus = gainFocus;
+        onFocusChanged();
+    }
+
+    private void onFocusChanged() {
+        // Wait for view focus to be set before propagating focus changes.
+        if (mHasViewFocus == null) return;
+
+        // See the comments on mPaused for why we use it to compute input focus.
+        boolean hasInputFocus = mHasViewFocus && !mPaused;
+        if (mHasInputFocus != null && mHasInputFocus == hasInputFocus) return;
+        mHasInputFocus = hasInputFocus;
 
         if (mWebContents == null) {
             // CVC is on its way to destruction. The rest needs not running as all the states
@@ -422,12 +436,12 @@
             return;
         }
 
-        getImeAdapter().onViewFocusChanged(gainFocus, hideKeyboardOnBlur);
+        getImeAdapter().onViewFocusChanged(mHasInputFocus, mHideKeyboardOnBlur);
         getJoystick().setScrollEnabled(
-                gainFocus && !getSelectionPopupController().isFocusedNodeEditable());
+                mHasInputFocus && !getSelectionPopupController().isFocusedNodeEditable());
 
         SelectionPopupControllerImpl controller = getSelectionPopupController();
-        if (gainFocus) {
+        if (mHasInputFocus) {
             controller.restoreSelectionPopupsIfNecessary();
         } else {
             getImeAdapter().cancelRequestToScrollFocusedEditableNodeIntoView();
@@ -442,7 +456,12 @@
                 controller.clearSelection();
             }
         }
-        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
+        if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, mHasInputFocus);
+    }
+
+    @Override
+    public void setHideKeyboardOnBlur(boolean hideKeyboardOnBlur) {
+        mHideKeyboardOnBlur = hideKeyboardOnBlur;
     }
 
     @Override
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java
index 792ca034..c134d3f8 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java
@@ -199,11 +199,16 @@
     void onResume();
 
     /**
-     * Called when keyboard/IME focus has changed.
+     * Called when view-level focus for the container view has changed.
      * @param gainFocus {@code true} if the focus is gained, otherwise {@code false}.
+     */
+    void onViewFocusChanged(boolean gainFocus);
+
+    /**
+     * Sets whether the keyboard should be hidden when losing input focus.
      * @param hideKeyboardOnBlur {@code true} if we should hide soft keyboard when losing focus.
      */
-    void onFocusChanged(boolean gainFocus, boolean hideKeyboardOnBlur);
+    void setHideKeyboardOnBlur(boolean hideKeyboardOnBlur);
 
     /**
      * @see View#scrollBy(int, int)
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java
index 877da992..154a7c26 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreSelectionTest.java
@@ -893,7 +893,7 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                contentViewCore.onFocusChanged(gainFocus, true);
+                contentViewCore.onViewFocusChanged(gainFocus);
             }
         });
     }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java
index df35db2..a7c55d1 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java
@@ -234,7 +234,7 @@
                 Assert.assertTrue(mPopupZoomer.isShowing());
 
                 // Simulate losing the focus.
-                mContentViewCore.onFocusChanged(false, true);
+                mContentViewCore.onViewFocusChanged(false);
 
                 // Wait for the hide animation to finish.
                 mPopupZoomer.finishPendingDraws();
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java
index daa04100..507bfb7 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java
@@ -86,7 +86,10 @@
     public void onResume() {}
 
     @Override
-    public void onFocusChanged(boolean gainFocus, boolean hideKeyboardOnBlur) {}
+    public void onViewFocusChanged(boolean gainFocus) {}
+
+    @Override
+    public void setHideKeyboardOnBlur(boolean hideKeyboardOnBlur) {}
 
     @Override
     public void scrollBy(float dxPix, float dyPix) {}
diff --git a/content/renderer/dom_storage/dom_storage_cached_area.cc b/content/renderer/dom_storage/dom_storage_cached_area.cc
index 654ed9e..18215ac 100644
--- a/content/renderer/dom_storage/dom_storage_cached_area.cc
+++ b/content/renderer/dom_storage/dom_storage_cached_area.cc
@@ -73,8 +73,9 @@
 
   // Ignore mutations to 'key' until OnSetItemComplete.
   blink::WebScopedVirtualTimePauser virtual_time_pauser =
-      main_thread_scheduler_->CreateWebScopedVirtualTimePauser();
-  virtual_time_pauser.PauseVirtualTime(true);
+      main_thread_scheduler_->CreateWebScopedVirtualTimePauser(
+          "DOMStorageCachedArea");
+  virtual_time_pauser.PauseVirtualTime();
   ignore_key_mutations_[key]++;
   proxy_->SetItem(connection_id, key, value, old_value, page_url,
                   base::BindOnce(&DOMStorageCachedArea::OnSetItemComplete,
@@ -100,8 +101,9 @@
 
   // Ignore mutations to 'key' until OnRemoveItemComplete.
   blink::WebScopedVirtualTimePauser virtual_time_pauser =
-      main_thread_scheduler_->CreateWebScopedVirtualTimePauser();
-  virtual_time_pauser.PauseVirtualTime(true);
+      main_thread_scheduler_->CreateWebScopedVirtualTimePauser(
+          "DOMStorageCachedArea");
+  virtual_time_pauser.PauseVirtualTime();
   ignore_key_mutations_[key]++;
   proxy_->RemoveItem(connection_id, key,
                      base::NullableString16(old_value, false), page_url,
@@ -117,8 +119,9 @@
 
   // Ignore all mutations until OnClearComplete time.
   blink::WebScopedVirtualTimePauser virtual_time_pauser =
-      main_thread_scheduler_->CreateWebScopedVirtualTimePauser();
-  virtual_time_pauser.PauseVirtualTime(true);
+      main_thread_scheduler_->CreateWebScopedVirtualTimePauser(
+          "DOMStorageCachedArea");
+  virtual_time_pauser.PauseVirtualTime();
   ignore_all_mutations_ = true;
   proxy_->ClearArea(connection_id, page_url,
                     base::BindOnce(&DOMStorageCachedArea::OnClearComplete,
diff --git a/content/renderer/dom_storage/local_storage_cached_area.cc b/content/renderer/dom_storage/local_storage_cached_area.cc
index d079a6c..fc605cce 100644
--- a/content/renderer/dom_storage/local_storage_cached_area.cc
+++ b/content/renderer/dom_storage/local_storage_cached_area.cc
@@ -162,8 +162,9 @@
         String16ToUint8Vector(old_nullable_value.string(), is_session_storage);
 
   blink::WebScopedVirtualTimePauser virtual_time_pauser =
-      main_thread_scheduler_->CreateWebScopedVirtualTimePauser();
-  virtual_time_pauser.PauseVirtualTime(true);
+      main_thread_scheduler_->CreateWebScopedVirtualTimePauser(
+          "LocalStorageCachedArea");
+  virtual_time_pauser.PauseVirtualTime();
   leveldb_->Put(String16ToUint8Vector(key, is_session_storage),
                 String16ToUint8Vector(value, is_session_storage),
                 optional_old_value, PackSource(page_url, storage_area_id),
@@ -194,8 +195,9 @@
     optional_old_value = String16ToUint8Vector(old_value, is_session_storage);
 
   blink::WebScopedVirtualTimePauser virtual_time_pauser =
-      main_thread_scheduler_->CreateWebScopedVirtualTimePauser();
-  virtual_time_pauser.PauseVirtualTime(true);
+      main_thread_scheduler_->CreateWebScopedVirtualTimePauser(
+          "LocalStorageCachedArea");
+  virtual_time_pauser.PauseVirtualTime();
   leveldb_->Delete(String16ToUint8Vector(key, is_session_storage),
                    optional_old_value, PackSource(page_url, storage_area_id),
                    base::BindOnce(&LocalStorageCachedArea::OnRemoveItemComplete,
@@ -211,8 +213,9 @@
   ignore_all_mutations_ = true;
 
   blink::WebScopedVirtualTimePauser virtual_time_pauser =
-      main_thread_scheduler_->CreateWebScopedVirtualTimePauser();
-  virtual_time_pauser.PauseVirtualTime(true);
+      main_thread_scheduler_->CreateWebScopedVirtualTimePauser(
+          "LocalStorageCachedArea");
+  virtual_time_pauser.PauseVirtualTime();
   leveldb_->DeleteAll(PackSource(page_url, storage_area_id),
                       base::BindOnce(&LocalStorageCachedArea::OnClearComplete,
                                      weak_factory_.GetWeakPtr(),
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index c4013c92..a7dde19 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -766,6 +766,12 @@
             static_cast<webrtc::KeyExchangeProtocolMedia>(counter),
             webrtc::kEnumCounterKeyProtocolMediaTypeMax);
         break;
+      case webrtc::kEnumCounterSdpFormatReceived:
+        UMA_HISTOGRAM_ENUMERATION(
+            "WebRTC.PeerConnection.SdpFormatReceived",
+            static_cast<webrtc::SdpFormatReceived>(counter),
+            webrtc::kSdpFormatReceivedMax);
+        break;
       default:
         // The default clause is expected to be reached when new enum types are
         // added.
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 2a9520e..d7102e0 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1566,13 +1566,14 @@
       RenderThreadImpl::current()
           ->GetWebMainThreadScheduler()
           ->CreateWebScopedVirtualTimePauser(
+              "NavigateBackForwardSoon",
               blink::WebScopedVirtualTimePauser::VirtualTaskDuration::kInstant);
-  history_navigation_virtual_time_pauser_.PauseVirtualTime(true);
+  history_navigation_virtual_time_pauser_.PauseVirtualTime();
   Send(new ViewHostMsg_GoToEntryAtOffset(GetRoutingID(), offset));
 }
 
 void RenderViewImpl::DidCommitProvisionalHistoryLoad() {
-  history_navigation_virtual_time_pauser_.PauseVirtualTime(false);
+  history_navigation_virtual_time_pauser_.UnpauseVirtualTime();
 }
 
 int RenderViewImpl::HistoryBackListCount() {
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 47f6df7c..e4cedaf 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -391,7 +391,6 @@
       closing_(false),
       host_closing_(false),
       is_swapped_out_(swapped_out),
-      for_oopif_(false),
       text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
       text_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT),
       text_input_flags_(0),
@@ -408,6 +407,7 @@
       has_host_context_menu_location_(false),
       has_added_input_handler_(false),
       has_focus_(false),
+      for_oopif_(false),
 #if defined(OS_MACOSX)
       text_input_client_observer_(new TextInputClientObserver(this)),
 #endif
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 94d077a..19740fc6 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -778,9 +778,6 @@
   // swapped out, the process can exit.
   bool is_swapped_out_;
 
-  // Whether this RenderWidget is for an out-of-process iframe or not.
-  bool for_oopif_;
-
   // Stores information about the current text input.
   blink::WebTextInputInfo text_input_info_;
 
@@ -945,6 +942,9 @@
   // Indicates whether this widget has focus.
   bool has_focus_;
 
+  // Whether this RenderWidget is for an out-of-process iframe or not.
+  bool for_oopif_;
+
   // A callback into the creator/opener of this widget, to be executed when
   // WebWidgetClient::show() occurs.
   ShowCallback show_callback_;
diff --git a/content/shell/app/blink_test_platform_support_mac.mm b/content/shell/app/blink_test_platform_support_mac.mm
index 7a16563..6ec3e9b 100644
--- a/content/shell/app/blink_test_platform_support_mac.mm
+++ b/content/shell/app/blink_test_platform_support_mac.mm
@@ -42,6 +42,10 @@
              forKey:@"NSScrollAnimationEnabled"];
   [defaults setObject:@"Always"
                forKey:@"AppleShowScrollBars"];
+
+  // Disable AppNap since layout tests do not always meet the requirements to
+  // avoid "napping" which will cause test timeouts. http://crbug.com/811525.
+  [defaults setBool:YES forKey:@"NSAppSleepDisabled"];
 }
 
 }  // namespace
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index bb117de8..63bdd9d 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -139,3 +139,6 @@
     self.Fail('Pixel_WebGLGreenTriangle_NoAA_NoAlpha', ['android'], bug=972546)
     self.Fail('Pixel_WebGLTransparentGreenTriangle_NoAlpha_ImplicitClear',
               ['android'], bug=972546)
+
+    # TODO(hubbe): Temporary supression for rebaseline
+    self.Fail('Pixel_Video_VP9', bug=754986)
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 023d63b..6532afac 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -185,7 +185,7 @@
       'pixel_video_vp9.html',
       base_name + '_Video_VP9',
       test_rect=[0, 0, 300, 300],
-      revision=7),
+      revision=8),
 
     PixelTestPage(
       'pixel_webgl_premultiplied_alpha_false.html',
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 81d675d..c8d8df92 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -277,6 +277,7 @@
       "//device/vr/public/mojom",
       "//services/device/public/cpp/generic_sensor",
       "//ui/display",
+      "//ui/display:test_support",
     ]
   }
 }
diff --git a/device/bluetooth/public/mojom/test/fake_bluetooth.mojom b/device/bluetooth/public/mojom/test/fake_bluetooth.mojom
index 6596473..8bd0d334 100644
--- a/device/bluetooth/public/mojom/test/fake_bluetooth.mojom
+++ b/device/bluetooth/public/mojom/test/fake_bluetooth.mojom
@@ -288,6 +288,28 @@
       string service_id,
       string peripheral_address) => (bool success);
 
+  // Sets the next unsubscribe from notifications response for characteristic
+  // with |characteristic_id| in |service_id| and in |peripheral_address| to
+  // |code|. |code| could be a GATT Error Response from BT 4.2 Vol 3
+  // Part F 3.4.1.1 Error Response or a number outside that range returned by
+  // specific platforms e.g. Android returns 0x101 to signal a GATT failure.
+  // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
+  // Calls callback with false if there was any error when simulating the next
+  // response.
+  SetNextUnsubscribeFromNotificationsResponse(
+      uint16 gatt_code,
+      string characteristic_id,
+      string service_id,
+      string peripheral_address) => (bool success);
+
+  // Returns whether or not a client has subscribed to notifications for a
+  // characteristic with |characteristic_id| in |service_id| in
+  // |peripheral_address|. If the value can't be retrieved, calls its callback
+  // with false.
+  IsNotifying(string characteristic_id,
+              string service_id,
+              string peripheral_address) => (bool success, bool is_notifying);
+
   // Gets the last successfully written value to the characteristic with
   // |characteristics_id| in |service_id| and in |peripheral_address|.
   // If the value can't be retrieved calls its callback with false. Calls its
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index 1b5fdc0..151e931 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -317,6 +317,38 @@
   std::move(callback).Run(true);
 }
 
+void FakeCentral::SetNextUnsubscribeFromNotificationsResponse(
+    uint16_t gatt_code,
+    const std::string& characteristic_id,
+    const std::string& service_id,
+    const std::string& peripheral_address,
+    SetNextUnsubscribeFromNotificationsResponseCallback callback) {
+  FakeRemoteGattCharacteristic* fake_remote_gatt_characteristic =
+      GetFakeRemoteGattCharacteristic(peripheral_address, service_id,
+                                      characteristic_id);
+  if (fake_remote_gatt_characteristic == nullptr) {
+    std::move(callback).Run(false);
+  }
+
+  fake_remote_gatt_characteristic->SetNextUnsubscribeFromNotificationsResponse(
+      gatt_code);
+  std::move(callback).Run(true);
+}
+
+void FakeCentral::IsNotifying(const std::string& characteristic_id,
+                              const std::string& service_id,
+                              const std::string& peripheral_address,
+                              IsNotifyingCallback callback) {
+  FakeRemoteGattCharacteristic* fake_remote_gatt_characteristic =
+      GetFakeRemoteGattCharacteristic(peripheral_address, service_id,
+                                      characteristic_id);
+  if (!fake_remote_gatt_characteristic) {
+    std::move(callback).Run(false, false);
+  }
+
+  std::move(callback).Run(true, fake_remote_gatt_characteristic->IsNotifying());
+}
+
 void FakeCentral::GetLastWrittenCharacteristicValue(
     const std::string& characteristic_id,
     const std::string& service_id,
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 18a35ea6..a584e347 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -98,7 +98,17 @@
       const std::string& characteristic_id,
       const std::string& service_id,
       const std::string& peripheral_address,
-      SetNextWriteCharacteristicResponseCallback callback) override;
+      SetNextSubscribeToNotificationsResponseCallback callback) override;
+  void SetNextUnsubscribeFromNotificationsResponse(
+      uint16_t gatt_code,
+      const std::string& characteristic_id,
+      const std::string& service_id,
+      const std::string& peripheral_address,
+      SetNextUnsubscribeFromNotificationsResponseCallback callback) override;
+  void IsNotifying(const std::string& characteristic_id,
+                   const std::string& service_id,
+                   const std::string& peripheral_address,
+                   IsNotifyingCallback callback) override;
   void GetLastWrittenCharacteristicValue(
       const std::string& characteristic_id,
       const std::string& service_id,
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.cc b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
index 8e0a8077..31acc78 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.cc
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
@@ -91,6 +91,12 @@
   next_subscribe_to_notifications_response_.emplace(gatt_code);
 }
 
+void FakeRemoteGattCharacteristic::SetNextUnsubscribeFromNotificationsResponse(
+    uint16_t gatt_code) {
+  DCHECK(!next_unsubscribe_from_notifications_response_);
+  next_unsubscribe_from_notifications_response_.emplace(gatt_code);
+}
+
 bool FakeRemoteGattCharacteristic::AllResponsesConsumed() {
   // TODO(crbug.com/569709): Update this when
   // SetNextUnsubscribeFromNotificationsResponse is implemented.
@@ -192,7 +198,11 @@
     device::BluetoothRemoteGattDescriptor* ccc_descriptor,
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  NOTREACHED();
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&FakeRemoteGattCharacteristic::
+                         DispatchUnsubscribeFromNotificationsResponse,
+                     weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
 }
 
 void FakeRemoteGattCharacteristic::DispatchReadResponse(
@@ -258,4 +268,23 @@
   }
 }
 
+void FakeRemoteGattCharacteristic::DispatchUnsubscribeFromNotificationsResponse(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  DCHECK(next_unsubscribe_from_notifications_response_);
+  uint16_t gatt_code = next_unsubscribe_from_notifications_response_.value();
+  next_unsubscribe_from_notifications_response_.reset();
+
+  switch (gatt_code) {
+    case mojom::kGATTSuccess:
+      callback.Run();
+      break;
+    case mojom::kGATTInvalidHandle:
+      error_callback.Run(device::BluetoothGattService::GATT_ERROR_FAILED);
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
 }  // namespace bluetooth
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.h b/device/bluetooth/test/fake_remote_gatt_characteristic.h
index 48ce0e6d..a4b0cc0b 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.h
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.h
@@ -59,6 +59,11 @@
   // call its error callback.
   void SetNextSubscribeToNotificationsResponse(uint16_t gatt_code);
 
+  // If |gatt_code| is mojom::kGATTSuccess the next unsubscribe to notifications
+  // with response request will call its success callback.  Otherwise it will
+  // call its error callback.
+  void SetNextUnsubscribeFromNotificationsResponse(uint16_t gatt_code);
+
   // Returns true if there are no pending responses for this characteristc or
   // any of its descriptors.
   bool AllResponsesConsumed();
@@ -108,6 +113,9 @@
   void DispatchSubscribeToNotificationsResponse(
       const base::Closure& callback,
       const ErrorCallback& error_callback);
+  void DispatchUnsubscribeFromNotificationsResponse(
+      const base::Closure& callback,
+      const ErrorCallback& error_callback);
 
   const std::string characteristic_id_;
   const device::BluetoothUUID characteristic_uuid_;
@@ -130,6 +138,10 @@
   // SubscribeToNotifications is called.
   base::Optional<uint16_t> next_subscribe_to_notifications_response_;
 
+  // Used to decide which callback should be called when
+  // UnsubscribeFromNotifications is called.
+  base::Optional<uint16_t> next_unsubscribe_from_notifications_response_;
+
   size_t last_descriptor_id_;
 
   using FakeDescriptorMap =
diff --git a/device/fido/attested_credential_data.cc b/device/fido/attested_credential_data.cc
index efada48b..77ae660 100644
--- a/device/fido/attested_credential_data.cc
+++ b/device/fido/attested_credential_data.cc
@@ -4,8 +4,7 @@
 
 #include "device/fido/attested_credential_data.h"
 
-#include <stddef.h>
-
+#include <algorithm>
 #include <utility>
 
 #include "base/numerics/safe_math.h"
@@ -15,15 +14,6 @@
 
 namespace device {
 
-namespace {
-
-constexpr size_t kAaguidLength = 16;
-
-// Number of bytes used to represent length of credential ID.
-constexpr size_t kCredentialIdLengthLength = 2;
-
-}  // namespace
-
 // static
 base::Optional<AttestedCredentialData>
 AttestedCredentialData::DecodeFromCtapResponse(
@@ -31,16 +21,20 @@
   if (buffer.size() < kAaguidLength + kCredentialIdLengthLength)
     return base::nullopt;
 
-  auto aaguid = u2f_parsing_utils::Extract(buffer, 0, kAaguidLength);
-  auto credential_id_length_span = u2f_parsing_utils::ExtractSpan(
-      buffer, kAaguidLength, kCredentialIdLengthLength);
-
-  if (aaguid.empty() || credential_id_length_span.empty())
+  std::array<uint8_t, kAaguidLength> aaguid;
+  if (!u2f_parsing_utils::ExtractArray(buffer, 0, &aaguid))
     return base::nullopt;
 
+  std::array<uint8_t, kCredentialIdLengthLength> credential_id_length_array;
+  if (!u2f_parsing_utils::ExtractArray(buffer, kAaguidLength,
+                                       &credential_id_length_array)) {
+    return base::nullopt;
+  }
+
   static_assert(kCredentialIdLengthLength == 2u, "L must be 2 bytes");
   const size_t credential_id_length =
-      credential_id_length_span[0] << 8 | credential_id_length_span[1];
+      (base::strict_cast<size_t>(credential_id_length_array[0]) << 8) |
+      base::strict_cast<size_t>(credential_id_length_array[1]);
 
   auto credential_id = u2f_parsing_utils::Extract(
       buffer, kAaguidLength + kCredentialIdLengthLength, credential_id_length);
@@ -54,9 +48,7 @@
           kAaguidLength + kCredentialIdLengthLength + credential_id_length));
 
   return AttestedCredentialData(
-      std::move(aaguid),
-      std::vector<uint8_t>(credential_id_length_span.begin(),
-                           credential_id_length_span.end()),
+      std::move(aaguid), std::move(credential_id_length_array),
       std::move(credential_id), std::move(credential_public_key_data));
 }
 
@@ -64,7 +56,6 @@
 base::Optional<AttestedCredentialData>
 AttestedCredentialData::CreateFromU2fRegisterResponse(
     base::span<const uint8_t> u2f_data,
-    std::vector<uint8_t> aaguid,
     std::unique_ptr<PublicKey> public_key) {
   // TODO(crbug/799075): Introduce a CredentialID class to do this extraction.
   // Extract the length of the credential (i.e. of the U2FResponse key
@@ -76,8 +67,13 @@
     return base::nullopt;
   }
 
+  // For U2F register request, device AAGUID is set to zeros.
+  std::array<uint8_t, kAaguidLength> aaguid;
+  aaguid.fill(0);
+
   // Note that U2F responses only use one byte for length.
-  std::vector<uint8_t> credential_id_length = {0, extracted_length[0]};
+  std::array<uint8_t, kCredentialIdLengthLength> credential_id_length = {
+      0, extracted_length[0]};
 
   // Extract the credential id (i.e. key handle).
   std::vector<uint8_t> credential_id = u2f_parsing_utils::Extract(
@@ -93,16 +89,6 @@
       std::move(credential_id), std::move(public_key));
 }
 
-AttestedCredentialData::AttestedCredentialData(
-    std::vector<uint8_t> aaguid,
-    std::vector<uint8_t> length,
-    std::vector<uint8_t> credential_id,
-    std::unique_ptr<PublicKey> public_key)
-    : aaguid_(std::move(aaguid)),
-      credential_id_length_(std::move(length)),
-      credential_id_(std::move(credential_id)),
-      public_key_(std::move(public_key)) {}
-
 AttestedCredentialData::AttestedCredentialData(AttestedCredentialData&& other) =
     default;
 
@@ -112,16 +98,29 @@
 AttestedCredentialData::~AttestedCredentialData() = default;
 
 void AttestedCredentialData::DeleteAaguid() {
-  aaguid_ = std::vector<uint8_t>(kAaguidLength, 0);
+  std::fill(aaguid_.begin(), aaguid_.end(), 0);
 }
 
 std::vector<uint8_t> AttestedCredentialData::SerializeAsBytes() const {
   std::vector<uint8_t> attestation_data;
-  u2f_parsing_utils::Append(&attestation_data, aaguid_);
-  u2f_parsing_utils::Append(&attestation_data, credential_id_length_);
+  u2f_parsing_utils::Append(&attestation_data,
+                            base::make_span(aaguid_.data(), kAaguidLength));
+  u2f_parsing_utils::Append(
+      &attestation_data,
+      base::make_span(credential_id_length_.data(), kCredentialIdLengthLength));
   u2f_parsing_utils::Append(&attestation_data, credential_id_);
   u2f_parsing_utils::Append(&attestation_data, public_key_->EncodeAsCOSEKey());
   return attestation_data;
 }
 
+AttestedCredentialData::AttestedCredentialData(
+    std::array<uint8_t, kAaguidLength> aaguid,
+    std::array<uint8_t, kCredentialIdLengthLength> credential_id_length,
+    std::vector<uint8_t> credential_id,
+    std::unique_ptr<PublicKey> public_key)
+    : aaguid_(std::move(aaguid)),
+      credential_id_length_(std::move(credential_id_length)),
+      credential_id_(std::move(credential_id)),
+      public_key_(std::move(public_key)) {}
+
 }  // namespace device
diff --git a/device/fido/attested_credential_data.h b/device/fido/attested_credential_data.h
index 323e1e5..c33dccb 100644
--- a/device/fido/attested_credential_data.h
+++ b/device/fido/attested_credential_data.h
@@ -5,6 +5,7 @@
 #ifndef DEVICE_FIDO_ATTESTED_CREDENTIAL_DATA_H_
 #define DEVICE_FIDO_ATTESTED_CREDENTIAL_DATA_H_
 
+#include <stddef.h>
 #include <stdint.h>
 #include <memory>
 #include <vector>
@@ -26,14 +27,8 @@
 
   static base::Optional<AttestedCredentialData> CreateFromU2fRegisterResponse(
       base::span<const uint8_t> u2f_data,
-      std::vector<uint8_t> aaguid,
       std::unique_ptr<PublicKey> public_key);
 
-  AttestedCredentialData(std::vector<uint8_t> aaguid,
-                         std::vector<uint8_t> length,
-                         std::vector<uint8_t> credential_id,
-                         std::unique_ptr<PublicKey> public_key);
-
   // Moveable.
   AttestedCredentialData(AttestedCredentialData&& other);
   AttestedCredentialData& operator=(AttestedCredentialData&& other);
@@ -54,11 +49,22 @@
   std::vector<uint8_t> SerializeAsBytes() const;
 
  private:
+  static constexpr size_t kAaguidLength = 16;
+  // Number of bytes used to represent length of credential ID.
+  static constexpr size_t kCredentialIdLengthLength = 2;
+
+  AttestedCredentialData(
+      std::array<uint8_t, kAaguidLength> aaguid,
+      std::array<uint8_t, kCredentialIdLengthLength> credential_id_length,
+      std::vector<uint8_t> credential_id,
+      std::unique_ptr<PublicKey> public_key);
+
   // The 16-byte AAGUID of the authenticator.
-  std::vector<uint8_t> aaguid_;
+  std::array<uint8_t, kAaguidLength> aaguid_;
 
   // Big-endian length of the credential (i.e. key handle).
-  std::vector<uint8_t> credential_id_length_;
+  std::array<uint8_t, kCredentialIdLengthLength> credential_id_length_;
+
   std::vector<uint8_t> credential_id_;
   std::unique_ptr<PublicKey> public_key_;
 
diff --git a/device/fido/authenticator_make_credential_response.cc b/device/fido/authenticator_make_credential_response.cc
index a6e4a8df..4b41a70 100644
--- a/device/fido/authenticator_make_credential_response.cc
+++ b/device/fido/authenticator_make_credential_response.cc
@@ -25,12 +25,9 @@
   if (!public_key)
     return base::nullopt;
 
-  // AAGUID is zeroed out for U2F responses.
-  std::vector<uint8_t> aaguid(16u);
-
   auto attested_credential_data =
       AttestedCredentialData::CreateFromU2fRegisterResponse(
-          u2f_data, std::move(aaguid), std::move(public_key));
+          u2f_data, std::move(public_key));
 
   if (!attested_credential_data)
     return base::nullopt;
diff --git a/device/fido/u2f_parsing_utils.h b/device/fido/u2f_parsing_utils.h
index 8676514..4c561530 100644
--- a/device/fido/u2f_parsing_utils.h
+++ b/device/fido/u2f_parsing_utils.h
@@ -7,6 +7,8 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <algorithm>
+#include <array>
 #include <vector>
 
 #include "base/component_export.h"
@@ -59,6 +61,18 @@
 base::span<const uint8_t> ExtractSuffixSpan(base::span<const uint8_t> span,
                                             size_t pos);
 
+template <size_t N>
+bool ExtractArray(base::span<const uint8_t> span,
+                  size_t pos,
+                  std::array<uint8_t, N>* array) {
+  const auto extracted_span = ExtractSpan(span, pos, N);
+  if (extracted_span.size() != N)
+    return false;
+
+  std::copy(extracted_span.begin(), extracted_span.end(), array->begin());
+  return true;
+}
+
 }  // namespace u2f_parsing_utils
 }  // namespace device
 
diff --git a/device/fido/u2f_parsing_utils_unittest.cc b/device/fido/u2f_parsing_utils_unittest.cc
index af1688464..4965febd 100644
--- a/device/fido/u2f_parsing_utils_unittest.cc
+++ b/device/fido/u2f_parsing_utils_unittest.cc
@@ -117,5 +117,21 @@
   EXPECT_THAT(ExtractSuffix(kOneTwoThree, 4), ::testing::IsEmpty());
 }
 
+TEST(U2fParsingUtils, ExtractArray) {
+  const std::vector<uint8_t> empty;
+  std::array<uint8_t, 0> array_empty;
+  EXPECT_TRUE(ExtractArray(empty, 0, &array_empty));
+
+  std::array<uint8_t, 2> array_two_three;
+  EXPECT_TRUE(ExtractArray(kTwoThree, 0, &array_two_three));
+  EXPECT_THAT(array_two_three, ::testing::ElementsAreArray(kTwoThree));
+
+  EXPECT_FALSE(ExtractArray(kOneTwoThree, 2, &array_two_three));
+
+  std::array<uint8_t, 1> array_three;
+  EXPECT_TRUE(ExtractArray(kOneTwoThree, 2, &array_three));
+  EXPECT_THAT(array_three, ::testing::ElementsAreArray(kThree));
+}
+
 }  // namespace u2f_parsing_utils
 }  // namespace device
diff --git a/device/fido/u2f_register_unittest.cc b/device/fido/u2f_register_unittest.cc
index b0bd8e1..cf6aef2a 100644
--- a/device/fido/u2f_register_unittest.cc
+++ b/device/fido/u2f_register_unittest.cc
@@ -729,8 +729,7 @@
           u2f_parsing_utils::kEs256, GetTestRegisterResponse());
   base::Optional<AttestedCredentialData> attested_data =
       AttestedCredentialData::CreateFromU2fRegisterResponse(
-          GetTestRegisterResponse(), std::vector<uint8_t>(16) /* aaguid */,
-          std::move(public_key));
+          GetTestRegisterResponse(), std::move(public_key));
 
   EXPECT_EQ(GetTestAttestedCredentialDataBytes(),
             attested_data->SerializeAsBytes());
@@ -743,8 +742,7 @@
           u2f_parsing_utils::kEs256, GetTestRegisterResponse());
   base::Optional<AttestedCredentialData> attested_data =
       AttestedCredentialData::CreateFromU2fRegisterResponse(
-          GetTestRegisterResponse(), std::vector<uint8_t>(16) /* aaguid */,
-          std::move(public_key));
+          GetTestRegisterResponse(), std::move(public_key));
 
   constexpr uint8_t flags =
       static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) |
@@ -766,8 +764,7 @@
           u2f_parsing_utils::kEs256, GetTestRegisterResponse());
   base::Optional<AttestedCredentialData> attested_data =
       AttestedCredentialData::CreateFromU2fRegisterResponse(
-          GetTestRegisterResponse(), std::vector<uint8_t>(16) /* aaguid */,
-          std::move(public_key));
+          GetTestRegisterResponse(), std::move(public_key));
 
   constexpr uint8_t flags =
       static_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence) |
diff --git a/device/fido/virtual_fido_device.cc b/device/fido/virtual_fido_device.cc
index 6f006cf..dc8cb9d 100644
--- a/device/fido/virtual_fido_device.cc
+++ b/device/fido/virtual_fido_device.cc
@@ -92,6 +92,28 @@
     : attestation_cert_common_name("Batch Certificate"),
       individual_attestation_cert_common_name("Individual Certificate") {}
 VirtualFidoDevice::State::~State() = default;
+
+bool VirtualFidoDevice::State::InjectRegistration(
+    const std::vector<uint8_t>& credential_id,
+    const std::string& relying_party_id) {
+  std::vector<uint8_t> application_parameter(crypto::kSHA256Length);
+  crypto::SHA256HashString(relying_party_id, application_parameter.data(),
+                           application_parameter.size());
+
+  auto private_key = crypto::ECPrivateKey::Create();
+  if (!private_key) {
+    return false;
+  }
+  RegistrationData registration(std::move(private_key),
+                                std::move(application_parameter),
+                                0 /* signature counter */);
+
+  bool was_inserted;
+  std::tie(std::ignore, was_inserted) =
+      registrations.emplace(credential_id, std::move(registration));
+  return was_inserted;
+}
+
 VirtualFidoDevice::VirtualFidoDevice()
     : state_(new State), weak_factory_(this) {}
 
diff --git a/device/fido/virtual_fido_device.h b/device/fido/virtual_fido_device.h
index 0504281e..b651e70 100644
--- a/device/fido/virtual_fido_device.h
+++ b/device/fido/virtual_fido_device.h
@@ -64,6 +64,15 @@
     // Registered keys. Keyed on key handle (a.k.a. "credential ID").
     std::map<std::vector<uint8_t>, RegistrationData> registrations;
 
+    // Adds a registration for the specified credential ID with the application
+    // parameter set to be valid for the given relying party ID (which would
+    // typically be a domain, e.g. "example.com").
+    //
+    // Returns true on success. Will fail if there already exists a credential
+    // with the given ID.
+    bool InjectRegistration(const std::vector<uint8_t>& credential_id,
+                            const std::string& relying_party_id);
+
    private:
     friend class base::RefCounted<State>;
     ~State();
diff --git a/device/geolocation/BUILD.gn b/device/geolocation/BUILD.gn
index 1769208..a69729c 100644
--- a/device/geolocation/BUILD.gn
+++ b/device/geolocation/BUILD.gn
@@ -53,6 +53,7 @@
     "wifi_data_provider_manager.h",
     "wifi_data_provider_win.cc",
     "wifi_data_provider_win.h",
+    "wifi_polling_policy.cc",
     "wifi_polling_policy.h",
   ]
 
@@ -172,6 +173,7 @@
     "wifi_data_provider_common_unittest.cc",
     "wifi_data_provider_linux_unittest.cc",
     "wifi_data_provider_win_unittest.cc",
+    "wifi_polling_policy_unittest.cc",
   ]
   public_deps = [
     ":geolocation",
diff --git a/device/geolocation/location_arbitrator.cc b/device/geolocation/location_arbitrator.cc
index fa0ffb01..61370c6 100644
--- a/device/geolocation/location_arbitrator.cc
+++ b/device/geolocation/location_arbitrator.cc
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "device/geolocation/network_location_provider.h"
 #include "device/geolocation/public/cpp/geoposition.h"
+#include "device/geolocation/wifi_polling_policy.h"
 
 namespace device {
 
@@ -33,7 +34,10 @@
       is_permission_granted_(false),
       is_running_(false) {}
 
-LocationArbitrator::~LocationArbitrator() = default;
+LocationArbitrator::~LocationArbitrator() {
+  // Release the global wifi polling policy state.
+  WifiPollingPolicy::Shutdown();
+}
 
 bool LocationArbitrator::HasPermissionBeenGrantedForTest() const {
   return is_permission_granted_;
@@ -45,6 +49,15 @@
     provider->OnPermissionGranted();
 }
 
+void LocationArbitrator::SetLastNetworkPosition(
+    const mojom::Geoposition& position) {
+  last_network_position_ = position;
+}
+
+const mojom::Geoposition& LocationArbitrator::GetLastNetworkPosition() {
+  return last_network_position_;
+}
+
 void LocationArbitrator::StartProvider(bool enable_high_accuracy) {
   is_running_ = true;
   enable_high_accuracy_ = enable_high_accuracy;
@@ -156,7 +169,8 @@
   // Android uses its own SystemLocationProvider.
   return nullptr;
 #else
-  return std::make_unique<NetworkLocationProvider>(std::move(context), api_key);
+  return std::make_unique<NetworkLocationProvider>(std::move(context), api_key,
+                                                   this);
 #endif
 }
 
diff --git a/device/geolocation/location_arbitrator.h b/device/geolocation/location_arbitrator.h
index 61c3f0cb..8e1e9ec 100644
--- a/device/geolocation/location_arbitrator.h
+++ b/device/geolocation/location_arbitrator.h
@@ -16,6 +16,7 @@
 #include "base/time/time.h"
 #include "device/geolocation/geolocation_export.h"
 #include "device/geolocation/geolocation_provider_impl.h"
+#include "device/geolocation/network_location_provider.h"
 #include "device/geolocation/public/cpp/location_provider.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "services/device/public/mojom/geoposition.mojom.h"
@@ -30,7 +31,9 @@
 // This class is responsible for handling updates from multiple underlying
 // providers and resolving them to a single 'best' location fix at any given
 // moment.
-class DEVICE_GEOLOCATION_EXPORT LocationArbitrator : public LocationProvider {
+class DEVICE_GEOLOCATION_EXPORT LocationArbitrator
+    : public LocationProvider,
+      public NetworkLocationProvider::LastPositionCache {
  public:
   // The TimeDelta newer a location provider has to be that it's worth
   // switching to this location provider on the basis of it being fresher
@@ -57,6 +60,10 @@
   const mojom::Geoposition& GetPosition() override;
   void OnPermissionGranted() override;
 
+  // NetworkLocationProvider::LastPositionCache implementation.
+  void SetLastNetworkPosition(const mojom::Geoposition& position) override;
+  const mojom::Geoposition& GetLastNetworkPosition() override;
+
  protected:
   // These functions are useful for injection of dependencies in derived
   // testing classes.
@@ -114,6 +121,11 @@
   // The current best estimate of our position.
   mojom::Geoposition position_;
 
+  // The most recent position estimate returned by the network location
+  // provider. This must be preserved by LocationArbitrator so it is not lost
+  // when the provider is destroyed in StopProvider.
+  mojom::Geoposition last_network_position_;
+
   // Tracks whether providers should be running.
   bool is_running_;
 
diff --git a/device/geolocation/network_location_provider.cc b/device/geolocation/network_location_provider.cc
index 61f00f2..00032cab 100644
--- a/device/geolocation/network_location_provider.cc
+++ b/device/geolocation/network_location_provider.cc
@@ -21,6 +21,11 @@
 // The maximum period of time we'll wait for a complete set of wifi data
 // before sending the request.
 const int kDataCompleteWaitSeconds = 2;
+
+// The maximum age of a cached network location estimate before it can no longer
+// be returned as a fresh estimate. This should be at least as long as the
+// longest polling interval used by the WifiDataProvider.
+const int kLastPositionMaxAgeSeconds = 10 * 60;  // 10 minutes
 }  // namespace
 
 // static
@@ -97,12 +102,14 @@
 // NetworkLocationProvider
 NetworkLocationProvider::NetworkLocationProvider(
     scoped_refptr<net::URLRequestContextGetter> url_context_getter,
-    const std::string& api_key)
+    const std::string& api_key,
+    LastPositionCache* last_position_cache)
     : wifi_data_provider_manager_(nullptr),
       wifi_data_update_callback_(
           base::Bind(&NetworkLocationProvider::OnWifiDataUpdate,
                      base::Unretained(this))),
       is_wifi_data_complete_(false),
+      last_position_delegate_(last_position_cache),
       is_permission_granted_(false),
       is_new_data_available_(false),
       request_(new NetworkLocationRequest(
@@ -111,7 +118,9 @@
           base::Bind(&NetworkLocationProvider::OnLocationResponse,
                      base::Unretained(this)))),
       position_cache_(new PositionCache),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  DCHECK(last_position_delegate_);
+}
 
 NetworkLocationProvider::~NetworkLocationProvider() {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -150,13 +159,15 @@
     const WifiData& wifi_data) {
   DCHECK(thread_checker_.CalledOnValidThread());
   // Record the position and update our cache.
-  position_ = position;
+  last_position_delegate_->SetLastNetworkPosition(position);
   if (ValidateGeoposition(position))
     position_cache_->CachePosition(wifi_data, position);
 
   // Let listeners know that we now have a position available.
-  if (!location_provider_update_callback_.is_null())
-    location_provider_update_callback_.Run(this, position_);
+  if (!location_provider_update_callback_.is_null()) {
+    location_provider_update_callback_.Run(
+        this, last_position_delegate_->GetLastNetworkPosition());
+  }
 }
 
 void NetworkLocationProvider::StartProvider(bool high_accuracy) {
@@ -188,12 +199,36 @@
 }
 
 const mojom::Geoposition& NetworkLocationProvider::GetPosition() {
-  return position_;
+  return last_position_delegate_->GetLastNetworkPosition();
 }
 
 void NetworkLocationProvider::RequestPosition() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  // The wifi polling policy may require us to wait for several minutes before
+  // fresh wifi data is available. To ensure we can return a position estimate
+  // quickly when the network location provider is the primary provider, allow
+  // a cached value to be returned under certain conditions.
+  //
+  // If we have a sufficiently recent network location estimate and we do not
+  // expect to receive a new one soon (i.e., no new wifi data is available and
+  // there is no pending network request), report the last network position
+  // estimate as if it were a fresh estimate.
+  const mojom::Geoposition& last_position =
+      last_position_delegate_->GetLastNetworkPosition();
+  if (!is_new_data_available_ && !request_->is_request_pending() &&
+      ValidateGeoposition(last_position)) {
+    base::Time now = base::Time::Now();
+    base::TimeDelta last_position_age = now - last_position.timestamp;
+    if (last_position_age.InSeconds() < kLastPositionMaxAgeSeconds &&
+        !location_provider_update_callback_.is_null()) {
+      // Update the timestamp to the current time.
+      mojom::Geoposition position = last_position;
+      position.timestamp = now;
+      location_provider_update_callback_.Run(this, position);
+    }
+  }
+
   if (!is_new_data_available_ || !is_wifi_data_complete_)
     return;
   DCHECK(!wifi_timestamp_.is_null())
@@ -202,19 +237,20 @@
   const mojom::Geoposition* cached_position =
       position_cache_->FindPosition(wifi_data_);
   if (cached_position) {
-    DCHECK(ValidateGeoposition(*cached_position));
-    // Record the position and update its timestamp.
-    position_ = *cached_position;
-
+    mojom::Geoposition position(*cached_position);
+    DCHECK(ValidateGeoposition(position));
     // The timestamp of a position fix is determined by the timestamp
-    // of the source data update. (The value of position_.timestamp from
+    // of the source data update. (The value of position.timestamp from
     // the cache could be from weeks ago!)
-    position_.timestamp = wifi_timestamp_;
+    position.timestamp = wifi_timestamp_;
     is_new_data_available_ = false;
 
+    // Record the position.
+    last_position_delegate_->SetLastNetworkPosition(position);
+
     // Let listeners know that we now have a position available.
     if (!location_provider_update_callback_.is_null())
-      location_provider_update_callback_.Run(this, position_);
+      location_provider_update_callback_.Run(this, position);
     return;
   }
   // Don't send network requests until authorized. http://crbug.com/39171
diff --git a/device/geolocation/network_location_provider.h b/device/geolocation/network_location_provider.h
index 59c1cd4..45058788 100644
--- a/device/geolocation/network_location_provider.h
+++ b/device/geolocation/network_location_provider.h
@@ -27,7 +27,19 @@
 
 class NetworkLocationProvider : public LocationProvider {
  public:
-  // Cache of recently resolved locations. Public for tests.
+  // To ensure the last-used position estimate can be preserved when the network
+  // location provider is torn down, a delegate manages the state of the cached
+  // position estimate outside of this provider.
+  class LastPositionCache {
+   public:
+    virtual ~LastPositionCache() = default;
+    virtual void SetLastNetworkPosition(
+        const mojom::Geoposition& new_position) = 0;
+    virtual const mojom::Geoposition& GetLastNetworkPosition() = 0;
+  };
+
+  // Cache of recently resolved locations, keyed by the set of unique WiFi APs
+  // used in the network query. Public for tests.
   class DEVICE_GEOLOCATION_EXPORT PositionCache {
    public:
     // The maximum size of the cache of positions.
@@ -64,7 +76,8 @@
 
   DEVICE_GEOLOCATION_EXPORT NetworkLocationProvider(
       scoped_refptr<net::URLRequestContextGetter> context,
-      const std::string& api_key);
+      const std::string& api_key,
+      LastPositionCache* last_position_cache);
   ~NetworkLocationProvider() override;
 
   // LocationProvider implementation
@@ -101,8 +114,9 @@
   // The timestamp for the latest wifi data update.
   base::Time wifi_timestamp_;
 
-  // The current best position estimate.
-  mojom::Geoposition position_;
+  // A delegate to manage the current best network position estimate. Must not
+  // be nullptr.
+  LastPositionCache* const last_position_delegate_;
 
   LocationProvider::LocationProviderUpdateCallback
       location_provider_update_callback_;
diff --git a/device/geolocation/network_location_provider_unittest.cc b/device/geolocation/network_location_provider_unittest.cc
index ed62e571..56f8832 100644
--- a/device/geolocation/network_location_provider_unittest.cc
+++ b/device/geolocation/network_location_provider_unittest.cc
@@ -8,13 +8,11 @@
 
 #include <memory>
 #include <string>
-#include <utility>
 #include <vector>
 
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -31,23 +29,22 @@
 
 namespace device {
 
-// Stops the specified (nested) message loop when the listener is called back.
-class MessageLoopQuitListener {
- public:
-  MessageLoopQuitListener()
-      : client_message_loop_(base::MessageLoop::current()),
-        updated_provider_(nullptr) {
-    CHECK(client_message_loop_);
-  }
+// Records the most recent position update and counts the number of times
+// OnLocationUpdate is called.
+struct LocationUpdateListener {
+  LocationUpdateListener()
+      : callback(base::BindRepeating(&LocationUpdateListener::OnLocationUpdate,
+                                     base::Unretained(this))) {}
 
   void OnLocationUpdate(const LocationProvider* provider,
                         const mojom::Geoposition& position) {
-    EXPECT_EQ(client_message_loop_, base::MessageLoop::current());
-    updated_provider_ = provider;
+    last_position = position;
+    update_count++;
   }
 
-  base::MessageLoop* client_message_loop_;
-  const LocationProvider* updated_provider_;
+  const LocationProvider::LocationProviderUpdateCallback callback;
+  mojom::Geoposition last_position;
+  int update_count = 0;
 };
 
 // A mock implementation of WifiDataProvider for testing. Adapted from
@@ -105,6 +102,21 @@
   DISALLOW_COPY_AND_ASSIGN(MockWifiDataProvider);
 };
 
+// An implementation of LastPositionCache.
+class TestLastPositionCache
+    : public NetworkLocationProvider::LastPositionCache {
+ public:
+  void SetLastNetworkPosition(const mojom::Geoposition& position) override {
+    last_network_position_ = position;
+  }
+  const mojom::Geoposition& GetLastNetworkPosition() override {
+    return last_network_position_;
+  }
+
+ private:
+  mojom::Geoposition last_network_position_;
+};
+
 MockWifiDataProvider* MockWifiDataProvider::instance_ = nullptr;
 
 // Main test fixture
@@ -118,7 +130,8 @@
                                    const std::string& api_key = std::string()) {
     // No URLContextGetter needed: The request within the provider is tested
     // directly using TestURLFetcherFactory.
-    LocationProvider* provider = new NetworkLocationProvider(nullptr, api_key);
+    LocationProvider* provider = new NetworkLocationProvider(
+        nullptr, api_key, last_position_cache_.get());
     if (set_permission_granted)
       provider->OnPermissionGranted();
     return provider;
@@ -126,7 +139,8 @@
 
  protected:
   GeolocationNetworkProviderTest()
-      : wifi_data_provider_(MockWifiDataProvider::CreateInstance()) {
+      : wifi_data_provider_(MockWifiDataProvider::CreateInstance()),
+        last_position_cache_(std::make_unique<TestLastPositionCache>()) {
     // TODO(joth): Really these should be in SetUp, not here, but they take no
     // effect on Mac OS Release builds if done there. I kid not. Figure out why.
     WifiDataProviderManager::SetFactoryForTesting(
@@ -183,6 +197,7 @@
     pos.latitude = id;
     pos.longitude = -(id + 1);
     pos.altitude = 2 * id;
+    pos.accuracy = 3 * id;
     pos.timestamp = base::Time::Now();
     return pos;
   }
@@ -275,6 +290,8 @@
   const base::MessageLoop main_message_loop_;
   const net::TestURLFetcherFactory url_fetcher_factory_;
   const scoped_refptr<MockWifiDataProvider> wifi_data_provider_;
+  std::unique_ptr<NetworkLocationProvider::LastPositionCache>
+      last_position_cache_;
 };
 
 // Tests that fixture members were SetUp correctly.
@@ -443,13 +460,12 @@
 // Tests that, if no Wifi scan data is available at startup, the provider
 // doesn't initiate a request, until Wifi data later becomes available.
 TEST_F(GeolocationNetworkProviderTest, NoRequestOnStartupUntilWifiData) {
-  MessageLoopQuitListener listener;
+  LocationUpdateListener listener;
   wifi_data_provider_->set_got_data(false);  // No initial Wifi data.
   std::unique_ptr<LocationProvider> provider(CreateProvider(true));
   provider->StartProvider(false);
 
-  provider->SetUpdateCallback(base::Bind(
-      &MessageLoopQuitListener::OnLocationUpdate, base::Unretained(&listener)));
+  provider->SetUpdateCallback(listener.callback);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(get_url_fetcher_and_advance_id())
@@ -542,4 +558,209 @@
   }
 }
 
+// Tests that the provider's last position cache delegate is correctly used to
+// cache the most recent network position estimate, and that this estimate is
+// not lost when the provider is torn down and recreated.
+TEST_F(GeolocationNetworkProviderTest, LastPositionCache) {
+  std::unique_ptr<LocationProvider> provider(CreateProvider(true));
+  provider->StartProvider(false);
+
+  // Check that the provider is initialized with an invalid position.
+  mojom::Geoposition position = provider->GetPosition();
+  EXPECT_FALSE(ValidateGeoposition(position));
+
+  // Check that the cached value is also invalid.
+  position = last_position_cache_->GetLastNetworkPosition();
+  EXPECT_FALSE(ValidateGeoposition(position));
+
+  // Now wifi data arrives -- SetData will notify listeners.
+  const int kFirstScanAps = 6;
+  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
+  base::RunLoop().RunUntilIdle();
+  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
+  ASSERT_TRUE(fetcher);
+  // The request should have the wifi data.
+  CheckRequestIsValid(*fetcher, kFirstScanAps, 0);
+
+  // Send a reply with good position fix.
+  const char* kReferenceNetworkResponse =
+      "{"
+      "  \"accuracy\": 1200.4,"
+      "  \"location\": {"
+      "    \"lat\": 51.0,"
+      "    \"lng\": -0.1"
+      "  }"
+      "}";
+  fetcher->set_status(net::URLRequestStatus());
+  fetcher->set_response_code(200);  // OK
+  fetcher->SetResponseString(kReferenceNetworkResponse);
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+  // The provider should return the position as the current best estimate.
+  position = provider->GetPosition();
+  EXPECT_EQ(51.0, position.latitude);
+  EXPECT_EQ(-0.1, position.longitude);
+  EXPECT_EQ(1200.4, position.accuracy);
+  EXPECT_FALSE(position.timestamp.is_null());
+  EXPECT_TRUE(ValidateGeoposition(position));
+
+  // Shut down the provider. This typically happens whenever there are no active
+  // Geolocation API calls.
+  provider->StopProvider();
+  provider = nullptr;
+
+  // The cache preserves the last estimate while the provider is inactive.
+  position = last_position_cache_->GetLastNetworkPosition();
+  EXPECT_EQ(51.0, position.latitude);
+  EXPECT_EQ(-0.1, position.longitude);
+  EXPECT_EQ(1200.4, position.accuracy);
+  EXPECT_FALSE(position.timestamp.is_null());
+  EXPECT_TRUE(ValidateGeoposition(position));
+
+  // Restart the provider.
+  provider.reset(CreateProvider(true));
+  provider->StartProvider(false);
+
+  // Check that the most recent position estimate is retained.
+  position = provider->GetPosition();
+  EXPECT_EQ(51.0, position.latitude);
+  EXPECT_EQ(-0.1, position.longitude);
+  EXPECT_EQ(1200.4, position.accuracy);
+  EXPECT_FALSE(position.timestamp.is_null());
+  EXPECT_TRUE(ValidateGeoposition(position));
+}
+
+// Tests that when the last network position estimate is sufficiently recent and
+// we do not expect to receive a fresh estimate soon (no new wifi data available
+// and no pending geolocation service request) then the provider may return the
+// last position instead of waiting to acquire a fresh estimate.
+TEST_F(GeolocationNetworkProviderTest, LastPositionCacheUsed) {
+  LocationUpdateListener listener;
+
+  // Seed the last position cache with a valid geoposition value and the
+  // timestamp set to the current time.
+  mojom::Geoposition last_position = CreateReferencePosition(0);
+  EXPECT_TRUE(ValidateGeoposition(last_position));
+  last_position_cache_->SetLastNetworkPosition(last_position);
+
+  // Simulate no initial wifi data.
+  wifi_data_provider_->set_got_data(false);
+
+  // Start the provider without geolocation permission.
+  std::unique_ptr<LocationProvider> provider(CreateProvider(false));
+  provider->StartProvider(false);
+
+  // Register a location update callback. The listener will count how many times
+  // OnLocationUpdate is called.
+  provider->SetUpdateCallback(listener.callback);
+
+  // Under normal circumstances, when there is no initial wifi data
+  // RequestPosition is not called until a few seconds after the provider is
+  // started to allow time for the wifi scan to complete. To avoid waiting,
+  // grant permissions once the provider is running to cause RequestPosition to
+  // be called immediately.
+  provider->OnPermissionGranted();
+
+  base::RunLoop().RunUntilIdle();
+
+  // Check that the listener received the position update and that the position
+  // is the same as the seeded value except for the timestamp, which should be
+  // newer.
+  EXPECT_EQ(1, listener.update_count);
+  EXPECT_TRUE(ValidateGeoposition(listener.last_position));
+  EXPECT_EQ(last_position.latitude, listener.last_position.latitude);
+  EXPECT_EQ(last_position.longitude, listener.last_position.longitude);
+  EXPECT_EQ(last_position.accuracy, listener.last_position.accuracy);
+  EXPECT_LT(last_position.timestamp, listener.last_position.timestamp);
+}
+
+// Tests that the last network position estimate is not returned if the
+// estimate is too old.
+TEST_F(GeolocationNetworkProviderTest, LastPositionNotUsedTooOld) {
+  LocationUpdateListener listener;
+
+  // Seed the last position cache with a geoposition value with the timestamp
+  // set to 20 minutes ago.
+  mojom::Geoposition last_position = CreateReferencePosition(0);
+  last_position.timestamp =
+      base::Time::Now() - base::TimeDelta::FromMinutes(20);
+  EXPECT_TRUE(ValidateGeoposition(last_position));
+  last_position_cache_->SetLastNetworkPosition(last_position);
+
+  // Simulate no initial wifi data.
+  wifi_data_provider_->set_got_data(false);
+
+  // Start the provider without geolocation permission.
+  std::unique_ptr<LocationProvider> provider(CreateProvider(false));
+  provider->StartProvider(false);
+
+  // Register a location update callback. The listener will count how many times
+  // OnLocationUpdate is called.
+  provider->SetUpdateCallback(listener.callback);
+
+  // Under normal circumstances, when there is no initial wifi data
+  // RequestPosition is not called until a few seconds after the provider is
+  // started to allow time for the wifi scan to complete. To avoid waiting,
+  // grant permissions once the provider is running to cause RequestPosition to
+  // be called immediately.
+  provider->OnPermissionGranted();
+
+  base::RunLoop().RunUntilIdle();
+
+  // Check that the listener received no updates.
+  EXPECT_EQ(0, listener.update_count);
+  EXPECT_FALSE(ValidateGeoposition(listener.last_position));
+}
+
+// Tests that the last network position estimate is not returned if there is
+// new wifi data or a pending geolocation service request.
+TEST_F(GeolocationNetworkProviderTest, LastPositionNotUsedNewData) {
+  LocationUpdateListener listener;
+
+  // Seed the last position cache with a valid geoposition value. The timestamp
+  // of the cached position is set to the current time.
+  mojom::Geoposition last_position = CreateReferencePosition(0);
+  EXPECT_TRUE(ValidateGeoposition(last_position));
+  last_position_cache_->SetLastNetworkPosition(last_position);
+
+  // Simulate a completed wifi scan.
+  const int kFirstScanAps = 6;
+  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
+
+  // Create the provider without permissions enabled.
+  std::unique_ptr<LocationProvider> provider(CreateProvider(false));
+
+  // Register a location update callback. The callback will count how many times
+  // OnLocationUpdate is called.
+  provider->SetUpdateCallback(listener.callback);
+
+  // Start the provider.
+  provider->StartProvider(false);
+  base::RunLoop().RunUntilIdle();
+
+  // The listener should not receive any updates. There is a valid cached value
+  // but it should not be sent while we have pending wifi data.
+  EXPECT_EQ(0, listener.update_count);
+  EXPECT_FALSE(ValidateGeoposition(listener.last_position));
+
+  // Check that there is no pending network request.
+  EXPECT_FALSE(get_url_fetcher_and_advance_id());
+
+  // Simulate no new wifi data.
+  wifi_data_provider_->set_got_data(false);
+
+  // Grant permission to allow the network request to proceed.
+  provider->OnPermissionGranted();
+  base::RunLoop().RunUntilIdle();
+
+  // The listener should still not receive any updates. There is a valid cached
+  // value and no new wifi data, but the cached value should not be sent while
+  // we have a pending request to the geolocation service.
+  EXPECT_EQ(0, listener.update_count);
+  EXPECT_FALSE(ValidateGeoposition(listener.last_position));
+
+  // Check that a network request is pending.
+  EXPECT_TRUE(get_url_fetcher_and_advance_id());
+}
+
 }  // namespace device
diff --git a/device/geolocation/wifi_data_provider_common.cc b/device/geolocation/wifi_data_provider_common.cc
index 615b53c..64102d56 100644
--- a/device/geolocation/wifi_data_provider_common.cc
+++ b/device/geolocation/wifi_data_provider_common.cc
@@ -35,18 +35,15 @@
     return;
   }
 
-  DCHECK(!polling_policy_);
-  polling_policy_ = CreatePollingPolicy();
-  DCHECK(polling_policy_);
+  if (!WifiPollingPolicy::IsInitialized())
+    WifiPollingPolicy::Initialize(CreatePollingPolicy());
+  DCHECK(WifiPollingPolicy::IsInitialized());
 
-  // Perform first scan ASAP regardless of the polling policy. If this scan
-  // fails we'll retry at a rate in line with the polling policy.
-  ScheduleNextScan(0);
+  ScheduleNextScan(WifiPollingPolicy::Get()->InitialInterval());
 }
 
 void WifiDataProviderCommon::StopDataProvider() {
   wlan_api_.reset();
-  polling_policy_.reset();
 }
 
 bool WifiDataProviderCommon::GetData(WifiData* data) {
@@ -64,12 +61,12 @@
   bool update_available = false;
   WifiData new_data;
   if (!wlan_api_->GetAccessPointData(&new_data.access_point_data)) {
-    ScheduleNextScan(polling_policy_->NoWifiInterval());
+    ScheduleNextScan(WifiPollingPolicy::Get()->NoWifiInterval());
   } else {
     update_available = wifi_data_.DiffersSignificantly(new_data);
     wifi_data_ = new_data;
-    polling_policy_->UpdatePollingInterval(update_available);
-    ScheduleNextScan(polling_policy_->PollingInterval());
+    WifiPollingPolicy::Get()->UpdatePollingInterval(update_available);
+    ScheduleNextScan(WifiPollingPolicy::Get()->PollingInterval());
   }
   if (update_available || !is_first_scan_complete_) {
     is_first_scan_complete_ = true;
diff --git a/device/geolocation/wifi_data_provider_common.h b/device/geolocation/wifi_data_provider_common.h
index 44a312c..ad39a54e 100644
--- a/device/geolocation/wifi_data_provider_common.h
+++ b/device/geolocation/wifi_data_provider_common.h
@@ -72,9 +72,6 @@
   // Underlying OS wifi API.
   std::unique_ptr<WlanApiInterface> wlan_api_;
 
-  // Controls the polling update interval.
-  std::unique_ptr<WifiPollingPolicy> polling_policy_;
-
   // Holder for delayed tasks; takes care of cleanup.
   base::WeakPtrFactory<WifiDataProviderCommon> weak_factory_;
 
diff --git a/device/geolocation/wifi_data_provider_common_unittest.cc b/device/geolocation/wifi_data_provider_common_unittest.cc
index a1cbb62..5f126ee 100644
--- a/device/geolocation/wifi_data_provider_common_unittest.cc
+++ b/device/geolocation/wifi_data_provider_common_unittest.cc
@@ -46,6 +46,7 @@
 class MockPollingPolicy : public WifiPollingPolicy {
  public:
   MockPollingPolicy() {
+    ON_CALL(*this, InitialInterval()).WillByDefault(Return(0));
     ON_CALL(*this, PollingInterval()).WillByDefault(Return(1));
     ON_CALL(*this, NoWifiInterval()).WillByDefault(Return(1));
     // We are not interested in calls to UpdatePollingInterval() method.
@@ -54,25 +55,28 @@
 
   // WifiPollingPolicy implementation.
   MOCK_METHOD1(UpdatePollingInterval, void(bool));
+  MOCK_METHOD0(InitialInterval, int());
   MOCK_METHOD0(PollingInterval, int());
   MOCK_METHOD0(NoWifiInterval, int());
 };
 
 class WifiDataProviderCommonWithMock : public WifiDataProviderCommon {
  public:
-  WifiDataProviderCommonWithMock()
-      : wlan_api_(new MockWlanApi), polling_policy_(new MockPollingPolicy) {}
+  WifiDataProviderCommonWithMock() : wlan_api_(new MockWlanApi) {}
 
   // WifiDataProviderCommon
   std::unique_ptr<WlanApiInterface> CreateWlanApi() override {
     return std::move(wlan_api_);
   }
   std::unique_ptr<WifiPollingPolicy> CreatePollingPolicy() override {
-    return std::move(polling_policy_);
+    auto policy = std::make_unique<MockPollingPolicy>();
+    // Save a pointer to the MockPollingPolicy.
+    polling_policy_ = policy.get();
+    return std::move(policy);
   }
 
   std::unique_ptr<MockWlanApi> wlan_api_;
-  std::unique_ptr<MockPollingPolicy> polling_policy_;
+  MockPollingPolicy* polling_policy_ = nullptr;
 
  private:
   ~WifiDataProviderCommonWithMock() override = default;
@@ -88,16 +92,22 @@
             base::test::ScopedTaskEnvironment::MainThreadType::UI),
         wifi_data_callback_(base::DoNothing()),
         provider_(new WifiDataProviderCommonWithMock),
-        wlan_api_(provider_->wlan_api_.get()),
-        polling_policy_(provider_->polling_policy_.get()) {}
+        wlan_api_(provider_->wlan_api_.get()) {}
 
   void SetUp() override {
+    // Initialize WifiPollingPolicy early so we can watch for calls to mocked
+    // functions.
+    WifiPollingPolicy::Initialize(provider_->CreatePollingPolicy());
+    if (WifiPollingPolicy::IsInitialized())
+      polling_policy_ = provider_->polling_policy_;
+
     provider_->AddCallback(&wifi_data_callback_);
   }
 
   void TearDown() override {
     provider_->RemoveCallback(&wifi_data_callback_);
     provider_->StopDataProvider();
+    WifiPollingPolicy::Shutdown();
   }
 
  protected:
@@ -106,7 +116,7 @@
   const scoped_refptr<WifiDataProviderCommonWithMock> provider_;
 
   MockWlanApi* const wlan_api_;
-  MockPollingPolicy* const polling_policy_;
+  MockPollingPolicy* polling_policy_ = nullptr;
 };
 
 TEST_F(GeolocationWifiDataProviderCommonTest, CreateDestroy) {
@@ -118,6 +128,7 @@
 
 TEST_F(GeolocationWifiDataProviderCommonTest, NoWifi) {
   base::RunLoop run_loop;
+  EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
   EXPECT_CALL(*polling_policy_, NoWifiInterval()).Times(AtLeast(1));
   EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
       .WillOnce(InvokeWithoutArgs([&run_loop]() {
@@ -131,6 +142,7 @@
 
 TEST_F(GeolocationWifiDataProviderCommonTest, IntermittentWifi) {
   base::RunLoop run_loop;
+  EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
   EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1));
   EXPECT_CALL(*polling_policy_, NoWifiInterval()).Times(1);
   EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
@@ -149,6 +161,7 @@
 TEST_F(GeolocationWifiDataProviderCommonTest, DoAnEmptyScan) {
   base::RunLoop run_loop;
 
+  EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
   EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1));
   EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
       .WillOnce(InvokeWithoutArgs([&run_loop]() {
@@ -169,6 +182,7 @@
 TEST_F(GeolocationWifiDataProviderCommonTest, DoScanWithResults) {
   base::RunLoop run_loop;
 
+  EXPECT_CALL(*polling_policy_, InitialInterval()).Times(1);
   EXPECT_CALL(*polling_policy_, PollingInterval()).Times(AtLeast(1));
   AccessPointData single_access_point;
   single_access_point.channel = 2;
diff --git a/device/geolocation/wifi_polling_policy.cc b/device/geolocation/wifi_polling_policy.cc
new file mode 100644
index 0000000..8c3b3f53
--- /dev/null
+++ b/device/geolocation/wifi_polling_policy.cc
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/geolocation/wifi_polling_policy.h"
+
+namespace device {
+
+namespace {
+WifiPollingPolicy* g_wifi_polling_policy;
+}  // namespace
+
+// static
+void WifiPollingPolicy::Initialize(std::unique_ptr<WifiPollingPolicy> policy) {
+  DCHECK(!g_wifi_polling_policy);
+  g_wifi_polling_policy = policy.release();
+}
+
+// static
+void WifiPollingPolicy::Shutdown() {
+  if (g_wifi_polling_policy)
+    delete g_wifi_polling_policy;
+  g_wifi_polling_policy = nullptr;
+}
+
+// static
+WifiPollingPolicy* WifiPollingPolicy::Get() {
+  DCHECK(g_wifi_polling_policy);
+  return g_wifi_polling_policy;
+}
+
+// static
+bool WifiPollingPolicy::IsInitialized() {
+  return g_wifi_polling_policy != nullptr;
+}
+
+}  // namespace device
diff --git a/device/geolocation/wifi_polling_policy.h b/device/geolocation/wifi_polling_policy.h
index 0ac9203..8463f3e 100644
--- a/device/geolocation/wifi_polling_policy.h
+++ b/device/geolocation/wifi_polling_policy.h
@@ -5,21 +5,54 @@
 #ifndef DEVICE_GEOLOCATION_WIFI_POLLING_POLICY_H_
 #define DEVICE_GEOLOCATION_WIFI_POLLING_POLICY_H_
 
+#include <memory>
+
 #include "base/macros.h"
+#include "base/time/time.h"
+#include "device/geolocation/geolocation_export.h"
 
 namespace device {
 
 // Allows sharing and mocking of the update polling policy function.
-class WifiPollingPolicy {
+class DEVICE_GEOLOCATION_EXPORT WifiPollingPolicy {
  public:
-  WifiPollingPolicy() {}
-  virtual ~WifiPollingPolicy() {}
-  // Calculates the new polling interval for wiFi scans, given the previous
+  virtual ~WifiPollingPolicy() = default;
+
+  // Methods for managing the single instance of WifiPollingPolicy. The WiFi
+  // policy is global so it can outlive the WifiDataProvider instance, which is
+  // shut down and destroyed when no WiFi scanning is active.
+  static void Initialize(std::unique_ptr<WifiPollingPolicy>);
+  static void Shutdown();
+  static WifiPollingPolicy* Get();
+  static bool IsInitialized();
+
+  // Calculates the new polling interval for wifi scans, given the previous
   // interval and whether the last scan produced new results.
   virtual void UpdatePollingInterval(bool scan_results_differ) = 0;
+
+  // Use InitialInterval to schedule the initial scan when the wifi data
+  // provider is first started. Returns the number of milliseconds before the
+  // initial scan should be performed. May return zero if the policy allows a
+  // scan to be performed immediately.
+  virtual int InitialInterval() = 0;
+
+  // Use PollingInterval to schedule a new scan after the previous scan results
+  // are available. Only use PollingInterval if WLAN hardware is available and
+  // can perform scans for nearby access points. If the current interval is
+  // complete, PollingInterval returns the duration for a new interval starting
+  // at the current time.
   virtual int PollingInterval() = 0;
+
+  // Use NoWifiInterval to schedule a new scan after the previous scan results
+  // are available. NoWifiInterval is typically shorter than PollingInterval
+  // and should not be used if wifi scanning is available in order to conserve
+  // power. If the current interval is complete, NoWifiInterval returns the
+  // duration for a new interval starting at the current time.
   virtual int NoWifiInterval() = 0;
 
+ protected:
+  WifiPollingPolicy() = default;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(WifiPollingPolicy);
 };
@@ -32,7 +65,8 @@
           int NO_WIFI_INTERVAL>
 class GenericWifiPollingPolicy : public WifiPollingPolicy {
  public:
-  GenericWifiPollingPolicy() : polling_interval_(DEFAULT_INTERVAL) {}
+  GenericWifiPollingPolicy() = default;
+
   // WifiPollingPolicy
   void UpdatePollingInterval(bool scan_results_differ) override {
     if (scan_results_differ) {
@@ -45,11 +79,57 @@
       polling_interval_ = TWO_NO_CHANGE_INTERVAL;
     }
   }
-  int PollingInterval() override { return polling_interval_; }
-  int NoWifiInterval() override { return NO_WIFI_INTERVAL; }
+  int InitialInterval() override { return ComputeInterval(polling_interval_); }
+  int PollingInterval() override {
+    int interval = ComputeInterval(polling_interval_);
+    return interval <= 0 ? polling_interval_ : interval;
+  }
+  int NoWifiInterval() override {
+    int interval = ComputeInterval(NO_WIFI_INTERVAL);
+    return interval <= 0 ? NO_WIFI_INTERVAL : interval;
+  }
 
  private:
-  int polling_interval_;
+  int ComputeInterval(int polling_interval) {
+    base::Time now = base::Time::Now();
+
+    int64_t remaining_millis = 0;
+    if (!interval_start_.is_null()) {
+      // If the new interval duration differs from the initial duration, use the
+      // shorter duration.
+      if (polling_interval < interval_duration_)
+        interval_duration_ = polling_interval;
+
+      // Compute the remaining duration of the current interval. If the interval
+      // is not yet complete, we will schedule a scan to occur once it is.
+      base::TimeDelta remaining =
+          interval_start_ +
+          base::TimeDelta::FromMilliseconds(interval_duration_) - now;
+      remaining_millis = remaining.InMilliseconds();
+    }
+
+    // If the current interval is complete (or if this is our first scan),
+    // start a new interval beginning now.
+    if (remaining_millis <= 0) {
+      interval_start_ = now;
+      interval_duration_ = polling_interval;
+      remaining_millis = 0;
+    }
+
+    return remaining_millis;
+  }
+
+  // The current duration of the polling interval. When wifi data is
+  // substantially the same from one scan to the next, this may be increased to
+  // reduce the frequency of wifi scanning.
+  int polling_interval_ = DEFAULT_INTERVAL;
+
+  // The start time for the most recent interval. Initialized to the "null" time
+  // value.
+  base::Time interval_start_;
+
+  // Duration for the interval starting at |interval_start_|.
+  int interval_duration_ = DEFAULT_INTERVAL;
 };
 
 }  // namespace device
diff --git a/device/geolocation/wifi_polling_policy_unittest.cc b/device/geolocation/wifi_polling_policy_unittest.cc
new file mode 100644
index 0000000..18c364f
--- /dev/null
+++ b/device/geolocation/wifi_polling_policy_unittest.cc
@@ -0,0 +1,145 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/geolocation/wifi_polling_policy.h"
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+namespace {
+
+const int kDefaultIntervalMillis = 200;
+const int kNoChangeIntervalMillis = 300;
+const int kTwoNoChangeIntervalMillis = 400;
+const int kNoWifiIntervalMillis = 100;
+
+}  // namespace
+
+// Main test fixture
+class GeolocationWifiPollingPolicyTest : public testing::Test {
+ public:
+  void SetUp() override {
+    WifiPollingPolicy::Initialize(
+        std::make_unique<GenericWifiPollingPolicy<
+            kDefaultIntervalMillis, kNoChangeIntervalMillis,
+            kTwoNoChangeIntervalMillis, kNoWifiIntervalMillis>>());
+    polling_policy_ = WifiPollingPolicy::Get();
+  }
+
+  void TearDown() override {
+    polling_policy_ = nullptr;
+    WifiPollingPolicy::Shutdown();
+  }
+
+ protected:
+  WifiPollingPolicy* polling_policy_ = nullptr;
+};
+
+TEST_F(GeolocationWifiPollingPolicyTest, CreateDestroy) {
+  // Test fixture members were SetUp correctly.
+  EXPECT_TRUE(polling_policy_);
+}
+
+// Tests that the InitialInterval is zero when the policy is first created.
+TEST_F(GeolocationWifiPollingPolicyTest, InitialIntervalZero) {
+  // The first call should return zero, indicating we may scan immediately.
+  // Internally, the policy starts a new interval at the current time.
+  EXPECT_EQ(0, polling_policy_->InitialInterval());
+
+  // The second call should return the non-zero remainder of the interval
+  // created by the first call.
+  int interval = polling_policy_->InitialInterval();
+  EXPECT_GT(interval, 0);
+  EXPECT_LE(interval, kDefaultIntervalMillis);
+}
+
+// Tests that the PollingInterval is equal to the default polling interval when
+// the policy is first created.
+TEST_F(GeolocationWifiPollingPolicyTest, PollingIntervalNonZero) {
+  // PollingInterval assumes it is only called immediately following a wifi
+  // scan. The first call should start a new interval at the current time and
+  // return the full duration of the new interval.
+  EXPECT_EQ(kDefaultIntervalMillis, polling_policy_->PollingInterval());
+
+  // The second call should return the non-zero remainder of the interval
+  // created by the first call.
+  int interval = polling_policy_->PollingInterval();
+  EXPECT_GT(interval, 0);
+  EXPECT_LE(interval, kDefaultIntervalMillis);
+}
+
+// Tests that the NoWifiInterval is equal to the default no-wifi interval when
+// the policy is first created.
+TEST_F(GeolocationWifiPollingPolicyTest, NoWifiIntervalNonZero) {
+  // NoWifiInterval assumes it is only called immediately following a failed
+  // attempt at a wifi scan. The first call should start a new interval at the
+  // current time and return the full duration of the new interval.
+  EXPECT_EQ(kNoWifiIntervalMillis, polling_policy_->NoWifiInterval());
+
+  // The second call should return the non-zero remainder of the interval
+  // created by the first call.
+  int interval = polling_policy_->NoWifiInterval();
+  EXPECT_GT(interval, 0);
+  EXPECT_LE(interval, kNoWifiIntervalMillis);
+}
+
+// Calls UpdatePollingInterval once with unchanged scan results. Verifies that
+// the no-change interval is used.
+TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalOnce) {
+  polling_policy_->UpdatePollingInterval(false);
+  EXPECT_EQ(kNoChangeIntervalMillis, polling_policy_->PollingInterval());
+}
+
+// Calls UpdatePollingInterval twice with unchanged scan results. Verifies that
+// the two-no-change interval is used.
+TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalTwice) {
+  polling_policy_->UpdatePollingInterval(false);
+  polling_policy_->UpdatePollingInterval(false);
+  EXPECT_EQ(kTwoNoChangeIntervalMillis, polling_policy_->PollingInterval());
+}
+
+// Calls UpdatePollingInterval three times with unchanged scan results. This
+// should have the same effect as calling it twice.
+TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalThrice) {
+  polling_policy_->UpdatePollingInterval(false);
+  polling_policy_->UpdatePollingInterval(false);
+  polling_policy_->UpdatePollingInterval(false);
+  EXPECT_EQ(kTwoNoChangeIntervalMillis, polling_policy_->PollingInterval());
+}
+
+// Calls UpdatePollingInterval twice with unchanged scan results and then once
+// with differing results. Verifies that the default interval is used.
+TEST_F(GeolocationWifiPollingPolicyTest, UpdatePollingIntervalResultsDiffer) {
+  polling_policy_->UpdatePollingInterval(false);
+  polling_policy_->UpdatePollingInterval(false);
+  polling_policy_->UpdatePollingInterval(true);
+  EXPECT_EQ(kDefaultIntervalMillis, polling_policy_->PollingInterval());
+}
+
+TEST_F(GeolocationWifiPollingPolicyTest, ShorterInterval) {
+  // Ask for a polling interval.
+  EXPECT_EQ(kDefaultIntervalMillis, polling_policy_->PollingInterval());
+
+  // Now ask for a no-wifi interval, which is shorter. The returned interval
+  // must be no longer than the shorter of the two intervals.
+  int interval = polling_policy_->NoWifiInterval();
+  EXPECT_GT(interval, 0);
+  EXPECT_LE(interval, kNoWifiIntervalMillis);
+}
+
+TEST_F(GeolocationWifiPollingPolicyTest, LongerInterval) {
+  // Ask for a no-wifi interval.
+  EXPECT_EQ(kNoWifiIntervalMillis, polling_policy_->NoWifiInterval());
+
+  // Now ask for a polling interval, which is longer. The returned interval
+  // must be no longer than the shorter of the two intervals.
+  int interval = polling_policy_->PollingInterval();
+  EXPECT_GT(interval, 0);
+  EXPECT_LE(interval, kNoWifiIntervalMillis);
+}
+
+}  // namespace device
diff --git a/device/vr/orientation/orientation_device_unittest.cc b/device/vr/orientation/orientation_device_unittest.cc
index d360d89d..4bbde60 100644
--- a/device/vr/orientation/orientation_device_unittest.cc
+++ b/device/vr/orientation/orientation_device_unittest.cc
@@ -22,10 +22,13 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/display/test/scoped_screen_override.h"
 #include "ui/gfx/geometry/quaternion.h"
 
 namespace device {
 
+using display::test::ScopedScreenOverride;
+
 namespace {
 
 class FakeScreen : public display::Screen {
@@ -88,7 +91,8 @@
 
     fake_screen_ = std::make_unique<FakeScreen>();
 
-    display::Screen::SetScreenInstance(fake_screen_.get());
+    scoped_screen_override_ =
+        std::make_unique<ScopedScreenOverride>(fake_screen_.get());
 
     scoped_task_environment_.RunUntilIdle();
   }
@@ -198,6 +202,7 @@
   mojom::SensorClientPtr sensor_client_ptr_;
 
   std::unique_ptr<FakeScreen> fake_screen_;
+  std::unique_ptr<ScopedScreenOverride> scoped_screen_override_;
 
   DISALLOW_COPY_AND_ASSIGN(VROrientationDeviceTest);
 };
diff --git a/docs/memory/tools.md b/docs/memory/tools.md
index e1f923aa..8b3d2c0 100644
--- a/docs/memory/tools.md
+++ b/docs/memory/tools.md
@@ -123,7 +123,7 @@
 Heap dumps provide extremely detailed data about object allocations and is
 useful for finding code locations that are generating a large number of live
 allocations. Data is tracked and recorded using the [Out-of-process Heap
-Profiler (OOPHP)](../../src/chrome/profiling/README.md).
+Profiler (OOPHP)](../../src/components/services/heap_profiling/README.md).
 
 For the Browser and GPU process, this often quickly finds objects that leak over
 time.
@@ -138,10 +138,11 @@
     `VirtualAlloc()`) will not be tracked.
   * Utility processes are currently not profiled.
   * Allocations are only recorded after the
-    [ProfilingService](../../src/chrome/profiling/profiling_service.h) has spun up the
-    profiling process and created a connection to the target process. The ProfilingService
-    is a mojo service that can be configured to start early in browser startup
-    but it still takes time to spin up and early allocations are thus lost.
+    [HeapProfilingService](../../src/components/services/heap_profiling/heap_profiling_service.h)
+    has spun up the profiling process and created a connection to the target
+    process. The HeapProfilingService is a mojo service that can be configured to
+    start early in browser startup but it still takes time to spin up and early
+    allocations are thus lost.
 
 ### Instructions
 #### <a name="configure-oophp"></a>Configuration and setup
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 92386f4..e85606eb 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -467,6 +467,7 @@
     "//services/device/public/cpp/hid",
     "//services/device/public/mojom",
     "//services/service_manager/public/cpp",
+    "//ui/display:test_support",
   ]
 
   if (is_mac) {
diff --git a/extensions/browser/api/declarative/declarative_api.cc b/extensions/browser/api/declarative/declarative_api.cc
index 6ca2658d..6614174 100644
--- a/extensions/browser/api/declarative/declarative_api.cc
+++ b/extensions/browser/api/declarative/declarative_api.cc
@@ -139,29 +139,23 @@
 
 RulesFunction::~RulesFunction() {}
 
-bool RulesFunction::HasPermission() {
+ExtensionFunction::ResponseAction RulesFunction::Run() {
+  EXTENSION_FUNCTION_VALIDATE(CreateParams());
+
   std::string event_name;
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name));
+
   int web_view_instance_id = 0;
   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &web_view_instance_id));
 
   // <webview> embedders use the declarativeWebRequest API via
   // <webview>.onRequest.
-  if (web_view_instance_id && extension_->permissions_data()->HasAPIPermission(
+  if (web_view_instance_id && !extension_->permissions_data()->HasAPIPermission(
                                   APIPermission::kWebView)) {
-    return true;
+    return RespondNow(Error("Missing webview permission"));
   }
-  return ExtensionFunction::HasPermission();
-}
 
-bool RulesFunction::RunAsync() {
-  std::string event_name;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name));
-
-  int web_view_instance_id = 0;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &web_view_instance_id));
   int embedder_process_id = render_frame_host()->GetProcess()->GetID();
-
   RecordUMA(event_name);
 
   bool from_web_view = web_view_instance_id != 0;
@@ -189,43 +183,50 @@
   // there should never be a request for a nonexisting rules registry.
   EXTENSION_FUNCTION_VALIDATE(rules_registry_.get());
 
-  if (content::BrowserThread::CurrentlyOn(rules_registry_->owner_thread())) {
-    bool success = RunAsyncOnCorrectThread();
-    SendResponse(success);
-  } else {
-    scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner =
-        content::BrowserThread::GetTaskRunnerForThread(
-            rules_registry_->owner_thread());
-    base::PostTaskAndReplyWithResult(
-        thread_task_runner.get(), FROM_HERE,
-        base::Bind(&RulesFunction::RunAsyncOnCorrectThread, this),
-        base::Bind(&RulesFunction::SendResponse, this));
-  }
+  if (content::BrowserThread::CurrentlyOn(rules_registry_->owner_thread()))
+    return RespondNow(RunAsyncOnCorrectThread());
 
-  return true;
+  scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner =
+      content::BrowserThread::GetTaskRunnerForThread(
+          rules_registry_->owner_thread());
+  base::PostTaskAndReplyWithResult(
+      thread_task_runner.get(), FROM_HERE,
+      base::BindOnce(&RulesFunction::RunAsyncOnCorrectThread, this),
+      base::BindOnce(&RulesFunction::SendResponse, this));
+  return RespondLater();
 }
 
-bool EventsEventAddRulesFunction::RunAsyncOnCorrectThread() {
+void RulesFunction::SendResponse(ResponseValue response) {
+  Respond(std::move(response));
+}
+
+EventsEventAddRulesFunction::EventsEventAddRulesFunction() = default;
+
+EventsEventAddRulesFunction::~EventsEventAddRulesFunction() = default;
+
+bool EventsEventAddRulesFunction::CreateParams() {
+  params_ = AddRules::Params::Create(*args_);
+  return params_ != nullptr;
+}
+
+ExtensionFunction::ResponseValue
+EventsEventAddRulesFunction::RunAsyncOnCorrectThread() {
   ConvertBinaryListElementsToBase64(args_.get());
-  std::unique_ptr<AddRules::Params> params(AddRules::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
 
   // TODO(devlin): Remove the dependency on linked_ptr here.
   std::vector<linked_ptr<api::events::Rule>> linked_rules;
-  for (api::events::Rule& rule : params->rules) {
+  for (api::events::Rule& rule : params_->rules) {
     linked_rules.push_back(
         make_linked_ptr(new api::events::Rule(std::move(rule))));
   }
-  error_ = rules_registry_->AddRules(extension_id(), linked_rules);
+  std::string error = rules_registry_->AddRules(extension_id(), linked_rules);
+  if (!error.empty())
+    return Error(error);
 
-  if (error_.empty()) {
-    std::unique_ptr<base::ListValue> rules_value(new base::ListValue());
-    for (const auto& rule : linked_rules)
-      rules_value->Append(rule->ToValue());
-    SetResult(std::move(rules_value));
-  }
-
-  return error_.empty();
+  auto rules_value = std::make_unique<base::ListValue>();
+  for (const auto& rule : linked_rules)
+    rules_value->Append(rule->ToValue());
+  return OneArgument(std::move(rules_value));
 }
 
 void EventsEventAddRulesFunction::RecordUMA(
@@ -248,19 +249,26 @@
   RecordUMAHelper(type);
 }
 
-bool EventsEventRemoveRulesFunction::RunAsyncOnCorrectThread() {
-  std::unique_ptr<RemoveRules::Params> params(
-      RemoveRules::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
+EventsEventRemoveRulesFunction::EventsEventRemoveRulesFunction() = default;
 
-  if (params->rule_identifiers.get()) {
-    error_ = rules_registry_->RemoveRules(extension_id(),
-                                          *params->rule_identifiers);
+EventsEventRemoveRulesFunction::~EventsEventRemoveRulesFunction() = default;
+
+bool EventsEventRemoveRulesFunction::CreateParams() {
+  params_ = RemoveRules::Params::Create(*args_);
+  return params_ != nullptr;
+}
+
+ExtensionFunction::ResponseValue
+EventsEventRemoveRulesFunction::RunAsyncOnCorrectThread() {
+  std::string error;
+  if (params_->rule_identifiers.get()) {
+    error = rules_registry_->RemoveRules(extension_id(),
+                                         *params_->rule_identifiers);
   } else {
-    error_ = rules_registry_->RemoveAllRules(extension_id());
+    error = rules_registry_->RemoveAllRules(extension_id());
   }
 
-  return error_.empty();
+  return error.empty() ? NoArguments() : Error(error);
 }
 
 void EventsEventRemoveRulesFunction::RecordUMA(
@@ -283,24 +291,29 @@
   RecordUMAHelper(type);
 }
 
-bool EventsEventGetRulesFunction::RunAsyncOnCorrectThread() {
-  std::unique_ptr<GetRules::Params> params(GetRules::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
+EventsEventGetRulesFunction::EventsEventGetRulesFunction() = default;
 
+EventsEventGetRulesFunction::~EventsEventGetRulesFunction() = default;
+
+bool EventsEventGetRulesFunction::CreateParams() {
+  params_ = GetRules::Params::Create(*args_);
+  return params_ != nullptr;
+}
+
+ExtensionFunction::ResponseValue
+EventsEventGetRulesFunction::RunAsyncOnCorrectThread() {
   std::vector<linked_ptr<Rule> > rules;
-  if (params->rule_identifiers.get()) {
-    rules_registry_->GetRules(
-        extension_id(), *params->rule_identifiers, &rules);
+  if (params_->rule_identifiers.get()) {
+    rules_registry_->GetRules(extension_id(), *params_->rule_identifiers,
+                              &rules);
   } else {
     rules_registry_->GetAllRules(extension_id(), &rules);
   }
 
-  std::unique_ptr<base::ListValue> rules_value(new base::ListValue());
+  auto rules_value = std::make_unique<base::ListValue>();
   for (const auto& rule : rules)
     rules_value->Append(rule->ToValue());
-  SetResult(std::move(rules_value));
-
-  return true;
+  return OneArgument(std::move(rules_value));
 }
 
 void EventsEventGetRulesFunction::RecordUMA(
diff --git a/extensions/browser/api/declarative/declarative_api.h b/extensions/browser/api/declarative/declarative_api.h
index 781e5c7..ceaa341 100644
--- a/extensions/browser/api/declarative/declarative_api.h
+++ b/extensions/browser/api/declarative/declarative_api.h
@@ -13,7 +13,23 @@
 
 namespace extensions {
 
-class RulesFunction : public AsyncExtensionFunction {
+namespace api {
+namespace events {
+namespace Event {
+namespace AddRules {
+struct Params;
+}  // namespace AddRules
+namespace GetRules {
+struct Params;
+}  // namespace GetRules
+namespace RemoveRules {
+struct Params;
+}  // namespace RemoveRules
+}  // namespace Event
+}  // namespace events
+}  // namespace api
+
+class RulesFunction : public UIThreadExtensionFunction {
  public:
   RulesFunction();
 
@@ -21,54 +37,80 @@
   ~RulesFunction() override;
 
   // ExtensionFunction:
-  bool HasPermission() override;
-  bool RunAsync() override;
+  ResponseAction Run() override;
+
+  // Returns whether or not params creation succeeded, the result is used to
+  // validate params.
+  virtual bool CreateParams() = 0;
 
   // Concrete implementation of the RulesFunction that is being called
   // on the thread on which the respective RulesRegistry lives.
   // Returns false in case of errors.
-  virtual bool RunAsyncOnCorrectThread() = 0;
+  virtual ResponseValue RunAsyncOnCorrectThread() = 0;
 
   // Records UMA metrics for the kind of declarative API call.
   virtual void RecordUMA(const std::string& event_name) const = 0;
 
   scoped_refptr<RulesRegistry> rules_registry_;
+
+ private:
+  void SendResponse(ResponseValue response);
+
+  DISALLOW_COPY_AND_ASSIGN(RulesFunction);
 };
 
 class EventsEventAddRulesFunction : public RulesFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("events.addRules", EVENTS_ADDRULES)
 
+  EventsEventAddRulesFunction();
+
  protected:
-  ~EventsEventAddRulesFunction() override {}
+  ~EventsEventAddRulesFunction() override;
 
   // RulesFunction:
-  bool RunAsyncOnCorrectThread() override;
+  bool CreateParams() override;
+  ResponseValue RunAsyncOnCorrectThread() override;
   void RecordUMA(const std::string& event_name) const override;
+
+ private:
+  std::unique_ptr<api::events::Event::AddRules::Params> params_;
 };
 
 class EventsEventRemoveRulesFunction : public RulesFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("events.removeRules", EVENTS_REMOVERULES)
 
+  EventsEventRemoveRulesFunction();
+
  protected:
-  ~EventsEventRemoveRulesFunction() override {}
+  ~EventsEventRemoveRulesFunction() override;
 
   // RulesFunction:
-  bool RunAsyncOnCorrectThread() override;
+  bool CreateParams() override;
+  ResponseValue RunAsyncOnCorrectThread() override;
   void RecordUMA(const std::string& event_name) const override;
+
+ private:
+  std::unique_ptr<api::events::Event::RemoveRules::Params> params_;
 };
 
 class EventsEventGetRulesFunction : public RulesFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("events.getRules", EVENTS_GETRULES)
 
+  EventsEventGetRulesFunction();
+
  protected:
-  ~EventsEventGetRulesFunction() override {}
+  ~EventsEventGetRulesFunction() override;
 
   // RulesFunction:
-  bool RunAsyncOnCorrectThread() override;
+  bool CreateParams() override;
+  ResponseValue RunAsyncOnCorrectThread() override;
   void RecordUMA(const std::string& event_name) const override;
+
+ private:
+  std::unique_ptr<api::events::Event::GetRules::Params> params_;
 };
 
 }  // namespace extensions
diff --git a/extensions/browser/api/system_display/system_display_apitest.cc b/extensions/browser/api/system_display/system_display_apitest.cc
index 524a95a0..03a4194 100644
--- a/extensions/browser/api/system_display/system_display_apitest.cc
+++ b/extensions/browser/api/system_display/system_display_apitest.cc
@@ -21,12 +21,14 @@
 #include "extensions/test/result_catcher.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/display/test/scoped_screen_override.h"
 
 namespace extensions {
 
 using api::system_display::Bounds;
 using api::system_display::DisplayUnitInfo;
 using display::Screen;
+using display::test::ScopedScreenOverride;
 
 class MockScreen : public Screen {
  public:
@@ -216,7 +218,8 @@
   void SetUpOnMainThread() override {
     ShellApiTest::SetUpOnMainThread();
     ANNOTATE_LEAKING_OBJECT_PTR(display::Screen::GetScreen());
-    display::Screen::SetScreenInstance(screen_.get());
+    scoped_screen_override_ =
+        std::make_unique<ScopedScreenOverride>(screen_.get());
     DisplayInfoProvider::InitializeForTesting(provider_.get());
   }
 
@@ -229,6 +232,7 @@
   }
   std::unique_ptr<MockDisplayInfoProvider> provider_;
   std::unique_ptr<display::Screen> screen_;
+  std::unique_ptr<ScopedScreenOverride> scoped_screen_override_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SystemDisplayApiTest);
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc
index cbdec3b..320cd7e 100644
--- a/extensions/browser/api/web_request/web_request_permissions.cc
+++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -84,18 +84,7 @@
 // modified/canceled by extensions, e.g. because it is targeted to the webstore
 // to check for updates, extension blacklisting, etc.
 bool IsSensitiveURL(const GURL& url,
-                    bool is_request_from_browser,
-                    bool is_request_from_webui_renderer) {
-  const bool is_request_from_sensitive_source =
-      is_request_from_browser || is_request_from_webui_renderer;
-
-  // WebUI renderers should never be making network requests. We assert that
-  // here just to be sure.
-  const bool is_network_request =
-      url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS();
-  DCHECK(!is_network_request || !is_request_from_webui_renderer)
-      << "Unsupported network request from WebUI for " << url.spec();
-
+                    bool is_request_from_browser_or_webui_renderer) {
   // TODO(battre) Merge this, CanExtensionAccessURL and
   // PermissionsData::CanAccessPage into one function.
   bool sensitive_chrome_url = false;
@@ -114,7 +103,7 @@
     // These URLs are only protected for requests from the browser and webui
     // renderers, not for requests from common renderers, because
     // clients*.google.com are also used by websites.
-    if (is_request_from_sensitive_source) {
+    if (is_request_from_browser_or_webui_renderer) {
       base::StringPiece::size_type pos = host.rfind(kClient);
       if (pos != base::StringPiece::npos) {
         bool match = true;
@@ -143,7 +132,7 @@
                                              base::CompareCase::SENSITIVE));
   }
 
-  if (is_request_from_sensitive_source) {
+  if (is_request_from_browser_or_webui_renderer) {
     sensitive_chrome_url =
         sensitive_chrome_url ||
         extensions::ExtensionsAPIClient::Get()->ShouldHideBrowserNetworkRequest(
@@ -196,8 +185,8 @@
             request.render_process_id);
   }
 
-  return IsSensitiveURL(request.url, is_request_from_browser,
-                        is_request_from_webui_renderer) ||
+  return IsSensitiveURL(request.url, is_request_from_browser ||
+                                         is_request_from_webui_renderer) ||
          !HasWebRequestScheme(request.url);
 }
 
diff --git a/extensions/browser/api/web_request/web_request_permissions.h b/extensions/browser/api/web_request/web_request_permissions.h
index b04712a8..7283a430 100644
--- a/extensions/browser/api/web_request/web_request_permissions.h
+++ b/extensions/browser/api/web_request/web_request_permissions.h
@@ -22,8 +22,7 @@
 
 // Exposed for unit testing.
 bool IsSensitiveURL(const GURL& url,
-                    bool is_request_from_browser,
-                    bool is_request_from_webui_renderer);
+                    bool is_request_from_browser_or_webui_renderer);
 
 // This class is used to test whether extensions may modify web requests.
 class WebRequestPermissions {
diff --git a/extensions/browser/api/web_request/web_request_permissions_unittest.cc b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
index 9efa392..1bb7db6b 100644
--- a/extensions/browser/api/web_request/web_request_permissions_unittest.cc
+++ b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
@@ -58,14 +58,12 @@
     GURL url(test.url);
     EXPECT_TRUE(url.is_valid()) << test.url;
     EXPECT_EQ(test.is_sensitive_if_request_from_common_renderer,
-              IsSensitiveURL(url, false /* is_request_from_browser */,
-                             false /* is_request_from_web_ui_renderer */))
+              IsSensitiveURL(
+                  url, false /* is_request_from_browser_or_webui_renderer */))
         << test.url;
-
-    const bool supported_in_webui_renderers = !url.SchemeIsHTTPOrHTTPS();
     EXPECT_EQ(test.is_sensitive_if_request_from_browser_or_webui_renderer,
-              IsSensitiveURL(url, true /* is_request_from_browser */,
-                             supported_in_webui_renderers))
+              IsSensitiveURL(
+                  url, true /* is_request_from_browser_or_webui_renderer */))
         << test.url;
   }
 }
diff --git a/google_apis/gaia/oauth2_api_call_flow_unittest.cc b/google_apis/gaia/oauth2_api_call_flow_unittest.cc
index c95a14a..04666db9 100644
--- a/google_apis/gaia/oauth2_api_call_flow_unittest.cc
+++ b/google_apis/gaia/oauth2_api_call_flow_unittest.cc
@@ -8,7 +8,7 @@
 
 #include <memory>
 #include <string>
-#include <vector>
+#include <utility>
 
 #include "base/message_loop/message_loop.h"
 #include "base/time/time.h"
@@ -39,6 +39,7 @@
 using net::URLRequestContextGetter;
 using net::URLRequestStatus;
 using testing::_;
+using testing::ByMove;
 using testing::Return;
 using testing::StrictMock;
 
@@ -64,13 +65,13 @@
   }
   virtual ~MockUrlFetcherFactory() {}
 
-  MOCK_METHOD5(
-      CreateURLFetcherMock,
-      URLFetcher*(int id,
-                  const GURL& url,
-                  URLFetcher::RequestType request_type,
-                  URLFetcherDelegate* d,
-                  net::NetworkTrafficAnnotationTag traffic_annotation));
+  MOCK_METHOD5(CreateURLFetcherMock,
+               std::unique_ptr<URLFetcher>(
+                   int id,
+                   const GURL& url,
+                   URLFetcher::RequestType request_type,
+                   URLFetcherDelegate* d,
+                   net::NetworkTrafficAnnotationTag traffic_annotation));
 
   std::unique_ptr<URLFetcher> CreateURLFetcher(
       int id,
@@ -78,8 +79,7 @@
       URLFetcher::RequestType request_type,
       URLFetcherDelegate* d,
       net::NetworkTrafficAnnotationTag traffic_annotation) override {
-    return std::unique_ptr<URLFetcher>(
-        CreateURLFetcherMock(id, url, request_type, d, traffic_annotation));
+    return CreateURLFetcherMock(id, url, request_type, d, traffic_annotation);
   }
 };
 
@@ -88,16 +88,13 @@
   MockApiCallFlow() {}
   ~MockApiCallFlow() {}
 
-  MOCK_METHOD0(CreateApiCallUrl, GURL ());
-  MOCK_METHOD0(CreateApiCallBody, std::string ());
-  MOCK_METHOD1(ProcessApiCallSuccess,
-      void (const URLFetcher* source));
-  MOCK_METHOD1(ProcessApiCallFailure,
-      void (const URLFetcher* source));
-  MOCK_METHOD1(ProcessNewAccessToken,
-      void (const std::string& access_token));
+  MOCK_METHOD0(CreateApiCallUrl, GURL());
+  MOCK_METHOD0(CreateApiCallBody, std::string());
+  MOCK_METHOD1(ProcessApiCallSuccess, void(const URLFetcher* source));
+  MOCK_METHOD1(ProcessApiCallFailure, void(const URLFetcher* source));
+  MOCK_METHOD1(ProcessNewAccessToken, void(const std::string& access_token));
   MOCK_METHOD1(ProcessMintAccessTokenFailure,
-      void (const GoogleServiceAuthError& error));
+               void(const GoogleServiceAuthError& error));
 
   net::PartialNetworkTrafficAnnotationTag GetNetworkTrafficAnnotationTag() {
     return PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS;
@@ -109,13 +106,15 @@
 class OAuth2ApiCallFlowTest : public testing::Test {
  protected:
   OAuth2ApiCallFlowTest()
-      : request_context_getter_(new net::TestURLRequestContextGetter(
-            message_loop_.task_runner())) {}
+      : request_context_getter_(
+            base::MakeRefCounted<net::TestURLRequestContextGetter>(
+                message_loop_.task_runner())) {}
 
-  TestURLFetcher* CreateURLFetcher(
-      const GURL& url, bool fetch_succeeds,
-      int response_code, const std::string& body) {
-    TestURLFetcher* url_fetcher = new TestURLFetcher(0, url, &flow_);
+  std::unique_ptr<TestURLFetcher> CreateURLFetcher(const GURL& url,
+                                                   bool fetch_succeeds,
+                                                   int response_code,
+                                                   const std::string& body) {
+    auto url_fetcher = std::make_unique<TestURLFetcher>(0, url, &flow_);
     net::Error error = fetch_succeeds ? net::OK : net::ERR_FAILED;
     url_fetcher->set_status(URLRequestStatus::FromError(error));
 
@@ -133,11 +132,12 @@
     GURL url(CreateApiUrl());
     EXPECT_CALL(flow_, CreateApiCallBody()).WillOnce(Return(body));
     EXPECT_CALL(flow_, CreateApiCallUrl()).WillOnce(Return(url));
-    TestURLFetcher* url_fetcher =
+    std::unique_ptr<TestURLFetcher> url_fetcher =
         CreateURLFetcher(url, succeeds, status, std::string());
+    TestURLFetcher* url_fetcher_ptr = url_fetcher.get();
     EXPECT_CALL(factory_, CreateURLFetcherMock(_, url, _, _, _))
-        .WillOnce(Return(url_fetcher));
-    return url_fetcher;
+        .WillOnce(Return(ByMove(std::move(url_fetcher))));
+    return url_fetcher_ptr;
   }
 
   base::MessageLoop message_loop_;
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index eba31b6..41b41c0c 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/resource_dispatcher_host.h"
@@ -30,6 +31,7 @@
 #include "headless/lib/browser/headless_quota_permission_context.h"
 #include "headless/lib/headless_macros.h"
 #include "net/base/url_util.h"
+#include "net/ssl/client_cert_identity.h"
 #include "storage/browser/quota/quota_settings.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_switches.h"
@@ -321,6 +323,14 @@
   }
 }
 
+void HeadlessContentBrowserClient::SelectClientCertificate(
+    content::WebContents* web_contents,
+    net::SSLCertRequestInfo* cert_request_info,
+    net::ClientCertIdentityList client_certs,
+    std::unique_ptr<content::ClientCertificateDelegate> delegate) {
+  delegate->ContinueWithCertificate(nullptr, nullptr);
+}
+
 void HeadlessContentBrowserClient::ResourceDispatcherHostCreated() {
   resource_dispatcher_host_delegate_.reset(
       new HeadlessResourceDispatcherHostDelegate);
diff --git a/headless/lib/browser/headless_content_browser_client.h b/headless/lib/browser/headless_content_browser_client.h
index e05bd02..6980a87 100644
--- a/headless/lib/browser/headless_content_browser_client.h
+++ b/headless/lib/browser/headless_content_browser_client.h
@@ -52,6 +52,12 @@
       const base::Callback<void(content::CertificateRequestResultType)>&
           callback) override;
 
+  void SelectClientCertificate(
+      content::WebContents* web_contents,
+      net::SSLCertRequestInfo* cert_request_info,
+      net::ClientCertIdentityList client_certs,
+      std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
+
   void ResourceDispatcherHostCreated() override;
 
   net::NetLog* GetNetLog() override;
diff --git a/headless/lib/headless_browser_browsertest.cc b/headless/lib/headless_browser_browsertest.cc
index 63142ec..735e470 100644
--- a/headless/lib/headless_browser_browsertest.cc
+++ b/headless/lib/headless_browser_browsertest.cc
@@ -971,4 +971,23 @@
   (void)web_contents;
 }
 
+IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ServerWantsClientCertificate) {
+  net::SpawnedTestServer::SSLOptions ssl_options;
+  ssl_options.request_client_certificate = true;
+
+  net::SpawnedTestServer server(
+      net::SpawnedTestServer::TYPE_HTTPS, ssl_options,
+      base::FilePath(FILE_PATH_LITERAL("headless/test/data")));
+  EXPECT_TRUE(server.Start());
+
+  HeadlessBrowserContext* browser_context =
+      browser()->CreateBrowserContextBuilder().Build();
+
+  HeadlessWebContents* web_contents =
+      browser_context->CreateWebContentsBuilder()
+          .SetInitialURL(server.GetURL("/hello.html"))
+          .Build();
+  EXPECT_TRUE(WaitForLoad(web_contents));
+}
+
 }  // namespace headless
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index db672e2..cceda1d 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -1113,6 +1113,18 @@
       mixins: "goma-j150"
       name: "linux_chromium_rel_ng"
     }
+    builders {
+      mixins: "linux-try"
+      mixins: "goma-j150"
+      name: "linux_chromium_rel_ng_patch_on_gclient"
+      dimensions: "builder:linux_chromium_rel_ng"
+      experimental: YES
+      auto_builder_dimension: NO
+      recipe {
+        properties: "buildername:linux_chromium_rel_ng"
+        properties_j: "$depot_tools/bot_update:{\"apply_patch_on_gclient\":true}"
+      }
+    }
     builders { mixins: "linux-try" name: "linux_chromium_tsan_rel_ng" }
     builders { mixins: "linux-try" name: "linux_chromium_tsan_variable" }
     builders { mixins: "linux-try" name: "linux_chromium_ubsan_rel_ng" }
diff --git a/infra/config/global/luci-milo-dev.cfg b/infra/config/global/luci-milo-dev.cfg
index ec73a6b..abf2911f4 100644
--- a/infra/config/global/luci-milo-dev.cfg
+++ b/infra/config/global/luci-milo-dev.cfg
@@ -2983,18 +2983,22 @@
   header_id: "chromium"
 
   builders: {
-    name: "buildbot/chromium.perf.fyi/Android Builder FYI"
+    name: "buildbot/chromium.perf.fyi/Android Builder Perf FYI"
     category: "android"
   }
   builders: {
-    name: "buildbot/chromium.perf.fyi/Android arm64 Builder FYI"
+    name: "buildbot/chromium.perf.fyi/Android arm64 Builder Perf FYI"
     category: "android"
   }
   builders: {
-    name: "buildbot/chromium.perf.fyi/Linux Compile FYI"
+    name: "buildbot/chromium.perf.fyi/Linux Compile Perf FYI"
     category: "linux"
   }
   builders: {
+    name: "buildbot/chromium.perf.fyi/Mac Builder Perf FYI"
+    category: "mac"
+  }
+  builders: {
     name: "buildbot/chromium.perf.fyi/Android Nexus 5X Perf FYI"
     category: "android"
   }
@@ -3031,10 +3035,6 @@
     category: "mac"
   }
   builders: {
-    name: "buildbot/chromium.perf.fyi/Mac Builder FYI"
-    category: "mac"
-  }
-  builders: {
     name: "buildbot/chromium.perf.fyi/Android Go"
     category: "android"
   }
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index 5308d2f..a9a83a1 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -281,6 +281,9 @@
     {"unified-consent", flag_descriptions::kUnifiedConsentName,
      flag_descriptions::kUnifiedConsentDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(signin::kUnifiedConsent)},
+    {"autofill-dynamic-forms", flag_descriptions::kAutofillDynamicFormsName,
+     flag_descriptions::kAutofillDynamicFormsDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(autofill::features::kAutofillDynamicForms)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
index 278d5c9..d752fa33 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
@@ -460,7 +460,8 @@
     return;
   }
 
-  if ((params.type == "blur" || params.type == "change")) {
+  if (params.type == "blur" || params.type == "change" ||
+      params.type == "form_changed") {
     return;
   }
 
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 51737b4..c9011c7 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -16,6 +16,10 @@
     "Delay between the different fields of a form being autofilled. In "
     "milliseconds.";
 
+const char kAutofillDynamicFormsName[] = "Autofill dynamic forms";
+const char kAutofillDynamicFormsDescription[] =
+    "Refills forms that dynamically change after an initial fill";
+
 const char kBrowserTaskScheduler[] = "Task Scheduler";
 const char kBrowserTaskSchedulerDescription[] =
     "Enables redirection of some task posting APIs to the task scheduler.";
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index 22ae52d..c8b1a63e3 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -11,6 +11,10 @@
 extern const char kAutofillIOSDelayBetweenFieldsName[];
 extern const char kAutofillIOSDelayBetweenFieldsDescription[];
 
+// Title and description for the flag to controll the dynamic autofill.
+extern const char kAutofillDynamicFormsName[];
+extern const char kAutofillDynamicFormsDescription[];
+
 // Title and description for the flag to control redirection to the task
 // scheduler.
 extern const char kBrowserTaskScheduler[];
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
index 92227cb..97c4181e 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
@@ -176,8 +176,13 @@
   metrics_service_ = std::make_unique<metrics::MetricsService>(
       metrics_state_manager_, this, local_state);
 
+  // Always restrict on iOS.
+  // TODO(crbug.com/828878): Use the flag to set this.
+  bool restrict_to_whitelist_entries = true;
+
   if (base::FeatureList::IsEnabled(ukm::kUkmFeature))
-    ukm_service_ = std::make_unique<ukm::UkmService>(local_state, this);
+    ukm_service_ = std::make_unique<ukm::UkmService>(
+        local_state, this, restrict_to_whitelist_entries);
 
   // Register metrics providers.
   metrics_service_->RegisterMetricsProvider(
diff --git a/ios/chrome/browser/passwords/js_password_manager.h b/ios/chrome/browser/passwords/js_password_manager.h
index 93a429fa..a2dc1af 100644
--- a/ios/chrome/browser/passwords/js_password_manager.h
+++ b/ios/chrome/browser/passwords/js_password_manager.h
@@ -6,16 +6,14 @@
 #define IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
 
 #include "base/ios/block_types.h"
-#import "ios/web/public/web_state/js/crw_js_injection_manager.h"
-
-@class CRWJSInjectionReceiver;
+#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 
 // Loads the JavaScript file, password_controller.js, which contains password
 // form parsing and autofill functions. It will be evaluated on a page that
 // is known to have at least one password form (see hasPasswordField_ in
 // password_controller.js) It returns contents of those password forms and also
 // registers functions that are later used to autofill them.
-@interface JsPasswordManager : CRWJSInjectionManager
+@interface JsPasswordManager : NSObject
 
 // Finds any password forms on the web page.
 // |completionHandler| is then called with the JSON string result (which can
@@ -48,6 +46,12 @@
                 password:(NSString*)password
        completionHandler:(void (^)(BOOL))completionHandler;
 
+// Designated initializer. |receiver| should not be nil.
+- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
diff --git a/ios/chrome/browser/passwords/js_password_manager.mm b/ios/chrome/browser/passwords/js_password_manager.mm
index 371ab3b..31f7d88 100644
--- a/ios/chrome/browser/passwords/js_password_manager.mm
+++ b/ios/chrome/browser/passwords/js_password_manager.mm
@@ -22,32 +22,39 @@
 }
 }  // namespace
 
-@interface JsPasswordManager ()
-// Injects a script that does two things:
-// 1. Injects password controller JavaScript in the page.
-// 2. Extracts the _submitted_ password form data from the DOM on the page.
-// The result is returned in |completionHandler|.
-// |completionHandler| cannot be nil.
-- (void)evaluateExtraScript:(NSString*)script
-          completionHandler:(void (^)(NSString*))completionHandler;
-@end
+@implementation JsPasswordManager {
+  // The injection receiver used to evaluate JavaScript.
+  CRWJSInjectionReceiver* _receiver;
+}
 
-@implementation JsPasswordManager
+- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver {
+  DCHECK(receiver);
+  self = [super init];
+  if (self) {
+    _receiver = receiver;
+  }
+  return self;
+}
 
 - (void)findPasswordFormsWithCompletionHandler:
     (void (^)(NSString*))completionHandler {
   DCHECK(completionHandler);
-  [self evaluateExtraScript:@"__gCrWeb.findPasswordForms()"
-          completionHandler:completionHandler];
+  [_receiver executeJavaScript:@"__gCrWeb.passwords.findPasswordForms()"
+             completionHandler:^(id result, NSError*) {
+               completionHandler(base::mac::ObjCCastStrict<NSString>(result));
+             }];
 }
 
 - (void)extractForm:(NSString*)formName
       completionHandler:(void (^)(NSString*))completionHandler {
   DCHECK(completionHandler);
-  NSString* extra =
-      [NSString stringWithFormat:@"__gCrWeb.getPasswordFormDataAsString(%@)",
-                                 JSONEscape(formName)];
-  [self evaluateExtraScript:extra completionHandler:completionHandler];
+  NSString* extra = [NSString
+      stringWithFormat:@"__gCrWeb.passwords.getPasswordFormDataAsString(%@)",
+                       JSONEscape(formName)];
+  [_receiver executeJavaScript:extra
+             completionHandler:^(id result, NSError*) {
+               completionHandler(base::mac::ObjCCastStrict<NSString>(result));
+             }];
 }
 
 - (void)fillPasswordForm:(NSString*)JSONString
@@ -56,30 +63,13 @@
        completionHandler:(void (^)(BOOL))completionHandler {
   DCHECK(completionHandler);
   NSString* script = [NSString
-      stringWithFormat:@"__gCrWeb.fillPasswordForm(%@, %@, %@)", JSONString,
-                       JSONEscape(username), JSONEscape(password)];
-  [self executeJavaScript:script completionHandler:^(id result, NSError*) {
-    completionHandler([result isEqual:@YES]);
-  }];
+      stringWithFormat:@"__gCrWeb.passwords.fillPasswordForm(%@, %@, %@)",
+                       JSONString, JSONEscape(username), JSONEscape(password)];
+  [_receiver executeJavaScript:script
+             completionHandler:^(id result, NSError*) {
+               completionHandler([result isEqual:@YES]);
+             }];
 }
 
-#pragma mark -
-#pragma mark ProtectedMethods
-
-- (NSString*)scriptPath {
-  return @"password_controller";
-}
-
-#pragma mark -
-#pragma mark Private
-
-- (void)evaluateExtraScript:(NSString*)script
-          completionHandler:(void (^)(NSString*))completionHandler {
-  DCHECK(completionHandler);
-  NSString* JS = [[self injectionContent] stringByAppendingString:script];
-  [self executeJavaScript:JS completionHandler:^(id result, NSError*) {
-    completionHandler(base::mac::ObjCCastStrict<NSString>(result));
-  }];
-}
 
 @end
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 99e44d2..b840e949 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -268,7 +268,7 @@
   std::unique_ptr<PasswordManagerDriver> passwordManagerDriver_;
   std::unique_ptr<CredentialManager> credentialManager_;
 
-  __weak JsPasswordManager* passwordJsManager_;
+  JsPasswordManager* passwordJsManager_;
 
   AccountSelectFillData fillData_;
 
@@ -327,12 +327,11 @@
     passwordManager_.reset(new PasswordManager(passwordManagerClient_.get()));
     passwordManagerDriver_.reset(new IOSChromePasswordManagerDriver(self));
 
-    passwordJsManager_ = base::mac::ObjCCastStrict<JsPasswordManager>(
-        [webState_->GetJSInjectionReceiver()
-            instanceOfClass:[JsPasswordManager class]]);
     webStateObserverBridge_ =
         std::make_unique<web::WebStateObserverBridge>(self);
     webState_->AddObserver(webStateObserverBridge_.get());
+    passwordJsManager_ = [[JsPasswordManager alloc]
+        initWithReceiver:webState_->GetJSInjectionReceiver()];
     sentRequestToStore_ = NO;
 
     if (base::FeatureList::IsEnabled(features::kCredentialManager)) {
diff --git a/ios/chrome/browser/passwords/password_controller_js_unittest.mm b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
index 600c3bc..c411a354 100644
--- a/ios/chrome/browser/passwords/password_controller_js_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_js_unittest.mm
@@ -22,10 +22,8 @@
     : public web::WebJsTest<web::WebTestWithWebState> {
  public:
   PasswordControllerJsTest()
-      : web::WebJsTest<web::WebTestWithWebState>(@[
-          @"chrome_bundle_all_frames", @"chrome_bundle_main_frame",
-          @"password_controller"
-        ]) {}
+      : web::WebJsTest<web::WebTestWithWebState>(
+            @[ @"chrome_bundle_all_frames", @"chrome_bundle_main_frame" ]) {}
 };
 
 // IDs used in the Username and Password <input> elements.
@@ -77,10 +75,11 @@
   NSString* const username = @"john.doe@gmail.com";
   NSString* const password = @"super!secret";
   LoadHtmlAndInject(GAIASignInForm(formAction, username, YES));
-  EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat(
-                        @"__gCrWeb.fillPasswordForm(%@, '%@', '%@', '%@')",
-                        GAIASignInFormData(formAction), username, password,
-                        formAction));
+  EXPECT_NSEQ(
+      @YES,
+      ExecuteJavaScriptWithFormat(
+          @"__gCrWeb.passwords.fillPasswordForm(%@, '%@', '%@', '%@')",
+          GAIASignInFormData(formAction), username, password, formAction));
   // Verifies that the sign-in form has been filled with username/password.
   ExecuteJavaScriptOnElementsAndCheck(@"document.getElementById('%@').value",
                                       @[ kEmailInputID, kPasswordInputID ],
@@ -97,10 +96,11 @@
   NSString* const username2 = @"jean.dubois@gmail.com";
   NSString* const password = @"super!secret";
   LoadHtmlAndInject(GAIASignInForm(formAction, username1, YES));
-  EXPECT_NSEQ(@NO, ExecuteJavaScriptWithFormat(
-                       @"__gCrWeb.fillPasswordForm(%@, '%@', '%@', '%@')",
-                       GAIASignInFormData(formAction), username2, password,
-                       formAction));
+  EXPECT_NSEQ(
+      @NO,
+      ExecuteJavaScriptWithFormat(
+          @"__gCrWeb.passwords.fillPasswordForm(%@, '%@', '%@', '%@')",
+          GAIASignInFormData(formAction), username2, password, formAction));
   // Verifies that the sign-in form has not been filled.
   ExecuteJavaScriptOnElementsAndCheck(@"document.getElementById('%@').value",
                                       @[ kEmailInputID, kPasswordInputID ],
@@ -117,10 +117,11 @@
   NSString* const username2 = @"jane.doe@gmail.com";
   NSString* const password = @"super!secret";
   LoadHtmlAndInject(GAIASignInForm(formAction, username1, NO));
-  EXPECT_NSEQ(@YES, ExecuteJavaScriptWithFormat(
-                        @"__gCrWeb.fillPasswordForm(%@, '%@', '%@', '%@')",
-                        GAIASignInFormData(formAction), username2, password,
-                        formAction));
+  EXPECT_NSEQ(
+      @YES,
+      ExecuteJavaScriptWithFormat(
+          @"__gCrWeb.passwords.fillPasswordForm(%@, '%@', '%@', '%@')",
+          GAIASignInFormData(formAction), username2, password, formAction));
   // Verifies that the sign-in form has been filled with the new username
   // and password.
   ExecuteJavaScriptOnElementsAndCheck(@"document.getElementById('%@').value",
@@ -154,8 +155,8 @@
           @"\"max_length\":524288,\"is_checkable\":false,\"value\":\"\","
           @"\"label\":\"Password:\"}]}]",
           base_url.c_str()];
-  EXPECT_NSEQ(result,
-              ExecuteJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()"));
+  EXPECT_NSEQ(result, ExecuteJavaScriptWithFormat(
+                          @"__gCrWeb.passwords.findPasswordForms()"));
 };
 
 // Check that multiple password forms are identified and serialized correctly.
@@ -198,8 +199,8 @@
           @"\"label\":\"Password:\"}]}]",
           base_url.c_str(), base_url.c_str()];
 
-  EXPECT_NSEQ(result,
-              ExecuteJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()"));
+  EXPECT_NSEQ(result, ExecuteJavaScriptWithFormat(
+                          @"__gCrWeb.passwords.findPasswordForms()"));
 };
 
 // Test serializing of password forms.
@@ -231,7 +232,8 @@
   EXPECT_NSEQ(
       result,
       ExecuteJavaScriptWithFormat(
-          @"__gCrWeb.stringify(__gCrWeb.getPasswordFormData(%@))", parameter));
+          @"__gCrWeb.stringify(__gCrWeb.passwords.getPasswordFormData(%@))",
+          parameter));
 };
 
 // Check that if a form action is not set then the action is parsed to the
@@ -258,8 +260,8 @@
           @"autocomplete\":true,\"is_focusable\":true,\"max_length\":524288,"
           @"\"is_checkable\":false,\"value\":\"\",\"label\":\"Password:\"}]}]",
           base_url.c_str(), base_url.c_str()];
-  EXPECT_NSEQ(result,
-              ExecuteJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()"));
+  EXPECT_NSEQ(result, ExecuteJavaScriptWithFormat(
+                          @"__gCrWeb.passwords.findPasswordForms()"));
 };
 
 // Checks that a touchend event from a button which contains in a password form
@@ -274,9 +276,9 @@
        "</form>"
        "</body></html>");
 
-  // Call __gCrWeb.findPasswordForms in order to set an event handler on the
-  // button touchend event.
-  ExecuteJavaScriptWithFormat(@"__gCrWeb.findPasswordForms()");
+  // Call __gCrWeb.passwords.findPasswordForms in order to set an event handler
+  // on the button touchend event.
+  ExecuteJavaScriptWithFormat(@"__gCrWeb.passwords.findPasswordForms()");
 
   // Replace __gCrWeb.message.invokeOnHost with mock method for checking of call
   // arguments.
@@ -348,7 +350,7 @@
                     page_origin.c_str(), form_fill_data_origin.c_str()];
   EXPECT_NSEQ(@YES,
               ExecuteJavaScriptWithFormat(
-                  @"__gCrWeb.fillPasswordForm(%@, '%@', '%@', '%s')",
+                  @"__gCrWeb.passwords.fillPasswordForm(%@, '%@', '%@', '%s')",
                   form_fill_data, username, password, page_origin.c_str()));
   // Verifies that the sign-in form has been filled with username/password.
   ExecuteJavaScriptOnElementsAndCheck(@"document.getElementById('%@').value",
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index e21bcf4e..de326c8 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -1127,6 +1127,11 @@
   TestChromeBrowserState::Builder builder;
   std::unique_ptr<TestChromeBrowserState> browser_state(builder.Build());
   MockWebState web_state;
+  id mock_js_injection_receiver =
+      [OCMockObject mockForClass:[CRWJSInjectionReceiver class]];
+  [[mock_js_injection_receiver expect] executeJavaScript:[OCMArg any]
+                                       completionHandler:[OCMArg any]];
+  web_state.SetJSInjectionReceiver(mock_js_injection_receiver);
   ON_CALL(web_state, GetBrowserState())
       .WillByDefault(testing::Return(browser_state.get()));
 
diff --git a/ios/chrome/browser/passwords/resources/password_controller.js b/ios/chrome/browser/passwords/resources/password_controller.js
index 6cb85d1..25eade0 100644
--- a/ios/chrome/browser/passwords/resources/password_controller.js
+++ b/ios/chrome/browser/passwords/resources/password_controller.js
@@ -2,301 +2,312 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This file adheres to closure-compiler conventions in order to enable
-// compilation with ADVANCED_OPTIMIZATIONS. See http://goo.gl/FwOgy
-//
-// Installs password management functions on the |__gCrWeb| object.
-//
-// Finds all password forms in the current document and extracts
-// their attributes and elements using the same logic as
-// third_party/WebKit/Source/WebCore/html/HTMLFormElement.cpp
-//
-// Returns a JSON string representing an array of objects,
-// where each object represents a password form with the discovered
-// elements and their values.
-//
-// The search for password form fields follows the same algorithm
-// as the WebKit implementation, see http://goo.gl/4hwh6
+/**
+ * @fileoverview Installs Passwords management functions on the __gCrWeb object.
+ *
+ * It scans the DOM, extracting and storing password forms and returns a JSON
+ * string representing an array of objects, each of which represents an Passord
+ * form with information about a form to be filled and/or submitted and it can
+ * be translated to struct FormData for further processing.
+ */
 
-// Only install the password management functions once.
-if (__gCrWeb && !__gCrWeb['fillPasswordForm']) {
-  /**
-   * Finds all password forms in the window and returns form data as a JSON
-   * string.
-   * @return {string} Form data as a JSON string.
-   */
-  __gCrWeb['findPasswordForms'] = function() {
-    var formDataList = [];
-    if (hasPasswordField_(window)) {
-      getPasswordFormDataList_(formDataList, window);
+goog.provide('__crWeb.passwords');
+
+/* Beginning of anonymous object. */
+(function() {
+
+/**
+ * Namespace for this file. It depends on |__gCrWeb| having already been
+ * injected.
+ */
+__gCrWeb.passwords = {};
+__gCrWeb['passwords'] = __gCrWeb.passwords;
+
+/**
+ * Finds all password forms in the window and returns form data as a JSON
+ * string.
+ * @return {string} Form data as a JSON string.
+ */
+__gCrWeb.passwords['findPasswordForms'] = function() {
+  var formDataList = [];
+  if (hasPasswordField_(window)) {
+    getPasswordFormDataList_(formDataList, window);
+  }
+  return __gCrWeb.stringify(formDataList);
+};
+
+/** Returns true if the supplied window or any frames inside contain an input
+ * field of type 'password'.
+ * @private
+ * @param {Window} win Whether the supplied window or any frames inside
+ * contain an input field of type 'password'.
+ * @return {boolean}
+ */
+var hasPasswordField_ = function(win) {
+  var doc = win.document;
+
+  // We may will not be allowed to read the 'document' property from a frame
+  // that is in a different domain.
+  if (!doc) {
+    return false;
+  }
+
+  if (doc.querySelector('input[type=password]')) {
+    return true;
+  }
+
+  return getSameOriginFrames_(win).some(hasPasswordField_);
+};
+
+/**
+ * Returns the contentWindow of all iframes that are from the the same origin
+ * as the containing window.
+ * @param {Window} win The window in which to look for frames.
+ * @return {Array.<Window>} Array of the same-origin frames found.
+ */
+var getSameOriginFrames_ = function(win) {
+  var frames = win.document.getElementsByTagName('iframe');
+  var result = [];
+  for (var i = 0; i < frames.length; i++) {
+    if (!frames[i].src ||
+        __gCrWeb.common.isSameOrigin(win.location.href, frames[i].src)) {
+      result.push(frames[i].contentWindow);
     }
-    return __gCrWeb.stringify(formDataList);
-  };
+  }
+  return result;
+};
 
-  /** Returns true if the supplied window or any frames inside contain an input
-   * field of type 'password'.
-   * @private
-   * @param {Window} win Whether the supplied window or any frames inside
-   * contain an input field of type 'password'.
-   * @return {boolean}
-   */
-  var hasPasswordField_ = function(win) {
-    var doc = win.document;
+/**
+ * If |form| has no submit elements and exactly 1 button that button
+ * is assumed to be a submit button. This function adds onSubmitButtonClick_
+ * as a handler for touchend event of this button. Touchend event is used as
+ * a proxy for onclick event because onclick handling might be prevented by
+ * the site JavaScript.
+ */
+var addSubmitButtonTouchEndHandler_ = function(form) {
+  if (form.querySelector('input[type=submit]'))
+    return;
+  // Try to find buttons of type submit at first.
+  var buttons = form.querySelectorAll('button[type="submit"]');
+  if (buttons.length == 0) {
+    // Try to check all buttons. If there is only one button, assume that this
+    // is the submit button.
+    buttons = form.querySelectorAll('button');
+    if (buttons.length != 1)
+      return;
+  }
+  for (var i = 0; i < buttons.length; ++i)
+    buttons[0].addEventListener('touchend', onSubmitButtonTouchEnd_);
+};
 
-    // We may will not be allowed to read the 'document' property from a frame
-    // that is in a different domain.
-    if (!doc) {
-      return false;
+/**
+ * Click handler for the submit button. It sends to the host
+ * form.submitButtonClick command.
+ */
+var onSubmitButtonTouchEnd_ = function(evt) {
+  var form = evt.currentTarget.form;
+  var formData = __gCrWeb.passwords.getPasswordFormData(form);
+  if (!formData)
+    return;
+  formData['command'] = 'passwordForm.submitButtonClick';
+  __gCrWeb.message.invokeOnHost(formData);
+};
+
+/**
+ * Returns the element from |inputs| which has the field identifier equal to
+ * |identifier| and null if there is no such element.
+ * @param {Array.<HTMLInputElement>} inputs
+ * @param {string} identifier
+ * @return {HTMLInputElement}
+ */
+var findInputByFieldIdentifier_ = function(inputs, identifier) {
+  for (var i = 0; i < inputs.length; ++i) {
+    if (identifier == __gCrWeb.form.getFieldIdentifier(inputs[i])) {
+      return inputs[i];
     }
+  }
+  return null;
+};
 
-    if (doc.querySelector('input[type=password]')) {
-      return true;
-    }
+/**
+ * Returns the password form with the given |identifier| as a JSON string
+ * from the frame |win| and all its same-origin subframes.
+ * @param {Window} win The window in which to look for forms.
+ * @param {string} identifier The name of the form to extract.
+ * @return {HTMLFormElement} The password form.
+ */
+var getPasswordFormElement_ = function(win, identifier) {
+  var el = win.__gCrWeb.form.getFormElementFromIdentifier(identifier);
+  if (el)
+    return el;
+  var frames = getSameOriginFrames_(win);
+  for (var i = 0; i < frames.length; ++i) {
+    el = getPasswordFormElement_(frames[i], identifier);
+    if (el)
+      return el;
+  }
+  return null;
+};
 
-    return getSameOriginFrames_(win).some(hasPasswordField_);
-  };
+/**
+ * Returns an array of input elements in a form.
+ * @param {HTMLFormElement} form A form element for which the input elements
+ *   are returned.
+ * @return {Array<HTMLInputElement>}
+ */
+var getFormInputElements_ = function(form) {
+  return __gCrWeb.form.getFormControlElements(form).filter(function(element) {
+    return element.tagName === 'INPUT';
+  });
+};
 
-  /**
-   * Returns the contentWindow of all iframes that are from the the same origin
-   * as the containing window.
-   * @param {Window} win The window in which to look for frames.
-   * @return {Array.<Window>} Array of the same-origin frames found.
-   */
-  var getSameOriginFrames_ = function(win) {
-    var frames = win.document.getElementsByTagName('iframe');
-    var result = [];
-    for (var i = 0; i < frames.length; i++) {
-      if (!frames[i].src ||
-          __gCrWeb.common.isSameOrigin(win.location.href, frames[i].src)) {
-        result.push(frames[i].contentWindow);
-      }
-    }
-    return result;
-  };
+/**
+ * Returns the password form with the given |identifier| as a JSON string.
+ * @param {string} identifier The identifier of the form to extract.
+ * @return {string} The password form.
+ */
+__gCrWeb.passwords['getPasswordFormDataAsString'] = function(identifier) {
+  var el = getPasswordFormElement_(window, identifier);
+  if (!el)
+    return '{}';
+  var formData = __gCrWeb.passwords.getPasswordFormData(el);
+  if (!formData)
+    return '{}';
+  return __gCrWeb.stringify(formData);
+};
 
-  /**
-   * If |form| has no submit elements and exactly 1 button that button
-   * is assumed to be a submit button. This function adds onSubmitButtonClick_
-   * as a handler for touchend event of this button. Touchend event is used as
-   * a proxy for onclick event because onclick handling might be prevented by
-   * the site JavaScript.
-   */
-  var addSubmitButtonTouchEndHandler_ = function(form) {
-    if (form.querySelector('input[type=submit]')) return;
-    // Try to find buttons of type submit at first.
-    var buttons = form.querySelectorAll('button[type="submit"]');
-    if (buttons.length == 0) {
-      // Try to check all buttons. If there is only one button, assume that this
-      // is the submit button.
-      buttons = form.querySelectorAll('button');
-      if (buttons.length != 1) return;
-    }
-    for (var i = 0; i < buttons.length; ++i)
-      buttons[0].addEventListener('touchend', onSubmitButtonTouchEnd_);
-  };
+/**
+ * Finds the form described by |formData| and fills in the
+ * username and password values.
+ *
+ * This is a public function invoked by Chrome. There is no information
+ * passed to this function that the page does not have access to anyway.
+ *
+ * @param {AutofillFormData} formData Form data.
+ * @param {string} username The username to fill.
+ * @param {string} password The password to fill.
+ * @param {string=} opt_normalizedOrigin The origin URL to compare to.
+ * @return {boolean} Whether a form field has been filled.
+ */
+__gCrWeb.passwords['fillPasswordForm'] = function(
+    formData, username, password, opt_normalizedOrigin) {
+  var normalizedOrigin = opt_normalizedOrigin ||
+      __gCrWeb.common.removeQueryAndReferenceFromURL(window.location.href);
+  var origin = /** @type {string} */ (formData['origin']);
+  if (!__gCrWeb.common.isSameOrigin(origin, normalizedOrigin)) {
+    return false;
+  }
+  return fillPasswordFormWithData_(
+      formData, username, password, window, opt_normalizedOrigin);
+};
 
-  /**
-   * Click handler for the submit button. It sends to the host
-   * form.submitButtonClick command.
-   */
-  var onSubmitButtonTouchEnd_ = function(evt) {
-    var form = evt.currentTarget.form;
-    var formData = __gCrWeb.getPasswordFormData(form);
-    if (!formData) return;
-    formData['command'] = 'passwordForm.submitButtonClick';
-    __gCrWeb.message.invokeOnHost(formData);
-  };
+/**
+ * Given a description of a form (origin, action and input fields),
+ * finds that form on the page and fills in the specified username
+ * and password.
+ *
+ * @param {AutofillFormData} formData Form data.
+ * @param {string} username The username to fill.
+ * @param {string} password The password to fill.
+ * @param {Window} win A window or a frame containing formData.
+ * @param {string=} opt_normalizedOrigin The origin URL to compare to.
+ * @return {boolean} Whether a form field has been filled.
+ */
+var fillPasswordFormWithData_ = function(
+    formData, username, password, win, opt_normalizedOrigin) {
+  var doc = win.document;
+  var forms = doc.forms;
+  var filled = false;
 
-  /**
-   * Returns the element from |inputs| which has the field identifier equal to
-   * |identifier| and null if there is no such element.
-   * @param {Array.<HTMLInputElement>} inputs
-   * @param {string} identifier
-   * @return {HTMLInputElement}
-   */
-  var findInputByFieldIdentifier_ = function(inputs, identifier) {
-    for (var i = 0; i < inputs.length; ++i) {
-      if (identifier == __gCrWeb.form.getFieldIdentifier(inputs[i])) {
-        return inputs[i];
-      }
-    }
-    return null;
-  };
+  for (var i = 0; i < forms.length; i++) {
+    var form = forms[i];
+    var normalizedFormAction =
+        opt_normalizedOrigin || __gCrWeb.fill.getCanonicalActionForForm(form);
+    if (formData.action != normalizedFormAction)
+      continue;
+    var inputs = getFormInputElements_(form);
+    var usernameInput =
+        findInputByFieldIdentifier_(inputs, formData.fields[0].name);
+    if (usernameInput == null || !__gCrWeb.common.isTextField(usernameInput) ||
+        usernameInput.disabled)
+      continue;
+    var passwordInput =
+        findInputByFieldIdentifier_(inputs, formData.fields[1].name);
+    if (passwordInput == null || passwordInput.type != 'password' ||
+        passwordInput.readOnly || passwordInput.disabled)
+      continue;
 
-  /**
-   * Returns the password form with the given |identifier| as a JSON string
-   * from the frame |win| and all its same-origin subframes.
-   * @param {Window} win The window in which to look for forms.
-   * @param {string} identifier The name of the form to extract.
-   * @return {HTMLFormElement} The password form.
-   */
-  var getPasswordFormElement_ = function(win, identifier) {
-    var el = win.__gCrWeb.form.getFormElementFromIdentifier(identifier);
-    if (el) return el;
-    var frames = getSameOriginFrames_(win);
-    for (var i = 0; i < frames.length; ++i) {
-      el = getPasswordFormElement_(frames[i], identifier);
-      if (el) return el;
-    }
-    return null;
-  };
-
-  /**
-   * Returns an array of input elements in a form.
-   * @param {HTMLFormElement} form A form element for which the input elements
-   *   are returned.
-   * @return {Array<HTMLInputElement>}
-   */
-  var getFormInputElements_ = function(form) {
-    return __gCrWeb.form.getFormControlElements(form).filter(function(
-        element) {
-      return element.tagName === 'INPUT';
-    });
-  };
-
-  /**
-   * Returns the password form with the given |identifier| as a JSON string.
-   * @param {string} identifier The identifier of the form to extract.
-   * @return {string} The password form.
-   */
-  __gCrWeb['getPasswordFormDataAsString'] = function(identifier) {
-    var el = getPasswordFormElement_(window, identifier);
-    if (!el) return '{}';
-    var formData = __gCrWeb.getPasswordFormData(el);
-    if (!formData) return '{}';
-    return __gCrWeb.stringify(formData);
-  };
-
-  /**
-   * Finds the form described by |formData| and fills in the
-   * username and password values.
-   *
-   * This is a public function invoked by Chrome. There is no information
-   * passed to this function that the page does not have access to anyway.
-   *
-   * @param {AutofillFormData} formData Form data.
-   * @param {string} username The username to fill.
-   * @param {string} password The password to fill.
-   * @param {string=} opt_normalizedOrigin The origin URL to compare to.
-   * @return {boolean} Whether a form field has been filled.
-   */
-  __gCrWeb['fillPasswordForm'] = function(
-      formData, username, password, opt_normalizedOrigin) {
-    var normalizedOrigin = opt_normalizedOrigin ||
-        __gCrWeb.common.removeQueryAndReferenceFromURL(window.location.href);
-    var origin = /** @type {string} */ (formData['origin']);
-    if (!__gCrWeb.common.isSameOrigin(origin, normalizedOrigin)) {
-      return false;
-    }
-    return fillPasswordFormWithData_(
-        formData, username, password, window, opt_normalizedOrigin);
-  };
-
-  /**
-   * Given a description of a form (origin, action and input fields),
-   * finds that form on the page and fills in the specified username
-   * and password.
-   *
-   * @param {AutofillFormData} formData Form data.
-   * @param {string} username The username to fill.
-   * @param {string} password The password to fill.
-   * @param {Window} win A window or a frame containing formData.
-   * @param {string=} opt_normalizedOrigin The origin URL to compare to.
-   * @return {boolean} Whether a form field has been filled.
-   */
-  var fillPasswordFormWithData_ = function(
-      formData, username, password, win, opt_normalizedOrigin) {
-    var doc = win.document;
-    var forms = doc.forms;
-    var filled = false;
-
-    for (var i = 0; i < forms.length; i++) {
-      var form = forms[i];
-      var normalizedFormAction = opt_normalizedOrigin ||
-          __gCrWeb.fill.getCanonicalActionForForm(form);
-      if (formData.action != normalizedFormAction) continue;
-      var inputs = getFormInputElements_(form);
-      var usernameInput =
-          findInputByFieldIdentifier_(inputs, formData.fields[0].name);
-      if (usernameInput == null ||
-          !__gCrWeb.common.isTextField(usernameInput) || usernameInput.disabled)
-        continue;
-      var passwordInput =
-          findInputByFieldIdentifier_(inputs, formData.fields[1].name);
-      if (passwordInput == null || passwordInput.type != 'password' ||
-          passwordInput.readOnly || passwordInput.disabled)
-        continue;
-
-      // If username was provided on a read-only field and it matches the
-      // requested username, fill the form.
-      if (usernameInput.readOnly) {
-        if (usernameInput.value == username) {
-          passwordInput.value = password;
-          filled = true;
-        }
-      } else {
-        // Setting input fields via .value assignment does not trigger all
-        // the events that a web site can observe. This has the effect of
-        // certain web sites rejecting an autofilled sign in form as not
-        // signed in because the user didn't actually "typed" into the field.
-        // Adding the .focus() works around this problems.
-        usernameInput.focus();
-        usernameInput.value = username;
-        passwordInput.focus();
+    // If username was provided on a read-only field and it matches the
+    // requested username, fill the form.
+    if (usernameInput.readOnly) {
+      if (usernameInput.value == username) {
         passwordInput.value = password;
         filled = true;
       }
+    } else {
+      // Setting input fields via .value assignment does not trigger all
+      // the events that a web site can observe. This has the effect of
+      // certain web sites rejecting an autofilled sign in form as not
+      // signed in because the user didn't actually "typed" into the field.
+      // Adding the .focus() works around this problems.
+      usernameInput.focus();
+      usernameInput.value = username;
+      passwordInput.focus();
+      passwordInput.value = password;
+      filled = true;
     }
+  }
 
-    // Recursively invoke for all iframes.
-    var frames = getSameOriginFrames_(win);
-    for (var i = 0; i < frames.length; i++) {
-      if (fillPasswordFormWithData_(
-              formData, username, password, frames[i], opt_normalizedOrigin)) {
-        filled = true;
-      }
+  // Recursively invoke for all iframes.
+  var frames = getSameOriginFrames_(win);
+  for (var i = 0; i < frames.length; i++) {
+    if (fillPasswordFormWithData_(
+            formData, username, password, frames[i], opt_normalizedOrigin)) {
+      filled = true;
     }
+  }
 
-    return filled;
-  };
+  return filled;
+};
 
-  /**
-   * Finds all forms with passwords in the supplied window or frame and appends
-   * JS objects containing the form data to |formDataList|.
-   * @param {!Array.<Object>} formDataList A list that this function populates
-   *     with descriptions of discovered forms.
-   * @param {Window} win A window (or frame) in which the function should
-   *    look for password forms.
-   */
-  var getPasswordFormDataList_ = function(formDataList, win) {
-    var doc = win.document;
-    var forms = doc.forms;
-    for (var i = 0; i < forms.length; i++) {
-      var formData = __gCrWeb.getPasswordFormData(forms[i]);
-      if (formData) {
-        formDataList.push(formData);
-        addSubmitButtonTouchEndHandler_(forms[i]);
-      }
+/**
+ * Finds all forms with passwords in the supplied window or frame and appends
+ * JS objects containing the form data to |formDataList|.
+ * @param {!Array.<Object>} formDataList A list that this function populates
+ *     with descriptions of discovered forms.
+ * @param {Window} win A window (or frame) in which the function should
+ *    look for password forms.
+ */
+var getPasswordFormDataList_ = function(formDataList, win) {
+  var doc = win.document;
+  var forms = doc.forms;
+  for (var i = 0; i < forms.length; i++) {
+    var formData = __gCrWeb.passwords.getPasswordFormData(forms[i]);
+    if (formData) {
+      formDataList.push(formData);
+      addSubmitButtonTouchEndHandler_(forms[i]);
     }
+  }
 
-    // Recursively invoke for all iframes.
-    var frames = getSameOriginFrames_(win);
-    for (var i = 0; i < frames.length; i++) {
-      getPasswordFormDataList_(formDataList, frames[i]);
-    }
-  };
+  // Recursively invoke for all iframes.
+  var frames = getSameOriginFrames_(win);
+  for (var i = 0; i < frames.length; i++) {
+    getPasswordFormDataList_(formDataList, frames[i]);
+  }
+};
 
-  /**
-   * Returns a JS object containing the data from |formElement|.
-   * @param {HTMLFormElement} formElement An HTML Form element.
-   * @return {Object} Object of data from formElement.
-   */
-  __gCrWeb.getPasswordFormData = function(formElement) {
-      var extractMask = __gCrWeb.fill.EXTRACT_MASK_VALUE;
-      var formData = {}
-      var ok = __gCrWeb.fill.webFormElementToFormData(
-        window, formElement,  null /* formControlElement */,
-        extractMask, formData, null /* field */);
-      return ok ? formData : null;
-  };
-}
+/**
+ * Returns a JS object containing the data from |formElement|.
+ * @param {HTMLFormElement} formElement An HTML Form element.
+ * @return {Object} Object of data from formElement.
+ */
+__gCrWeb.passwords.getPasswordFormData = function(formElement) {
+  var extractMask = __gCrWeb.fill.EXTRACT_MASK_VALUE;
+  var formData = {};
+  var ok = __gCrWeb.fill.webFormElementToFormData(
+      window, formElement, null /* formControlElement */, extractMask, formData,
+      null /* field */);
+  return ok ? formData : null;
+};
+
+}());  // End of anonymous object
diff --git a/ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.cc b/ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.cc
index d5bd390..794b6d5 100644
--- a/ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.cc
@@ -29,6 +29,7 @@
 #include "ios/chrome/browser/services/gcm/ios_chrome_gcm_profile_service_factory.h"
 #include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
 #include "ios/chrome/browser/signin/about_signin_internals_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/signin/oauth2_token_service_factory.h"
 #include "ios/chrome/browser/signin/signin_client_factory.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
@@ -105,6 +106,7 @@
   DependsOn(ios::SigninManagerFactory::GetInstance());
   DependsOn(ios::TemplateURLServiceFactory::GetInstance());
   DependsOn(ios::WebDataServiceFactory::GetInstance());
+  DependsOn(IdentityManagerFactory::GetInstance());
   DependsOn(IOSChromeGCMProfileServiceFactory::GetInstance());
   DependsOn(IOSChromePasswordStoreFactory::GetInstance());
   DependsOn(IOSChromeProfileInvalidationProviderFactory::GetInstance());
@@ -121,11 +123,6 @@
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
 
-  SigninManagerBase* signin =
-      ios::SigninManagerFactory::GetForBrowserState(browser_state);
-  SigninClient* signin_client =
-      SigninClientFactory::GetForBrowserState(browser_state);
-
   // Always create the GCMProfileService instance such that we can listen to
   // the profile notifications and purge the GCM store when the profile is
   // being signed out.
@@ -136,9 +133,12 @@
   ios::AboutSigninInternalsFactory::GetForBrowserState(browser_state);
 
   ProfileSyncService::InitParams init_params;
-  init_params.signin_wrapper = std::make_unique<SigninManagerWrapper>(signin);
+  init_params.signin_wrapper = std::make_unique<SigninManagerWrapper>(
+      IdentityManagerFactory::GetForBrowserState(browser_state),
+      ios::SigninManagerFactory::GetForBrowserState(browser_state));
   init_params.signin_scoped_device_id_callback = base::BindRepeating(
-      &SigninClient::GetSigninScopedDeviceId, base::Unretained(signin_client));
+      &SigninClient::GetSigninScopedDeviceId,
+      base::Unretained(SigninClientFactory::GetForBrowserState(browser_state)));
   init_params.oauth2_token_service =
       OAuth2TokenServiceFactory::GetForBrowserState(browser_state);
   init_params.start_behavior = ProfileSyncService::MANUAL_START;
diff --git a/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc b/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc
index c5373bd..02d9207 100644
--- a/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc
+++ b/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc
@@ -15,6 +15,7 @@
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/sync/driver/signin_manager_wrapper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/signin/oauth2_token_service_factory.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "ios/chrome/browser/sync/ios_chrome_sync_client.h"
@@ -27,6 +28,7 @@
   browser_sync::ProfileSyncService::InitParams init_params;
 
   init_params.signin_wrapper = std::make_unique<SigninManagerWrapper>(
+      IdentityManagerFactory::GetForBrowserState(browser_state),
       ios::SigninManagerFactory::GetForBrowserState(browser_state));
   init_params.signin_scoped_device_id_callback =
       base::BindRepeating([]() { return std::string(); });
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
index 04902648..15c38bd 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
@@ -20,6 +20,21 @@
 class ContentSuggestionsCollectionUtilsTest : public PlatformTest {
  public:
   void SetAsIPad() {
+    UITraitCollection* horizontalRegular = [UITraitCollection
+        traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
+    UITraitCollection* verticalRegular = [UITraitCollection
+        traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
+    customTraitCollection_ =
+        [UITraitCollection traitCollectionWithTraitsFromCollections:@[
+          verticalRegular, horizontalRegular
+        ]];
+
+    trait_swizzler_ = std::make_unique<ScopedBlockSwizzler>(
+        [UIWindow class], @selector(traitCollection),
+        ^UITraitCollection*(id self) {
+          return customTraitCollection_;
+        });
+
     device_type_swizzler_ = std::make_unique<ScopedBlockSwizzler>(
         [UIDevice class], @selector(userInterfaceIdiom),
         ^UIUserInterfaceIdiom(id self) {
@@ -27,6 +42,21 @@
         });
   }
   void SetAsIPhone() {
+    UITraitCollection* horizontalCompact = [UITraitCollection
+        traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
+    UITraitCollection* verticalCompact = [UITraitCollection
+        traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
+    customTraitCollection_ =
+        [UITraitCollection traitCollectionWithTraitsFromCollections:@[
+          verticalCompact, horizontalCompact
+        ]];
+
+    trait_swizzler_ = std::make_unique<ScopedBlockSwizzler>(
+        [UIWindow class], @selector(traitCollection),
+        ^UITraitCollection*(id self) {
+          return customTraitCollection_;
+        });
+
     device_type_swizzler_ = std::make_unique<ScopedBlockSwizzler>(
         [UIDevice class], @selector(userInterfaceIdiom),
         ^UIUserInterfaceIdiom(id self) {
@@ -49,6 +79,8 @@
   }
 
  private:
+  UITraitCollection* customTraitCollection_;
+  std::unique_ptr<ScopedBlockSwizzler> trait_swizzler_;
   std::unique_ptr<ScopedBlockSwizzler> device_type_swizzler_;
   std::unique_ptr<ScopedBlockSwizzler> orientation_swizzler_;
 };
@@ -57,22 +89,26 @@
   // Setup.
   SetAsIPhone();
 
-  // Action.
-  CGFloat result = centeredTilesMarginForWidth(374);
-
-  // Tests.
-  EXPECT_EQ(17, result);
+  if (IsUIRefreshPhase1Enabled()) {
+    CGFloat result = centeredTilesMarginForWidth(375);
+    EXPECT_EQ(28, result);
+  } else {
+    CGFloat result = centeredTilesMarginForWidth(374);
+    EXPECT_EQ(17, result);
+  }
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, centeredTilesMarginIPad) {
   // Setup.
   SetAsIPad();
 
-  // Action.
-  CGFloat result = centeredTilesMarginForWidth(700);
-
-  // Tests.
-  EXPECT_EQ(168, result);
+  if (IsUIRefreshPhase1Enabled()) {
+    CGFloat result = centeredTilesMarginForWidth(767);
+    EXPECT_EQ(209, result);
+  } else {
+    CGFloat result = centeredTilesMarginForWidth(700);
+    EXPECT_EQ(168, result);
+  }
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPad) {
@@ -86,8 +122,8 @@
 
   // Test.
   if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(68, height);
-    EXPECT_EQ(48, topMargin);
+    EXPECT_EQ(120, height);
+    EXPECT_EQ(162, topMargin);
   } else {
     EXPECT_EQ(120, height);
     EXPECT_EQ(82, topMargin);
@@ -108,7 +144,7 @@
 
   // Test.
   if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(68, heightLogo);
+    EXPECT_EQ(120, heightLogo);
     EXPECT_EQ(60, heightNoLogo);
     EXPECT_EQ(48, topMargin);
   } else {
@@ -132,7 +168,7 @@
 
   // Test.
   if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(68, heightLogo);
+    EXPECT_EQ(120, heightLogo);
     EXPECT_EQ(60, heightNoLogo);
     EXPECT_EQ(48, topMargin);
   } else {
@@ -158,8 +194,8 @@
   // Test.
   if (IsUIRefreshPhase1Enabled()) {
     EXPECT_EQ(32, topMargin);
-    EXPECT_EQ(343, resultWidth);
-    EXPECT_EQ(343, resultWidthLargeIPad);
+    EXPECT_EQ(432, resultWidth);
+    EXPECT_EQ(432, resultWidthLargeIPad);
   } else {
     EXPECT_EQ(82, topMargin);
     EXPECT_EQ(width - 2 * margin, resultWidth);
@@ -215,10 +251,10 @@
 
   // Action, tests.
   if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(214, heightForLogoHeader(YES, YES, YES));
-    EXPECT_EQ(238, heightForLogoHeader(YES, NO, YES));
-    EXPECT_EQ(214, heightForLogoHeader(YES, YES, NO));
-    EXPECT_EQ(238, heightForLogoHeader(YES, NO, NO));
+    EXPECT_EQ(380, heightForLogoHeader(YES, YES, YES));
+    EXPECT_EQ(404, heightForLogoHeader(YES, NO, YES));
+    EXPECT_EQ(380, heightForLogoHeader(YES, YES, NO));
+    EXPECT_EQ(404, heightForLogoHeader(YES, NO, NO));
   } else {
     EXPECT_EQ(350, heightForLogoHeader(YES, YES, YES));
     EXPECT_EQ(374, heightForLogoHeader(YES, NO, YES));
@@ -233,10 +269,10 @@
 
   // Action, tests.
   if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(214, heightForLogoHeader(YES, YES, YES));
-    EXPECT_EQ(214, heightForLogoHeader(YES, NO, YES));
-    EXPECT_EQ(214, heightForLogoHeader(YES, YES, NO));
-    EXPECT_EQ(214, heightForLogoHeader(YES, NO, NO));
+    EXPECT_EQ(266, heightForLogoHeader(YES, YES, YES));
+    EXPECT_EQ(266, heightForLogoHeader(YES, NO, YES));
+    EXPECT_EQ(266, heightForLogoHeader(YES, YES, NO));
+    EXPECT_EQ(266, heightForLogoHeader(YES, NO, NO));
   } else {
     EXPECT_EQ(274, heightForLogoHeader(YES, YES, YES));
     EXPECT_EQ(274, heightForLogoHeader(YES, NO, YES));
diff --git a/ios/chrome/browser/ui/download/download_manager_view_controller.mm b/ios/chrome/browser/ui/download/download_manager_view_controller.mm
index cccedfd..beda02c 100644
--- a/ios/chrome/browser/ui/download/download_manager_view_controller.mm
+++ b/ios/chrome/browser/ui/download/download_manager_view_controller.mm
@@ -633,8 +633,8 @@
                                     : self.actionButton;
 
   self.statusLabelTrailingConstraint = [self.statusLabel.trailingAnchor
-      constraintEqualToAnchor:secondAnchorElement.leadingAnchor
-                     constant:-kElementMargin];
+      constraintLessThanOrEqualToAnchor:secondAnchorElement.leadingAnchor
+                               constant:-kElementMargin];
 
   self.statusLabelTrailingConstraint.active = YES;
 }
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.mm b/ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.mm
index 15ef410c..fc63817 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.mm
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.mm
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/logging.h"
-#include "base/metrics/field_trial.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
@@ -95,23 +94,11 @@
   modified_time = synced_session->modified_time;
   device_type = synced_session->device_type;
 
-  const std::string group_name =
-      base::FieldTrialList::FindFullName("TabSyncByRecency");
-  if (group_name == "Enabled") {
-    // Order tabs by recency.
-    std::vector<const sessions::SessionTab*> session_tabs;
-    open_tabs->GetForeignSessionTabs(synced_session->session_tag,
-                                     &session_tabs);
-    for (const auto* session_tab : session_tabs) {
+  // Order tabs by their visual position within window.
+  for (const auto& kv : synced_session->windows) {
+    for (const auto& session_tab : kv.second->wrapped_window.tabs) {
       AddTabToDistantSession(*session_tab, synced_session->session_tag, this);
     }
-  } else {
-    // Order tabs by their visual position within window.
-    for (const auto& kv : synced_session->windows) {
-      for (const auto& session_tab : kv.second->wrapped_window.tabs) {
-        AddTabToDistantSession(*session_tab, synced_session->session_tag, this);
-      }
-    }
   }
 }
 
diff --git a/ios/chrome/browser/ui/popup_menu/BUILD.gn b/ios/chrome/browser/ui/popup_menu/BUILD.gn
index 003b8271..4a14b4c 100644
--- a/ios/chrome/browser/ui/popup_menu/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/BUILD.gn
@@ -65,7 +65,6 @@
     "resources:popup_menu_request_desktop_site",
     "resources:popup_menu_request_mobile_site",
     "resources:popup_menu_settings",
-    "resources:popup_menu_shadow",
     "resources:popup_menu_site_information",
     "resources:popup_menu_stop",
     "//base",
@@ -75,6 +74,7 @@
     "//ios/chrome/browser/ui/image_util",
     "//ios/chrome/browser/ui/popup_menu/cells",
     "//ios/chrome/browser/ui/presenters",
+    "//ios/chrome/browser/ui/resources:menu_shadow",
     "//ios/chrome/browser/ui/table_view",
     "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/popup_menu/cells/BUILD.gn b/ios/chrome/browser/ui/popup_menu/cells/BUILD.gn
index 76200de..9aeb4a3 100644
--- a/ios/chrome/browser/ui/popup_menu/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/cells/BUILD.gn
@@ -15,6 +15,7 @@
   ]
   deps = [
     "//base",
+    "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/reading_list:reading_list_ui",
     "//ios/chrome/browser/ui/table_view:styler",
     "//ios/chrome/browser/ui/table_view/cells",
diff --git a/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm b/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm
index 27ed98a..220bb3c 100644
--- a/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm
+++ b/ios/chrome/browser/ui/popup_menu/cells/popup_menu_tools_item.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/reading_list/number_badge_view.h"
 #import "ios/chrome/browser/ui/reading_list/text_badge_view.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/util/constraints_ui_util.h"
 #import "ios/chrome/common/material_timing.h"
 
@@ -18,13 +19,14 @@
 #endif
 
 namespace {
+const int kEnabledColor = 0x1A73E8;
 const CGFloat kImageLength = 28;
 const CGFloat kCellHeight = 44;
 const CGFloat kInnerMargin = 11;
 const CGFloat kMargin = 15;
 const CGFloat kTopMargin = 8;
 const CGFloat kTopMarginBadge = 14;
-}
+}  // namespace
 
 @implementation PopupMenuToolsItem
 
@@ -236,8 +238,8 @@
 - (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled {
   [super setUserInteractionEnabled:userInteractionEnabled];
   if (userInteractionEnabled) {
-    self.titleLabel.textColor = self.tintColor;
-    self.imageView.tintColor = self.tintColor;
+    self.titleLabel.textColor = UIColorFromRGB(kEnabledColor);
+    self.imageView.tintColor = UIColorFromRGB(kEnabledColor);
   } else {
     self.titleLabel.textColor = [[self class] disabledColor];
     self.imageView.tintColor = [[self class] disabledColor];
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_view_controller.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_view_controller.mm
index d6b28f9e8..f36ff1e 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_view_controller.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_view_controller.mm
@@ -60,8 +60,8 @@
   _contentContainer.backgroundColor =
       [UIColor colorWithWhite:kBackgroundGreyScale alpha:1];
 
-  UIImageView* shadow = [[UIImageView alloc]
-      initWithImage:StretchableImageNamed(@"popup_menu_shadow")];
+  UIImageView* shadow =
+      [[UIImageView alloc] initWithImage:StretchableImageNamed(@"menu_shadow")];
   [_contentContainer addSubview:shadow];
   shadow.translatesAutoresizingMaskIntoConstraints = NO;
   AddSameConstraintsToSidesWithInsets(
diff --git a/ios/chrome/browser/ui/popup_menu/resources/BUILD.gn b/ios/chrome/browser/ui/popup_menu/resources/BUILD.gn
index 36375a6f..a93df83 100644
--- a/ios/chrome/browser/ui/popup_menu/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/popup_menu/resources/BUILD.gn
@@ -58,15 +58,6 @@
   ]
 }
 
-imageset("popup_menu_shadow") {
-  sources = [
-    "popup_menu_shadow.imageset/Contents.json",
-    "popup_menu_shadow.imageset/popup_menu_shadow.png",
-    "popup_menu_shadow.imageset/popup_menu_shadow@2x.png",
-    "popup_menu_shadow.imageset/popup_menu_shadow@3x.png",
-  ]
-}
-
 imageset("popup_menu_new_incognito_tab") {
   sources = [
     "popup_menu_new_incognito_tab.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/resources/BUILD.gn b/ios/chrome/browser/ui/resources/BUILD.gn
index 8e14b77..408fe06 100644
--- a/ios/chrome/browser/ui/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/resources/BUILD.gn
@@ -61,3 +61,12 @@
     "keyboard_button.imageset/keyboard_button~ipad.png",
   ]
 }
+
+imageset("menu_shadow") {
+  sources = [
+    "menu_shadow.imageset/Contents.json",
+    "menu_shadow.imageset/menu_shadow.png",
+    "menu_shadow.imageset/menu_shadow@2x.png",
+    "menu_shadow.imageset/menu_shadow@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/Contents.json b/ios/chrome/browser/ui/resources/menu_shadow.imageset/Contents.json
similarity index 69%
rename from ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/Contents.json
rename to ios/chrome/browser/ui/resources/menu_shadow.imageset/Contents.json
index 2f7a347..c07c84b 100644
--- a/ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/Contents.json
+++ b/ios/chrome/browser/ui/resources/menu_shadow.imageset/Contents.json
@@ -3,17 +3,17 @@
         {
             "idiom": "universal",
             "scale": "1x",
-            "filename": "popup_menu_shadow.png"
+            "filename": "menu_shadow.png"
         },
         {
             "idiom": "universal",
             "scale": "2x",
-            "filename": "popup_menu_shadow@2x.png"
+            "filename": "menu_shadow@2x.png"
         },
         {
             "idiom": "universal",
             "scale": "3x",
-            "filename": "popup_menu_shadow@3x.png"
+            "filename": "menu_shadow@3x.png"
         }
     ],
     "info": {
diff --git a/ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/popup_menu_shadow.png b/ios/chrome/browser/ui/resources/menu_shadow.imageset/menu_shadow.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/popup_menu_shadow.png
rename to ios/chrome/browser/ui/resources/menu_shadow.imageset/menu_shadow.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/popup_menu_shadow@2x.png b/ios/chrome/browser/ui/resources/menu_shadow.imageset/menu_shadow@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/popup_menu_shadow@2x.png
rename to ios/chrome/browser/ui/resources/menu_shadow.imageset/menu_shadow@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/popup_menu_shadow@3x.png b/ios/chrome/browser/ui/resources/menu_shadow.imageset/menu_shadow@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/popup_menu/resources/popup_menu_shadow.imageset/popup_menu_shadow@3x.png
rename to ios/chrome/browser/ui/resources/menu_shadow.imageset/menu_shadow@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index 7ca9748..d6e3b78 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -29,6 +29,14 @@
   TabGridConfigurationBottomToolbar = 1,
   TabGridConfigurationFloatingButton,
 };
+
+// Computes the page from the offset and width of |scrollView|.
+TabGridPage GetPageFromScrollView(UIScrollView* scrollView) {
+  // TODO(crbug.com/822328) : Fix for RTL.
+  CGFloat pageWidth = scrollView.frame.size.width;
+  float fractionalPage = scrollView.contentOffset.x / pageWidth;
+  return static_cast<TabGridPage>(lround(fractionalPage));
+}
 }  // namespace
 
 @interface TabGridViewController ()<GridViewControllerDelegate,
@@ -169,19 +177,20 @@
         self.scrollView.contentSize.width - self.scrollView.frame.size.width;
     CGFloat offset = scrollView.contentOffset.x / offsetWidth;
     self.topToolbar.pageControl.sliderPosition = offset;
-  }
 
-  // Bookkeeping for the current page.
-  // TODO(crbug.com/822328) : Fix for RTL.
-  CGFloat pageWidth = scrollView.frame.size.width;
-  float fractionalPage = scrollView.contentOffset.x / pageWidth;
-  NSUInteger page = lround(fractionalPage);
-  if (page != self.currentPage) {
-    _currentPage = static_cast<TabGridPage>(page);
-    [self configureButtonsForOriginalAndCurrentPage];
+    TabGridPage page = GetPageFromScrollView(scrollView);
+    if (page != _currentPage) {
+      _currentPage = page;
+      [self configureButtonsForOriginalAndCurrentPage];
+    }
   }
 }
 
+- (void)scrollViewDidEndScrollingAnimation:(UIScrollView*)scrollView {
+  _currentPage = GetPageFromScrollView(scrollView);
+  [self configureButtonsForOriginalAndCurrentPage];
+}
+
 #pragma mark - UIScrollViewAccessibilityDelegate
 
 - (NSString*)accessibilityScrollStatusForScrollView:(UIScrollView*)scrollView {
@@ -280,7 +289,7 @@
     _currentPage = currentPage;
   } else {
     [self.scrollView setContentOffset:offset animated:YES];
-    // _currentPage is set in scrollViewDidScroll:
+    // _currentPage is set in scrollViewDidEndScrollingAnimation:
   }
 }
 
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index cb2d34ea..12ff25a 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -121,6 +121,7 @@
   sources = [
     "//components/autofill/ios/browser/resources/autofill_controller.js",
     "//components/autofill/ios/fill/resources/fill.js",
+    "//ios/chrome/browser/passwords/resources/password_controller.js",
     "resources/chrome_bundle_main_frame.js",
     "resources/print.js",
   ]
diff --git a/ios/chrome/browser/web/resources/chrome_bundle_main_frame.js b/ios/chrome/browser/web/resources/chrome_bundle_main_frame.js
index e5a9caf..23cee10 100644
--- a/ios/chrome/browser/web/resources/chrome_bundle_main_frame.js
+++ b/ios/chrome/browser/web/resources/chrome_bundle_main_frame.js
@@ -7,4 +7,5 @@
 
 goog.require('__crWeb.autofill');
 goog.require('__crWeb.fill');
+goog.require('__crWeb.passwords');
 goog.require('__crWeb.print');
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index 893e5ba99..f565051c 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -388,7 +388,10 @@
 
 - (void)cancelContextMenuDisplay {
   _contextMenuNeedsDisplay = NO;
-  [_pendingElementFetchRequests removeAllObjects];
+  for (HTMLElementFetchRequest* fetchRequest in _pendingElementFetchRequests
+           .allValues) {
+    [fetchRequest invalidate];
+  }
 }
 
 #pragma mark -
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 6493b5e..00d73c17 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -2012,7 +2012,11 @@
   // nothing if the document has already loaded.
   if (_loadPhase == web::PAGE_LOADED)
     return;
-  [self loadCompleteWithSuccess:YES forNavigation:navigation];
+
+  web::NavigationContextImpl* context =
+      [_navigationStates contextForNavigation:navigation];
+  BOOL success = !context || !context->GetError();
+  [self loadCompleteWithSuccess:success forNavigation:navigation];
 }
 
 - (void)loadCompleteWithSuccess:(BOOL)loadSuccess
@@ -4697,7 +4701,7 @@
         // of session restoration. It is now safe to update the navigation
         // item URL to the original app-specific URL.
         item->SetURL(originalURL);
-      } else if (item->GetURL() != originalURL) {
+      } else if (item->GetVirtualURL() != originalURL) {
         // The |didFinishNavigation| callback can arrive after another
         // navigation has started. Abort in this case.
         return;
@@ -4731,29 +4735,34 @@
       }
     }
 
-    // Handle state transitions for retrying a previously failed navigation.
-    switch (errorRetryState) {
-      case web::ErrorRetryState::kDisplayingErrorForFailedNavigation:
-        DCHECK(context->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK);
-        if (item->GetURL() == webViewURL) {
-          // Shortcut: if WebView already has the original URL (can happen when
-          // WebKit renders page from cache after after repeated back/forward
-          // navigations), skip kNavigatingToFailedNavigationItem state and just
-          // reload the page.
+    // Handle state transitions for retrying a previously failed navigation to a
+    // web URL. App-specific URLs should never fail to load so should not change
+    // the error retry state.
+    if (!web::GetWebClient()->IsAppSpecificURL(item->GetURL())) {
+      switch (errorRetryState) {
+        case web::ErrorRetryState::kDisplayingErrorForFailedNavigation:
+          DCHECK(context->GetPageTransition() &
+                 ui::PAGE_TRANSITION_FORWARD_BACK);
+          if (item->GetURL() == webViewURL) {
+            // Shortcut: if WebView already has the original URL (can happen
+            // when WebKit renders page from cache after after repeated
+            // back/forward navigations), skip kNavigatingToFailedNavigationItem
+            // state and just reload the page.
+            [self handleRetryFailedNavigationItem:item];
+          } else {
+            [self handleNavigationToFailedNavigationItem:item];
+          }
+          break;
+        case web::ErrorRetryState::kNavigatingToFailedNavigationItem:
           [self handleRetryFailedNavigationItem:item];
-        } else {
-          [self handleNavigationToFailedNavigationItem:item];
-        }
-        break;
-      case web::ErrorRetryState::kNavigatingToFailedNavigationItem:
-        [self handleRetryFailedNavigationItem:item];
-        break;
-      case web::ErrorRetryState::kRetryFailedNavigationItem:
-        item->SetErrorRetryState(web::ErrorRetryState::kNoNavigationError);
-        break;
-      case web::ErrorRetryState::kNoNavigationError:
-      case web::ErrorRetryState::kReadyToDisplayErrorForFailedNavigation:
-        break;
+          break;
+        case web::ErrorRetryState::kRetryFailedNavigationItem:
+          item->SetErrorRetryState(web::ErrorRetryState::kNoNavigationError);
+          break;
+        case web::ErrorRetryState::kNoNavigationError:
+        case web::ErrorRetryState::kReadyToDisplayErrorForFailedNavigation:
+          break;
+      }
     }
   }
 
diff --git a/ios/web/web_state/ui/html_element_fetch_request.h b/ios/web/web_state/ui/html_element_fetch_request.h
index 4571948..9aa02d35 100644
--- a/ios/web/web_state/ui/html_element_fetch_request.h
+++ b/ios/web/web_state/ui/html_element_fetch_request.h
@@ -24,8 +24,12 @@
     (void (^)(NSDictionary*))foundElementHandler NS_DESIGNATED_INITIALIZER;
 
 // Calls the |foundElementHandler| from the receiver's initializer with
-// |response| as the parameter.
+// |response| as the parameter. This method has no effect if |invalidate| has
+// been called.
 - (void)runHandlerWithResponse:(NSDictionary*)response;
+// Removes the stored |foundElementHandler| from the receiver's initializer.
+// |runHandlerWithResponse:| will have no effect if called after |invalidate|.
+- (void)invalidate;
 
 @end
 
diff --git a/ios/web/web_state/ui/html_element_fetch_request.mm b/ios/web/web_state/ui/html_element_fetch_request.mm
index 89861e70..0835371 100644
--- a/ios/web/web_state/ui/html_element_fetch_request.mm
+++ b/ios/web/web_state/ui/html_element_fetch_request.mm
@@ -31,7 +31,13 @@
 }
 
 - (void)runHandlerWithResponse:(NSDictionary*)response {
-  _foundElementHandler(response);
+  if (_foundElementHandler) {
+    _foundElementHandler(response);
+  }
+}
+
+- (void)invalidate {
+  _foundElementHandler = nullptr;
 }
 
 @end
diff --git a/ios/web/web_state/ui/html_element_fetch_request_unittest.mm b/ios/web/web_state/ui/html_element_fetch_request_unittest.mm
index bfd5731a..58b4b563 100644
--- a/ios/web/web_state/ui/html_element_fetch_request_unittest.mm
+++ b/ios/web/web_state/ui/html_element_fetch_request_unittest.mm
@@ -45,4 +45,18 @@
   EXPECT_NSEQ(response, received_response);
 }
 
+// Tests that |runHandlerWithResponse:| does not run the handler from the
+// object's initializer if |invalidate| has been called.
+TEST_F(HtmlElementFetchRequestTest, Invalidate) {
+  __block bool handler_called = false;
+  void (^handler)(NSDictionary*) = ^(NSDictionary* response) {
+    handler_called = true;
+  };
+  HTMLElementFetchRequest* request =
+      [[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
+  [request invalidate];
+  [request runHandlerWithResponse:nil];
+  EXPECT_FALSE(handler_called);
+}
+
 }  // namespace web
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 57d006f..900e3c0 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -313,6 +313,7 @@
     "//components/autofill/ios/browser/resources/autofill_controller.js",
     "//components/autofill/ios/fill/resources/fill.js",
     "//components/autofill/ios/fill/resources/form.js",
+    "//ios/chrome/browser/passwords/resources/password_controller.js",
     "resources/web_view_bundle.js",
   ]
 }
diff --git a/ios/web_view/resources/web_view_bundle.js b/ios/web_view/resources/web_view_bundle.js
index 0eec4949..acb4dac 100644
--- a/ios/web_view/resources/web_view_bundle.js
+++ b/ios/web_view/resources/web_view_bundle.js
@@ -8,3 +8,4 @@
 goog.require('__crWeb.autofill');
 goog.require('__crWeb.fill');
 goog.require('__crWeb.form');
+goog.require('__crWeb.passwords');
diff --git a/ios/web_view/tools/build.py b/ios/web_view/tools/build.py
index c6511ff..c4aa03a 100755
--- a/ios/web_view/tools/build.py
+++ b/ios/web_view/tools/build.py
@@ -19,7 +19,7 @@
     build_config: A string describing the build configuration. Ex: 'Debug'
     target_device: A string describing the target device. Ex: 'simulator'
   """
-  return '%s-iphone%s' % (build_config, target_device)
+  return '%s-%s' % (build_config, target_device)
 
 def build(build_config, target_device, extra_gn_options, extra_ninja_options):
   """Generates and builds ChromeWebView.framework.
@@ -36,7 +36,7 @@
     The return code of generating ninja if it is non-zero, else the return code
       of the ninja build command.
   """
-  if target_device == 'os':
+  if target_device == 'iphoneos':
     target_cpu = 'arm'
     additional_cpu = 'arm64'
   else:
@@ -133,7 +133,7 @@
   return 0
 
 def package_all_frameworks(out_dir, output_name, extra_gn_options,
-                           extra_ninja_options):
+                           build_configs, target_devices, extra_ninja_options):
   """Builds ChromeWebView.framework.
 
   Builds Release and Debug versions of ChromeWebView.framework for both
@@ -143,6 +143,8 @@
     out_dir: A string to the path which all build products will be copied.
     extra_gn_options: A string of gn args (space separated key=value items) to
       be appended to the gn gen command.
+    build_configs: A list of configs to build.
+    target_devices: A list of devices to target.
     extra_ninja_options: A string of gn options to be appended to the ninja
       command.
 
@@ -154,11 +156,8 @@
   # Package all builds in the output directory
   os.makedirs(out_dir)
 
-  configurations = [('Debug', 'simulator'),
-                    ('Debug', 'os'),
-                    ('Release', 'simulator'),
-                    ('Release', 'os')]
-  for build_config, target_device in configurations:
+  configs_and_devices = [(a,b) for a in build_configs for b in target_devices]
+  for build_config, target_device in configs_and_devices:
     if package_framework(build_config,
                          target_device,
                          out_dir,
@@ -192,6 +191,14 @@
                       help='Combines Cronet and ChromeWebView as 1 framework.')
   parser.add_argument('--enable_sync', action='store_true',
                       help='Enables public API for ChromeSync.')
+  build_configs = ['Debug', 'Release']
+  target_devices = ['iphonesimulator', 'iphoneos']
+  parser.add_argument('--build_configs', nargs='+', default=build_configs,
+                      choices=build_configs,
+                      help='Specify which configs to build.')
+  parser.add_argument('--target_devices', nargs='+', default=target_devices,
+                      choices=target_devices,
+                      help='Specify which devices to target.')
 
   options, extra_options = parser.parse_known_args()
   print 'Options:', options
@@ -222,6 +229,8 @@
   extra_gn_options += 'ios_web_view_output_name="%s" ' % output_name
 
   return package_all_frameworks(out_dir, output_name, extra_gn_options,
+                                set(options.build_configs),
+                                set(options.target_devices),
                                 options.ninja_args)
 
 if __name__ == '__main__':
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
index acc04df..0e47699 100644
--- a/media/filters/vpx_video_decoder.cc
+++ b/media/filters/vpx_video_decoder.cc
@@ -374,7 +374,7 @@
       ->metadata()
       ->SetInteger(VideoFrameMetadata::COLOR_SPACE, color_space);
 
-  if (config_.color_space_info() != VideoColorSpace()) {
+  if (config_.color_space_info().IsSpecified()) {
     // config_.color_space_info() comes from the color tag which is
     // more expressive than the bitstream, so prefer it over the
     // bitstream data below.
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index b716573bb..3e288d1e 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -746,19 +746,25 @@
   }
 
   // And create a context associated with them.
-  va_res = vaCreateContext(va_display_, va_config_id_, size.width(),
-                           size.height(), VA_PROGRESSIVE, &va_surface_ids_[0],
-                           va_surface_ids_.size(), &va_context_id_);
+  const bool success = CreateContext(va_format, size, va_surface_ids_);
+  if (success)
+    *va_surfaces = va_surface_ids_;
+  else
+    DestroySurfaces_Locked();
+  return success;
+}
+
+bool VaapiWrapper::CreateContext(unsigned int va_format,
+                                 const gfx::Size& size,
+                                 const std::vector<VASurfaceID>& va_surfaces) {
+  VAStatus va_res = vaCreateContext(
+      va_display_, va_config_id_, size.width(), size.height(), VA_PROGRESSIVE,
+      &va_surface_ids_[0], va_surface_ids_.size(), &va_context_id_);
 
   VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
-  if (va_res != VA_STATUS_SUCCESS) {
-    DestroySurfaces_Locked();
-    return false;
-  }
-
-  *va_surfaces = va_surface_ids_;
-  va_surface_format_ = va_format;
-  return true;
+  if (va_res == VA_STATUS_SUCCESS)
+    va_surface_format_ = va_format;
+  return va_res == VA_STATUS_SUCCESS;
 }
 
 void VaapiWrapper::DestroySurfaces() {
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index f019e7a..f77ced99 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -89,7 +89,7 @@
   // Return true when JPEG encode is supported.
   static bool IsJpegEncodeSupported();
 
-  // Create |num_surfaces| backing surfaces in driver for VASurfaces of
+  // Creates |num_surfaces| backing surfaces in driver for VASurfaces of
   // |va_format|, each of size |size|. Returns true when successful, with the
   // created IDs in |va_surfaces| to be managed and later wrapped in
   // VASurfaces.
@@ -102,7 +102,12 @@
                               size_t num_surfaces,
                               std::vector<VASurfaceID>* va_surfaces);
 
-  // Free all memory allocated in CreateSurfaces.
+  // Creates a VA Context associated with the set of |va_surfaces| of |size|.
+  bool CreateContext(unsigned int va_format,
+                     const gfx::Size& size,
+                     const std::vector<VASurfaceID>& va_surfaces);
+
+  // Frees all memory allocated in CreateSurfaces.
   virtual void DestroySurfaces();
 
   // Create a VASurface for |pixmap|. The ownership of the surface is
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 24f11c2..dd599ca 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -63,13 +63,6 @@
 // We delete the temporary resource if it is not used for 3 seconds.
 const int kTemporaryResourceDeletionDelay = 3;  // Seconds;
 
-bool CheckColorSpace(const VideoFrame* video_frame, ColorSpace color_space) {
-  int result;
-  return video_frame->metadata()->GetInteger(VideoFrameMetadata::COLOR_SPACE,
-                                             &result) &&
-         result == color_space;
-}
-
 class SyncTokenClientImpl : public VideoFrame::SyncTokenClient {
  public:
   explicit SyncTokenClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
@@ -153,11 +146,10 @@
                        GrMipMapped::kNo, source_textures[2]),
   };
 
+  // TODO(hubbe): This should really default to rec709.
+  // https://crbug.com/828599
   SkYUVColorSpace color_space = kRec601_SkYUVColorSpace;
-  if (CheckColorSpace(video_frame, media::COLOR_SPACE_JPEG))
-    color_space = kJPEG_SkYUVColorSpace;
-  else if (CheckColorSpace(video_frame, media::COLOR_SPACE_HD_REC709))
-    color_space = kRec709_SkYUVColorSpace;
+  video_frame->ColorSpace().ToSkYUVColorSpace(&color_space);
 
   sk_sp<SkImage> img;
   if (video_frame->format() == PIXEL_FORMAT_NV12) {
@@ -268,12 +260,11 @@
     }
 
     if (color_space) {
-      if (CheckColorSpace(frame_.get(), COLOR_SPACE_JPEG))
-        *color_space = kJPEG_SkYUVColorSpace;
-      else if (CheckColorSpace(frame_.get(), COLOR_SPACE_HD_REC709))
-        *color_space = kRec709_SkYUVColorSpace;
-      else
+      if (!frame_->ColorSpace().ToSkYUVColorSpace(color_space)) {
+        // TODO(hubbe): This really should default to rec709
+        // https://crbug.com/828599
         *color_space = kRec601_SkYUVColorSpace;
+      }
     }
 
     for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane;
@@ -514,6 +505,7 @@
       format, video_frame->coded_size(), video_frame->visible_rect(),
       video_frame->natural_size(), video_frame->timestamp());
 
+  ret->set_color_space(video_frame->ColorSpace());
   // Copy all metadata.
   // (May be enough to copy color space)
   ret->metadata()->MergeMetadataFrom(video_frame->metadata());
@@ -672,39 +664,48 @@
     return;
   }
 
+  // TODO(hubbe): This should really default to the rec709 colorspace.
+  // https://crbug.com/828599
+  SkYUVColorSpace color_space = kRec601_SkYUVColorSpace;
+  video_frame->ColorSpace().ToSkYUVColorSpace(&color_space);
+
   switch (video_frame->format()) {
     case PIXEL_FORMAT_YV12:
     case PIXEL_FORMAT_I420:
-      if (CheckColorSpace(video_frame, COLOR_SPACE_JPEG)) {
-        LIBYUV_J420_TO_ARGB(video_frame->visible_data(VideoFrame::kYPlane),
-                            video_frame->stride(VideoFrame::kYPlane),
-                            video_frame->visible_data(VideoFrame::kUPlane),
-                            video_frame->stride(VideoFrame::kUPlane),
-                            video_frame->visible_data(VideoFrame::kVPlane),
-                            video_frame->stride(VideoFrame::kVPlane),
-                            static_cast<uint8_t*>(rgb_pixels), row_bytes,
-                            video_frame->visible_rect().width(),
-                            video_frame->visible_rect().height());
-      } else if (CheckColorSpace(video_frame, COLOR_SPACE_HD_REC709)) {
-        LIBYUV_H420_TO_ARGB(video_frame->visible_data(VideoFrame::kYPlane),
-                            video_frame->stride(VideoFrame::kYPlane),
-                            video_frame->visible_data(VideoFrame::kUPlane),
-                            video_frame->stride(VideoFrame::kUPlane),
-                            video_frame->visible_data(VideoFrame::kVPlane),
-                            video_frame->stride(VideoFrame::kVPlane),
-                            static_cast<uint8_t*>(rgb_pixels), row_bytes,
-                            video_frame->visible_rect().width(),
-                            video_frame->visible_rect().height());
-      } else {
-        LIBYUV_I420_TO_ARGB(video_frame->visible_data(VideoFrame::kYPlane),
-                            video_frame->stride(VideoFrame::kYPlane),
-                            video_frame->visible_data(VideoFrame::kUPlane),
-                            video_frame->stride(VideoFrame::kUPlane),
-                            video_frame->visible_data(VideoFrame::kVPlane),
-                            video_frame->stride(VideoFrame::kVPlane),
-                            static_cast<uint8_t*>(rgb_pixels), row_bytes,
-                            video_frame->visible_rect().width(),
-                            video_frame->visible_rect().height());
+      switch (color_space) {
+        case kJPEG_SkYUVColorSpace:
+          LIBYUV_J420_TO_ARGB(video_frame->visible_data(VideoFrame::kYPlane),
+                              video_frame->stride(VideoFrame::kYPlane),
+                              video_frame->visible_data(VideoFrame::kUPlane),
+                              video_frame->stride(VideoFrame::kUPlane),
+                              video_frame->visible_data(VideoFrame::kVPlane),
+                              video_frame->stride(VideoFrame::kVPlane),
+                              static_cast<uint8_t*>(rgb_pixels), row_bytes,
+                              video_frame->visible_rect().width(),
+                              video_frame->visible_rect().height());
+          break;
+        case kRec709_SkYUVColorSpace:
+          LIBYUV_H420_TO_ARGB(video_frame->visible_data(VideoFrame::kYPlane),
+                              video_frame->stride(VideoFrame::kYPlane),
+                              video_frame->visible_data(VideoFrame::kUPlane),
+                              video_frame->stride(VideoFrame::kUPlane),
+                              video_frame->visible_data(VideoFrame::kVPlane),
+                              video_frame->stride(VideoFrame::kVPlane),
+                              static_cast<uint8_t*>(rgb_pixels), row_bytes,
+                              video_frame->visible_rect().width(),
+                              video_frame->visible_rect().height());
+          break;
+        case kRec601_SkYUVColorSpace:
+          LIBYUV_I420_TO_ARGB(video_frame->visible_data(VideoFrame::kYPlane),
+                              video_frame->stride(VideoFrame::kYPlane),
+                              video_frame->visible_data(VideoFrame::kUPlane),
+                              video_frame->stride(VideoFrame::kUPlane),
+                              video_frame->visible_data(VideoFrame::kVPlane),
+                              video_frame->stride(VideoFrame::kVPlane),
+                              static_cast<uint8_t*>(rgb_pixels), row_bytes,
+                              video_frame->visible_rect().width(),
+                              video_frame->visible_rect().height());
+          break;
       }
       break;
     case PIXEL_FORMAT_I422:
@@ -748,7 +749,7 @@
       break;
 
     case PIXEL_FORMAT_YUV420P10:
-      if (CheckColorSpace(video_frame, COLOR_SPACE_HD_REC709)) {
+      if (color_space == kRec709_SkYUVColorSpace) {
         LIBYUV_H010_TO_ARGB(reinterpret_cast<const uint16_t*>(
                                 video_frame->visible_data(VideoFrame::kYPlane)),
                             video_frame->stride(VideoFrame::kYPlane) / 2,
diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc
index 285ccf4..8a346ca9 100644
--- a/mojo/edk/system/channel_win.cc
+++ b/mojo/edk/system/channel_win.cc
@@ -175,10 +175,15 @@
                      DWORD bytes_transfered,
                      DWORD error) override {
     if (error != ERROR_SUCCESS) {
-      if (context == &write_context_)
+      if (context == &write_context_) {
+        {
+          base::AutoLock lock(write_lock_);
+          reject_writes_ = true;
+        }
         OnWriteError(Error::kDisconnected);
-      else
+      } else {
         OnError(Error::kDisconnected);
+      }
     } else if (context == &connect_context_) {
       DCHECK(is_connect_pending_);
       is_connect_pending_ = false;
diff --git a/net/base/filename_util_unittest.cc b/net/base/filename_util_unittest.cc
index 0010b44b..5b40aee 100644
--- a/net/base/filename_util_unittest.cc
+++ b/net/base/filename_util_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_file_util.h"
+#include "build/build_config.h"
 #include "net/base/mime_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -284,79 +285,64 @@
   EXPECT_FALSE(FileURLToFilePath(GURL("filefoobar"), &output));
 }
 
-// Flaky, see http://crbug.com/828954.
-TEST(FilenameUtilTest, DISABLED_GenerateSafeFileName) {
+TEST(FilenameUtilTest, GenerateSafeFileName) {
   const struct {
+    int line;
     const char* mime_type;
-    const base::FilePath::CharType* filename;
-    const base::FilePath::CharType* expected_filename;
+    const char* filename;
+    const char* expected_filename;
   } safe_tests[] = {
+    {__LINE__, "text/html", "bar.htm", "bar.htm"},
+    {__LINE__, "text/html", "bar.html", "bar.html"},
+    {__LINE__, "application/x-chrome-extension", "bar", "bar.crx"},
+    {__LINE__, "image/png", "bar.html", "bar.html"},
+    {__LINE__, "text/html", "bar.exe", "bar.exe"},
+    {__LINE__, "image/gif", "bar.exe", "bar.exe"},
+    {__LINE__, "text/html", "google.com", "google.com"},
+    // Allow extension synonyms.
+    {__LINE__, "image/jpeg", "bar.jpg", "bar.jpg"},
+    {__LINE__, "image/jpeg", "bar.jpeg", "bar.jpeg"},
+
 #if defined(OS_WIN)
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\bar.htm"),
-     FILE_PATH_LITERAL("C:\\foo\\bar.htm")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\bar.html"),
-     FILE_PATH_LITERAL("C:\\foo\\bar.html")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\bar"),
-     FILE_PATH_LITERAL("C:\\foo\\bar.htm")},
-    {"image/png", FILE_PATH_LITERAL("C:\\bar.html"),
-     FILE_PATH_LITERAL("C:\\bar.html")},
-    {"image/png", FILE_PATH_LITERAL("C:\\bar"),
-     FILE_PATH_LITERAL("C:\\bar.png")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\bar.exe"),
-     FILE_PATH_LITERAL("C:\\foo\\bar.exe")},
-    {"image/gif", FILE_PATH_LITERAL("C:\\foo\\bar.exe"),
-     FILE_PATH_LITERAL("C:\\foo\\bar.exe")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\google.com"),
-     FILE_PATH_LITERAL("C:\\foo\\google.com")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\con.htm"),
-     FILE_PATH_LITERAL("C:\\foo\\_con.htm")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\con"),
-     FILE_PATH_LITERAL("C:\\foo\\_con.htm")},
-    {"text/html",
-     FILE_PATH_LITERAL("C:\\foo\\harmless.{not-really-this-may-be-a-guid}"),
-     FILE_PATH_LITERAL("C:\\foo\\harmless.download")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\harmless.local"),
-     FILE_PATH_LITERAL("C:\\foo\\harmless.download")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\harmless.lnk"),
-     FILE_PATH_LITERAL("C:\\foo\\harmless.download")},
-    {"text/html", FILE_PATH_LITERAL("C:\\foo\\harmless.{mismatched-"),
-     FILE_PATH_LITERAL("C:\\foo\\harmless.{mismatched-")},
-    // Allow extension synonyms.
-    {"image/jpeg", FILE_PATH_LITERAL("C:\\foo\\bar.jpg"),
-     FILE_PATH_LITERAL("C:\\foo\\bar.jpg")},
-    {"image/jpeg", FILE_PATH_LITERAL("C:\\foo\\bar.jpeg"),
-     FILE_PATH_LITERAL("C:\\foo\\bar.jpeg")},
-#else   // !defined(OS_WIN)
-    {"text/html", FILE_PATH_LITERAL("/foo/bar.htm"),
-     FILE_PATH_LITERAL("/foo/bar.htm")},
-    {"text/html", FILE_PATH_LITERAL("/foo/bar.html"),
-     FILE_PATH_LITERAL("/foo/bar.html")},
-    {"text/html", FILE_PATH_LITERAL("/foo/bar"),
-     FILE_PATH_LITERAL("/foo/bar.html")},
-    {"image/png", FILE_PATH_LITERAL("/bar.html"),
-     FILE_PATH_LITERAL("/bar.html")},
-    {"image/png", FILE_PATH_LITERAL("/bar"), FILE_PATH_LITERAL("/bar.png")},
-    {"image/gif", FILE_PATH_LITERAL("/foo/bar.exe"),
-     FILE_PATH_LITERAL("/foo/bar.exe")},
-    {"text/html", FILE_PATH_LITERAL("/foo/google.com"),
-     FILE_PATH_LITERAL("/foo/google.com")},
-    {"text/html", FILE_PATH_LITERAL("/foo/con.htm"),
-     FILE_PATH_LITERAL("/foo/con.htm")},
-    {"text/html", FILE_PATH_LITERAL("/foo/con"),
-     FILE_PATH_LITERAL("/foo/con.html")},
-    // Allow extension synonyms.
-    {"image/jpeg", FILE_PATH_LITERAL("/bar.jpg"),
-     FILE_PATH_LITERAL("/bar.jpg")},
-    {"image/jpeg", FILE_PATH_LITERAL("/bar.jpeg"),
-     FILE_PATH_LITERAL("/bar.jpeg")},
+    // Device names
+    {__LINE__, "text/html", "con.htm", "_con.htm"},
+    {__LINE__, "text/html", "lpt1.htm", "_lpt1.htm"},
+    {__LINE__, "application/x-chrome-extension", "con", "_con.crx"},
+
+    // Looks like foo.{GUID} which get treated as namespace mounts on Windows.
+    {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
+     "harmless.download"},
+    {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
+
+    // Dangerous extensions
+    {__LINE__, "text/html", "harmless.local", "harmless.download"},
+    {__LINE__, "text/html", "harmless.lnk", "harmless.download"},
+#else   // OS_WIN
+    // On Posix, none of the above set is particularly dangerous.
+    {__LINE__, "text/html", "con.htm", "con.htm"},
+    {__LINE__, "text/html", "lpt1.htm", "lpt1.htm"},
+    {__LINE__, "application/x-chrome-extension", "con", "con.crx"},
+    {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
+     "harmless.{not-really-this-may-be-a-guid}"},
+    {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
+    {__LINE__, "text/html", "harmless.local", "harmless.local"},
+    {__LINE__, "text/html", "harmless.lnk", "harmless.lnk"},
 #endif  // !defined(OS_WIN)
   };
 
-  for (size_t i = 0; i < arraysize(safe_tests); ++i) {
-    base::FilePath file_path(safe_tests[i].filename);
-    GenerateSafeFileName(safe_tests[i].mime_type, false, &file_path);
-    EXPECT_EQ(safe_tests[i].expected_filename, file_path.value())
-        << "Iteration " << i;
+#if defined(OS_WIN)
+  base::FilePath base_path(L"C:\\foo");
+#else
+  base::FilePath base_path("/foo");
+#endif
+
+  for (const auto& test : safe_tests) {
+    base::FilePath file_path = base_path.AppendASCII(test.filename);
+    base::FilePath expected_path =
+        base_path.AppendASCII(test.expected_filename);
+    GenerateSafeFileName(test.mime_type, false, &file_path);
+    EXPECT_EQ(expected_path.value(), file_path.value())
+        << "Test case at line " << test.line;
   }
 }
 
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
index bbf9773..3fbb29f 100644
--- a/net/disk_cache/simple/simple_backend_impl.cc
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -32,6 +32,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "base/trace_event/process_memory_dump.h"
+#include "build/build_config.h"
 #include "net/base/net_errors.h"
 #include "net/disk_cache/backend_cleanup_tracker.h"
 #include "net/disk_cache/cache_util.h"
@@ -73,11 +74,6 @@
 // Maximum fraction of the cache that one entry can consume.
 const int kMaxFileRatio = 8;
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-// Period to check the maximum count of files available
-constexpr int kUpdateIntervalInSeconds = 10 * 60;  // 10 min
-#endif
-
 bool g_fd_limit_histogram_has_been_populated = false;
 
 void MaybeHistogramFdLimit() {
@@ -644,18 +640,6 @@
                                         const DiskStatResult& result) {
   if (result.net_error == net::OK) {
     index_->SetMaxSize(result.max_size);
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-    int64_t available = base::SysInfo::AmountOfAvailableDiskInode(path_);
-    int64_t total = base::SysInfo::AmountOfMaxDiskInode(path_);
-    if (available != -1 && total != -1) {
-      index_->UpdateMaxFiles(available, total);
-      if (!update_timer_.IsRunning())
-        update_timer_.Start(
-            FROM_HERE, base::TimeDelta::FromSeconds(kUpdateIntervalInSeconds),
-            base::BindRepeating(&SimpleBackendImpl::OnUpdateMaxFiles,
-                                AsWeakPtr()));
-    }
-#endif
     index_->Initialize(result.cache_dir_mtime);
   }
   callback.Run(result.net_error);
@@ -867,13 +851,4 @@
   base::TaskScheduler::GetInstance()->FlushForTesting();
 }
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-void SimpleBackendImpl::OnUpdateMaxFiles() {
-  int64_t available = base::SysInfo::AmountOfAvailableDiskInode(path_);
-  int64_t total = base::SysInfo::AmountOfMaxDiskInode(path_);
-  if (available != -1 && total != -1)
-    index_->UpdateMaxFiles(available, total);
-}
-#endif
-
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h
index 1190461..370969b0 100644
--- a/net/disk_cache/simple/simple_backend_impl.h
+++ b/net/disk_cache/simple/simple_backend_impl.h
@@ -21,7 +21,6 @@
 #include "base/strings/string_split.h"
 #include "base/task_runner.h"
 #include "base/time/time.h"
-#include "build/build_config.h"
 #include "net/base/cache_type.h"
 #include "net/base/net_export.h"
 #include "net/disk_cache/disk_cache.h"
@@ -29,10 +28,6 @@
 #include "net/disk_cache/simple/simple_experiment.h"
 #include "net/disk_cache/simple/simple_index_delegate.h"
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-#include "base/timer/timer.h"
-#endif
-
 namespace base {
 class SequencedTaskRunner;
 class TaskRunner;
@@ -242,11 +237,6 @@
                            const CompletionCallback& callback,
                            int result);
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-  // update limit of max files to be created
-  void OnUpdateMaxFiles();
-#endif
-
   // We want this destroyed after every other field.
   scoped_refptr<BackendCleanupTracker> cleanup_tracker_;
 
@@ -276,10 +266,6 @@
       entries_pending_doom_;
 
   net::NetLog* const net_log_;
-
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-  base::RepeatingTimer update_timer_;
-#endif
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_index.cc b/net/disk_cache/simple/simple_index.cc
index 3d4b702..a891458 100644
--- a/net/disk_cache/simple/simple_index.cc
+++ b/net/disk_cache/simple/simple_index.cc
@@ -155,9 +155,6 @@
       max_size_(0),
       high_watermark_(0),
       low_watermark_(0),
-      max_files_(0),
-      files_high_watermark_(0),
-      files_low_watermark_(0),
       eviction_in_progress_(false),
       initialized_(false),
       init_method_(INITIALIZE_METHOD_MAX),
@@ -350,10 +347,7 @@
 
 void SimpleIndex::StartEvictionIfNeeded() {
   DCHECK(io_thread_checker_.CalledOnValidThread());
-  uint64_t cache_files = entries_set_.size();
-  if (eviction_in_progress_ ||
-      (cache_size_ <= high_watermark_ &&
-       (files_high_watermark_ == 0 || cache_files <= files_high_watermark_)))
+  if (eviction_in_progress_ || cache_size_ <= high_watermark_)
     return;
   // Take all live key hashes from the index and sort them by time.
   eviction_in_progress_ = true;
@@ -384,21 +378,13 @@
   }
 
   uint64_t evicted_so_far_size = 0;
-  const uint64_t amount_to_evict =
-      (cache_size_ > low_watermark_) ? cache_size_ - low_watermark_ : 0;
-  uint64_t evicted_files_count = 0;
-  const uint64_t file_amount_to_evict =
-      (files_low_watermark_ > 0 && cache_files > files_low_watermark_)
-          ? cache_files - files_low_watermark_
-          : 0;
+  const uint64_t amount_to_evict = cache_size_ - low_watermark_;
   std::vector<uint64_t> entry_hashes;
   std::sort(entries.begin(), entries.end());
   for (const auto& score_metadata_pair : entries) {
-    if (evicted_so_far_size >= amount_to_evict &&
-        evicted_files_count >= file_amount_to_evict)
+    if (evicted_so_far_size >= amount_to_evict)
       break;
     evicted_so_far_size += score_metadata_pair.second->second.GetEntrySize();
-    evicted_files_count++;
     entry_hashes.push_back(score_metadata_pair.second->first);
   }
 
@@ -543,10 +529,6 @@
     io_thread_->PostTask(FROM_HERE, base::Bind((*it), net::OK));
   }
   to_run_when_initialized_.clear();
-  if (!update_max_files_cb_.is_null()) {
-    std::move(update_max_files_cb_).Run();
-    CHECK(update_max_files_cb_.is_null());
-  }
 }
 
 #if defined(OS_ANDROID)
@@ -599,23 +581,4 @@
                            app_on_background_, after_write);
 }
 
-void SimpleIndex::UpdateMaxFiles(uint64_t available, uint64_t total) {
-  DCHECK(io_thread_checker_.CalledOnValidThread());
-  if (!initialized_) {
-    update_max_files_cb_ = base::BindOnce(&SimpleIndex::UpdateMaxFiles,
-                                          AsWeakPtr(), available, total);
-    return;
-  }
-
-  auto cache_files = entries_set_.size();
-  uint64_t max_files = static_cast<uint64_t>((cache_files + available) * 0.3);
-  if (max_files_ == max_files)
-    return;
-
-  max_files_ = max_files;
-  files_high_watermark_ = max_files_ - max_files_ / kEvictionMarginDivisor;
-  files_low_watermark_ = max_files_ - 2 * (max_files_ / kEvictionMarginDivisor);
-  StartEvictionIfNeeded();
-}
-
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_index.h b/net/disk_cache/simple/simple_index.h
index 68c1024..0da8cd9 100644
--- a/net/disk_cache/simple/simple_index.h
+++ b/net/disk_cache/simple/simple_index.h
@@ -194,8 +194,6 @@
 
   void SetLastUsedTimeForTest(uint64_t entry_hash, const base::Time last_used);
 
-  void UpdateMaxFiles(uint64_t available, uint64_t total);
-
  private:
   friend class SimpleIndexTest;
   FRIEND_TEST_ALL_PREFIXES(SimpleIndexTest, IndexSizeCorrectOnMerge);
@@ -233,10 +231,6 @@
   uint64_t max_size_;
   uint64_t high_watermark_;
   uint64_t low_watermark_;
-  uint64_t max_files_;  // Maximum number of files allowed (0 if unlimited).
-  // High and low thresholds for allowed number of files (0 if unlimited).
-  uint64_t files_high_watermark_;
-  uint64_t files_low_watermark_;
   bool eviction_in_progress_;
   base::TimeTicks eviction_start_time_;
 
@@ -261,7 +255,6 @@
 
   base::OneShotTimer write_to_disk_timer_;
   base::Closure write_to_disk_cb_;
-  base::OnceClosure update_max_files_cb_;
 
   typedef std::list<net::CompletionCallback> CallbackList;
   CallbackList to_run_when_initialized_;
diff --git a/net/disk_cache/simple/simple_index_unittest.cc b/net/disk_cache/simple/simple_index_unittest.cc
index 7463c09..03a6bedc 100644
--- a/net/disk_cache/simple/simple_index_unittest.cc
+++ b/net/disk_cache/simple/simple_index_unittest.cc
@@ -176,7 +176,7 @@
   }
   int doom_entries_calls() const { return doom_entries_calls_; }
 
-  const simple_util::ImmutableArray<uint64_t, 20> hashes_;
+  const simple_util::ImmutableArray<uint64_t, 16> hashes_;
   std::unique_ptr<SimpleIndex> index_;
   base::WeakPtr<MockSimpleIndexFile> index_file_;
 
@@ -697,66 +697,6 @@
   ASSERT_EQ(2u, last_doom_entry_hashes().size());
 }
 
-TEST_F(SimpleIndexTest, EvictByFileCount) {
-  base::Time now(base::Time::Now());
-  index()->SetMaxSize(50000);
-  InsertIntoIndexFileReturn(hashes_.at<0>(),
-                            now - base::TimeDelta::FromDays(21), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<1>(),
-                            now - base::TimeDelta::FromDays(20), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<2>(),
-                            now - base::TimeDelta::FromDays(19), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<3>(),
-                            now - base::TimeDelta::FromDays(18), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<4>(),
-                            now - base::TimeDelta::FromDays(17), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<5>(),
-                            now - base::TimeDelta::FromDays(16), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<6>(),
-                            now - base::TimeDelta::FromDays(15), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<7>(),
-                            now - base::TimeDelta::FromDays(14), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<8>(),
-                            now - base::TimeDelta::FromDays(13), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<9>(),
-                            now - base::TimeDelta::FromDays(12), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<10>(),
-                            now - base::TimeDelta::FromDays(11), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<11>(),
-                            now - base::TimeDelta::FromDays(10), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<12>(),
-                            now - base::TimeDelta::FromDays(9), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<13>(),
-                            now - base::TimeDelta::FromDays(8), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<14>(),
-                            now - base::TimeDelta::FromDays(7), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<15>(),
-                            now - base::TimeDelta::FromDays(6), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<16>(),
-                            now - base::TimeDelta::FromDays(5), 10u);
-  InsertIntoIndexFileReturn(hashes_.at<17>(),
-                            now - base::TimeDelta::FromDays(4), 10u);
-  index()->UpdateMaxFiles(50, 200);
-  // This should set the max files limit to 20, so the high watermark is 19 and
-  // the lower watermark is 18.
-  ReturnIndexFile();
-  WaitForTimeChange();
-
-  // No eviction should have happened yet.
-  EXPECT_EQ(18, index()->GetEntryCount());
-
-  index()->Insert(hashes_.at<18>());
-  index()->UpdateEntrySize(hashes_.at<18>(), 10u);
-  index()->Insert(hashes_.at<19>());
-  index()->UpdateEntrySize(hashes_.at<19>(), 10u);
-
-  // Eviction has happened, we get back to 18 elements
-  EXPECT_EQ(18, index()->GetEntryCount());
-  EXPECT_EQ(1, doom_entries_calls());
-  EXPECT_TRUE(index()->Has(hashes_.at<2>()));
-  EXPECT_FALSE(index()->Has(hashes_.at<1>()));
-}
-
 // Confirm all the operations queue a disk write at some point in the
 // future.
 TEST_F(SimpleIndexTest, DiskWriteQueued) {
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 106f59e..8656d3f 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -191,6 +191,8 @@
     ssl_info.connection_status = connection_status;
   }
 
+  // Signed Certificate Timestamps are no longer persisted to the cache, so
+  // ignore them when reading them out.
   if (flags & RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS) {
     int num_scts;
     if (!iter.ReadInt(&num_scts))
@@ -201,11 +203,6 @@
       uint16_t status;
       if (!sct.get() || !iter.ReadUInt16(&status))
         return false;
-      if (!net::ct::IsValidSCTStatus(status))
-        return false;
-      ssl_info.signed_certificate_timestamps.push_back(
-          SignedCertificateTimestampAndStatus(
-              sct, static_cast<ct::SCTVerifyStatus>(status)));
     }
   }
 
@@ -305,8 +302,6 @@
     flags |= RESPONSE_INFO_USE_HTTP_AUTHENTICATION;
   if (unused_since_prefetch)
     flags |= RESPONSE_INFO_UNUSED_SINCE_PREFETCH;
-  if (!ssl_info.signed_certificate_timestamps.empty())
-    flags |= RESPONSE_INFO_HAS_SIGNED_CERTIFICATE_TIMESTAMPS;
   if (ssl_info.pkp_bypassed)
     flags |= RESPONSE_INFO_PKP_BYPASSED;
 
@@ -335,15 +330,6 @@
       pickle->WriteInt(ssl_info.security_bits);
     if (ssl_info.connection_status != 0)
       pickle->WriteInt(ssl_info.connection_status);
-    if (!ssl_info.signed_certificate_timestamps.empty()) {
-      pickle->WriteInt(ssl_info.signed_certificate_timestamps.size());
-      for (SignedCertificateTimestampAndStatusList::const_iterator it =
-           ssl_info.signed_certificate_timestamps.begin(); it !=
-           ssl_info.signed_certificate_timestamps.end(); ++it) {
-        it->sct->Persist(pickle);
-        pickle->WriteUInt16(static_cast<uint16_t>(it->status));
-      }
-    }
   }
 
   if (vary_data.is_valid())
diff --git a/net/http/http_response_info_unittest.cc b/net/http/http_response_info_unittest.cc
index b8aadbb..d880cff 100644
--- a/net/http/http_response_info_unittest.cc
+++ b/net/http/http_response_info_unittest.cc
@@ -72,35 +72,6 @@
   EXPECT_FALSE(restored_response_info.ssl_info.pkp_bypassed);
 }
 
-TEST_F(HttpResponseInfoTest, FailsInitFromPickleWithInvalidSCTStatus) {
-  // A valid certificate is needed for ssl_info.is_valid() to be true
-  // so that the SCTs would be serialized.
-  response_info_.ssl_info.cert =
-      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
-
-  scoped_refptr<ct::SignedCertificateTimestamp> sct;
-  ct::GetX509CertSCT(&sct);
-
-  response_info_.ssl_info.signed_certificate_timestamps.push_back(
-      SignedCertificateTimestampAndStatus(
-          sct, ct::SCTVerifyStatus::SCT_STATUS_LOG_UNKNOWN));
-
-  base::Pickle pickle;
-  response_info_.Persist(&pickle, false, false);
-  bool truncated = false;
-  net::HttpResponseInfo restored_response_info;
-  EXPECT_TRUE(restored_response_info.InitFromPickle(pickle, &truncated));
-
-  response_info_.ssl_info.signed_certificate_timestamps.push_back(
-      SignedCertificateTimestampAndStatus(sct,
-                                          static_cast<ct::SCTVerifyStatus>(2)));
-  base::Pickle pickle_invalid;
-  response_info_.Persist(&pickle_invalid, false, false);
-  net::HttpResponseInfo restored_invalid_response;
-  EXPECT_FALSE(
-      restored_invalid_response.InitFromPickle(pickle_invalid, &truncated));
-}
-
 // Test that key_exchange_group is preserved for ECDHE ciphers.
 TEST_F(HttpResponseInfoTest, KeyExchangeGroupECDHE) {
   response_info_.ssl_info.cert =
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index d2f010a..cfd6728 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -1056,11 +1056,18 @@
             NetLogEventType::HTTP_STREAM_REQUEST_PROTO,
             base::Bind(&NetLogHttpStreamProtoCallback, negotiated_protocol_));
         if (negotiated_protocol_ == kProtoHTTP2) {
-          // If request is WebSocket with no proxy, then HTTP/2 must not have
-          // been advertised in the TLS handshake.  The TLS layer must not have
-          // accepted the server choosing HTTP/2.
-          // TODO(bnc): Change to DCHECK once https://crbug.com/819101 is fixed.
-          CHECK(!(is_websocket_ && proxy_info_.is_direct()));
+          if (is_websocket_) {
+            // If request is WebSocket with no proxy, then HTTP/2 must not have
+            // been advertised in the TLS handshake.  The TLS layer must not
+            // have accepted the server choosing HTTP/2.
+            // TODO(bnc): Change to DCHECK once https://crbug.com/819101 is
+            // fixed.
+            CHECK(!proxy_info_.is_direct());
+
+            // WebSocket is not supported over a fresh HTTP/2 connection.
+            return ERR_NOT_IMPLEMENTED;
+          }
+
           using_spdy_ = true;
         }
       }
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 6420f6b..596c35c 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -3096,50 +3096,6 @@
 //  }
 EVENT_TYPE(DATA_REDUCTION_PROXY_CONFIG_REQUEST)
 
-// -----------------------------------------------------------------------------
-// Safe Browsing related events
-// -----------------------------------------------------------------------------
-
-// The start/end of an async URL check by Safe Browsing. Will only show up if
-// it can't be classified as "safe" synchronously.
-//
-// The BEGIN phase contains the following parameters:
-//  {
-//    "url": <The URL being checked>,
-//  }
-//
-// The END phase contains the following parameters:
-//  {
-//    "result": <"safe", "unsafe", or "request_canceled">
-//  }
-EVENT_TYPE(SAFE_BROWSING_CHECKING_URL)
-
-// The start/end of some portion of the SAFE_BROWSING_CHECKING_URL during which
-// the request is delayed due to that check.
-//
-// The BEGIN phase contains the following parameters:
-//  {
-//    "url": <The URL being checked>,
-//    "defer_reason" : < "at_start", "at_response", "redirect",
-//                       "resumed_redirect", "unchecked_redirect">
-//  }
-EVENT_TYPE(SAFE_BROWSING_DEFERRED)
-
-// The start/end of a Safe Browsing ping being sent.
-//
-// The BEGIN phase contains the following parameters:
-//  {
-//    "url": <The URL the ping is going to, which identifies the type of ping
-//            that is being sent (eg: ThreatReport, SafeBrowsingHit)>
-//    "data": <The base64 encoding of the payload sent with the ping>
-//
-// The END phase contains the following parameters:
-//  {
-//    "status": <The integer status of the report transmission. Corresponds to
-//               URLRequestStatus::Status>
-//    "error": <The error code returned by the server, 0 indicating success>
-EVENT_TYPE(SAFE_BROWSING_PING)
-
 // Marks start of UploadDataStream that is logged on initialization.
 // The END phase contains the following parameters:
 // {
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index bdd5df5..7d844f7 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -510,11 +510,7 @@
       // run through the MessageLoop once to get it completely released.
       handle_->socket()->Disconnect();
       handle_->Reset();
-      {
-        base::MessageLoop::ScopedNestableTaskAllower allow(
-            base::MessageLoop::current());
-        base::RunLoop().RunUntilIdle();
-      }
+      base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
       within_callback_ = true;
       scoped_refptr<TransportSocketParams> dest(new TransportSocketParams(
           HostPortPair("www.google.com", 80), false, OnHostResolutionCallback(),
diff --git a/net/spdy/chromium/spdy_network_transaction_unittest.cc b/net/spdy/chromium/spdy_network_transaction_unittest.cc
index c4d3816..080b765 100644
--- a/net/spdy/chromium/spdy_network_transaction_unittest.cc
+++ b/net/spdy/chromium/spdy_network_transaction_unittest.cc
@@ -7350,6 +7350,8 @@
   SSLSocketDataProvider ssl_provider(ASYNC, OK);
   ssl_provider.ssl_info.cert =
       ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem");
+  // A WebSocket request should not advertise HTTP/2 support.
+  ssl_provider.next_protos_expected_in_ssl_config = NextProtoVector{};
   // This test uses WebSocket over HTTP/1.1.
   ssl_provider.next_proto = kProtoHTTP11;
   helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
@@ -7378,6 +7380,56 @@
   helper.VerifyDataConsumed();
 }
 
+// Regression test for https://crbug.com/828865.
+TEST_F(SpdyNetworkTransactionTest,
+       SecureWebSocketOverHttp2ProxyNegotiatesHttp2) {
+  SpdySerializedFrame connect_request(spdy_util_.ConstructSpdyConnect(
+      nullptr, 0, 1, LOWEST, HostPortPair("www.example.org", 443)));
+  MockWrite writes[] = {CreateMockWrite(connect_request, 0)};
+  SpdySerializedFrame connect_response(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  MockRead reads[] = {CreateMockRead(connect_response, 1),
+                      MockRead(ASYNC, 0, 2)};
+  SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
+
+  request_.url = GURL("wss://www.example.org/");
+  request_.extra_headers.SetHeader("Connection", "Upgrade");
+  request_.extra_headers.SetHeader("Upgrade", "websocket");
+  request_.extra_headers.SetHeader("Origin", "http://www.example.org");
+  request_.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
+  auto session_deps = std::make_unique<SpdySessionDependencies>(
+      ProxyResolutionService::CreateFixed("https://proxy:70",
+                                          TRAFFIC_ANNOTATION_FOR_TESTS));
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  helper.RunPreTestSetup();
+  helper.AddData(&data);
+
+  // Add SSL data for the tunneled connection.
+  SSLSocketDataProvider ssl_provider(ASYNC, OK);
+  ssl_provider.ssl_info.cert =
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem");
+  // A WebSocket request should not advertise HTTP/2 support.
+  ssl_provider.next_protos_expected_in_ssl_config = NextProtoVector{};
+  // The server should not negotiate HTTP/2 over the tunnelled connection,
+  // but it must be handled gracefully if it does.
+  ssl_provider.next_proto = kProtoHTTP2;
+  helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
+      &ssl_provider);
+
+  HttpNetworkTransaction* trans = helper.trans();
+  TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
+  trans->SetWebSocketHandshakeStreamCreateHelper(
+      &websocket_stream_create_helper);
+
+  EXPECT_TRUE(helper.StartDefaultTest());
+  helper.WaitForCallbackToComplete();
+  EXPECT_THAT(helper.output().rv, IsError(ERR_NOT_IMPLEMENTED));
+
+  base::RunLoop().RunUntilIdle();
+  helper.VerifyDataConsumed();
+}
+
 #endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
 
 }  // namespace net
diff --git a/net/tools/quic/quic_server_bin.cc b/net/tools/quic/quic_server_bin.cc
index 2bb71ba..c3d2291b 100644
--- a/net/tools/quic/quic_server_bin.cc
+++ b/net/tools/quic/quic_server_bin.cc
@@ -33,13 +33,13 @@
 }
 
 int main(int argc, char* argv[]) {
-  base::TaskScheduler::CreateAndStartWithDefaultParams("quic_server");
   base::AtExitManager exit_manager;
-  base::MessageLoopForIO message_loop;
-
   base::CommandLine::Init(argc, argv);
   base::CommandLine* line = base::CommandLine::ForCurrentProcess();
 
+  base::MessageLoopForIO message_loop;
+  base::TaskScheduler::CreateAndStartWithDefaultParams("quic_server");
+
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
   CHECK(logging::InitLogging(settings));
diff --git a/net/tools/quic/quic_simple_client_bin.cc b/net/tools/quic/quic_simple_client_bin.cc
index af5e63e2..f14f2aa 100644
--- a/net/tools/quic/quic_simple_client_bin.cc
+++ b/net/tools/quic/quic_simple_client_bin.cc
@@ -136,10 +136,10 @@
 };
 
 int main(int argc, char* argv[]) {
-  base::TaskScheduler::CreateAndStartWithDefaultParams("quic_client");
   base::CommandLine::Init(argc, argv);
   base::CommandLine* line = base::CommandLine::ForCurrentProcess();
   const base::CommandLine::StringVector& urls = line->GetArgs();
+  base::TaskScheduler::CreateAndStartWithDefaultParams("quic_client");
 
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn
index 1398402..71846a6 100644
--- a/remoting/protocol/BUILD.gn
+++ b/remoting/protocol/BUILD.gn
@@ -109,6 +109,8 @@
     "mouse_input_filter.h",
     "named_message_pipe_handler.cc",
     "named_message_pipe_handler.h",
+    "native_ip_synthesizer.cc",
+    "native_ip_synthesizer.h",
     "negotiating_authenticator_base.cc",
     "negotiating_authenticator_base.h",
     "negotiating_client_authenticator.cc",
diff --git a/remoting/protocol/ice_transport.cc b/remoting/protocol/ice_transport.cc
index 637498d..ff782f0d 100644
--- a/remoting/protocol/ice_transport.cc
+++ b/remoting/protocol/ice_transport.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "remoting/protocol/channel_authenticator.h"
 #include "remoting/protocol/channel_multiplexer.h"
+#include "remoting/protocol/native_ip_synthesizer.h"
 #include "remoting/protocol/pseudotcp_channel_factory.h"
 #include "remoting/protocol/secure_channel_factory.h"
 #include "remoting/protocol/stream_channel_factory.h"
@@ -72,6 +73,8 @@
   for (auto it = transport_info.candidates.begin();
        it != transport_info.candidates.end(); ++it) {
     ChannelsMap::iterator channel = channels_.find(it->name);
+    rtc::SocketAddress address = ToNativeSocket(it->candidate.address());
+    it->candidate.set_address(address);
     if (channel != channels_.end()) {
       channel->second->AddRemoteCandidate(it->candidate);
     } else {
diff --git a/remoting/protocol/native_ip_synthesizer.cc b/remoting/protocol/native_ip_synthesizer.cc
new file mode 100644
index 0000000..8b2a782
--- /dev/null
+++ b/remoting/protocol/native_ip_synthesizer.cc
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/protocol/native_ip_synthesizer.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "net/base/sys_addrinfo.h"
+#include "third_party/webrtc/rtc_base/ipaddress.h"
+#include "third_party/webrtc/rtc_base/socketaddress.h"
+
+namespace remoting {
+namespace protocol {
+
+// static
+rtc::SocketAddress ToNativeSocket(const rtc::SocketAddress& original_socket) {
+#if defined(OS_IOS)
+  // Currently only iOS needs the extra translation step. Android emulates an
+  // IPv4 network stack.
+
+  addrinfo hints;
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = PF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_DEFAULT;
+
+  addrinfo* result = nullptr;
+  // getaddrinfo() will resolve an IPv4 address into its curresponding IPv4/IPv6
+  // address connectable on current network environment. Note that this doesn't
+  // really send out a DNS request on iOS.
+  int error = getaddrinfo(original_socket.ipaddr().ToString().c_str(), nullptr,
+                          &hints, &result);
+  if (error) {
+    LOG(ERROR) << "getaddrinfo() failed for " << gai_strerror(error);
+    return original_socket;
+  }
+
+  if (!result) {
+    return original_socket;
+  }
+
+  rtc::SocketAddress new_socket;
+  bool success = rtc::SocketAddressFromSockAddrStorage(
+      *reinterpret_cast<sockaddr_storage*>(result->ai_addr), &new_socket);
+  DCHECK(success);
+  freeaddrinfo(result);
+  new_socket.SetPort(original_socket.port());
+  return new_socket;
+#else
+  return original_socket;
+#endif  // defined(OS_IOS)
+}
+
+}  // namespace protocol
+}  // namespace remoting
diff --git a/remoting/protocol/native_ip_synthesizer.h b/remoting/protocol/native_ip_synthesizer.h
new file mode 100644
index 0000000..6855b016
--- /dev/null
+++ b/remoting/protocol/native_ip_synthesizer.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_PROTOCOL_NATIVE_IP_SYNTHESIZER_H_
+#define REMOTING_PROTOCOL_NATIVE_IP_SYNTHESIZER_H_
+
+namespace rtc {
+class SocketAddress;
+}  // namespace rtc
+
+namespace remoting {
+namespace protocol {
+
+// Helper functions for synthesizing native IP address that is acceptabled by
+// the OS from an IP literal.
+//
+// We hardcode IPv4 literals in stanza and other places. Some mobile ISP have an
+// IPv6-only network with an IPv6->IPv4 gateway, so connecting with IPv4 literal
+// may not work. Android and other OSes have a 464XLAT CLAT converter built into
+// their network stack so IPv4 APIs are available to the app. However, iOS
+// doesn't have this logic built into its network stack and instead requires
+// developer to resolve an IPv6 address from IPv4 literal. This class helps
+// working with this.
+
+// Translate socket address into the one acceptable by the OS.
+// If native IP synthesis is not needed by the OS, |original_socket| will be
+// returned.
+rtc::SocketAddress ToNativeSocket(const rtc::SocketAddress& original_socket);
+
+}  // namespace protocol
+}  // namespace remoting
+
+#endif  // REMOTING_PROTOCOL_NATIVE_IP_SYNTHESIZER_H_
diff --git a/remoting/protocol/port_allocator.cc b/remoting/protocol/port_allocator.cc
index 82efea2..ae438e1 100644
--- a/remoting/protocol/port_allocator.cc
+++ b/remoting/protocol/port_allocator.cc
@@ -15,6 +15,7 @@
 #include "net/base/escape.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "remoting/protocol/native_ip_synthesizer.h"
 #include "remoting/protocol/network_settings.h"
 #include "remoting/protocol/transport_context.h"
 
@@ -232,8 +233,9 @@
       base::StringToUint(relay_port, &relay_port_int)) {
     cricket::RelayServerConfig relay_config(cricket::RELAY_GTURN);
     rtc::SocketAddress address(relay_ip, relay_port_int);
+    // |relay_ip| is in IPv4 so we will need to do an IPv6 synthesis.
     relay_config.ports.push_back(
-        cricket::ProtocolAddress(address, cricket::PROTO_UDP));
+        cricket::ProtocolAddress(ToNativeSocket(address), cricket::PROTO_UDP));
     config->AddRelay(relay_config);
   }
 
diff --git a/services/network/test/test_url_loader_factory.cc b/services/network/test/test_url_loader_factory.cc
index d2582233..0df8159c 100644
--- a/services/network/test/test_url_loader_factory.cc
+++ b/services/network/test/test_url_loader_factory.cc
@@ -96,13 +96,16 @@
     return false;
 
   CHECK(it->second.redirects.empty()) << "TODO(jam): handle redirects";
-  client->OnReceiveResponse(it->second.head, nullptr);
-  mojo::DataPipe data_pipe(it->second.content.size());
-  uint32_t bytes_written = it->second.content.size();
-  CHECK_EQ(MOJO_RESULT_OK, data_pipe.producer_handle->WriteData(
-                               it->second.content.data(), &bytes_written,
-                               MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
-  client->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));
+
+  if (it->second.status.error_code == net::OK) {
+    client->OnReceiveResponse(it->second.head, nullptr);
+    mojo::DataPipe data_pipe(it->second.content.size());
+    uint32_t bytes_written = it->second.content.size();
+    CHECK_EQ(MOJO_RESULT_OK, data_pipe.producer_handle->WriteData(
+                                 it->second.content.data(), &bytes_written,
+                                 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+    client->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));
+  }
   client->OnComplete(it->second.status);
   return true;
 }
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
index 1477b173..70b4201 100644
--- a/testing/BUILD.gn
+++ b/testing/BUILD.gn
@@ -30,10 +30,13 @@
   ]
 }
 
-group("run_gtest_perf_test") {
+group("run_perf_test") {
   data = [
     "//testing/scripts/common.py",
     "//testing/scripts/run_gtest_perf_test.py",
+    "//testing/scripts/run_performance_tests.py",
+    "//testing/scripts/run_performance_tests_wrapper.py",
+    "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
     "//tools/perf/generate_legacy_perf_dashboard_json.py",
   ]
 
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index bc989093..390ae542 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -4,7 +4,8 @@
       {
         "args": [
           "-v",
-          "--browser=android-chromium"
+          "--browser=android-chromium",
+          "--upload-results"
         ],
         "isolate_name": "performance_test_suite",
         "merge": {
@@ -450,11 +451,10 @@
     "isolated_scripts": [
       {
         "args": [
-          "-v",
-          "--browser=release",
-          "--non-telemetry=true"
+          "--non-telemetry=true",
+          "--migrated-test=true"
         ],
-        "isolate_name": "load_library_perf_tests_v2",
+        "isolate_name": "load_library_perf_tests",
         "merge": {
           "args": [
             "--service-account-file",
@@ -462,9 +462,9 @@
           ],
           "script": "//tools/perf/process_perf_results.py"
         },
-        "name": "load_library_perf_tests_v2",
+        "name": "load_library_perf_tests",
         "override_compile_targets": [
-          "load_library_perf_tests_v2"
+          "load_library_perf_tests"
         ],
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -495,7 +495,8 @@
       {
         "args": [
           "-v",
-          "--browser=release"
+          "--browser=release",
+          "--upload-results"
         ],
         "isolate_name": "performance_test_suite",
         "merge": {
@@ -573,10 +574,53 @@
     "isolated_scripts": [
       {
         "args": [
+          "--non-telemetry=true",
+          "--migrated-test=true"
+        ],
+        "isolate_name": "load_library_perf_tests",
+        "merge": {
+          "args": [
+            "--service-account-file",
+            "/creds/service_accounts/service-account-chromium-perf-histograms.json"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "load_library_perf_tests",
+        "override_compile_targets": [
+          "load_library_perf_tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Linux",
+              "pool": "Chrome-perf-fyi"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 36000,
+          "ignore_task_failure": false,
+          "io_timeout": 1800,
+          "shards": 1,
+          "upload_test_results": true
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"id\": \"swarm823-c4\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/perf_device_trigger.py"
+        }
+      },
+      {
+        "args": [
           "-v",
           "--browser=reference",
-          "--xvfb",
-          "--testing=true"
+          "--upload-results",
+          "--testing=true",
+          "--xvfb"
         ],
         "isolate_name": "telemetry_perf_tests_experimental",
         "merge": {
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 603e3f49..9921e60 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -7,6 +7,7 @@
 -ChromeSecurityExploitBrowserTest.CreateFilesystemURLInExtensionOrigin
 -DisabledSignInIsolationBrowserTest.SyntheticTrial
 -DomainReliabilityBrowserTest.Upload
+-DownloadTest.DownloadDangerousBlobData
 -EnabledSignInIsolationBrowserTest.SyntheticTrial
 -ExpectCTBrowserTest.TestDynamicExpectCTHeaderProcessing
 -ExpectCTBrowserTest.TestDynamicExpectCTReporting
@@ -22,8 +23,6 @@
 -PKPModelClientTest.PKPEnforced
 -PlatformAppBrowserTest.AppWindowAdjustBoundsToBeVisibleOnScreen
 -PlatformAppBrowserTest.CreateAndCloseAppWindow
--PlatformAppDevToolsBrowserTest.ReOpenedWithID
--PlatformAppDevToolsBrowserTest.ReOpenedWithURL
 -PolicyTest.DefaultCookiesSetting
 -PrefetchBrowserTestPredictionDisabled.ExperimentDisabled
 -PreviewsOptimizationGuideBrowserTest.NoScriptPreviewsEnabledByWhitelist
@@ -41,6 +40,8 @@
 -WebViewTests/WebViewTest.ClearDataCache/1
 -WebViewTests/WebViewTest.WebViewInBackgroundPage/0
 -WebViewTests/WebViewTest.WebViewInBackgroundPage/1
+-WebViewTests/WebViewTest.DownloadPermission/0
+-WebViewTests/WebViewTest.DownloadPermission/1
 
 # Redirects to chrome-extension:// should be blocked unless the resource is
 # webaccessible.  https://crbug.com/821586
@@ -175,6 +176,7 @@
 # http://crbug.com/721414
 # TODO(rockot): add support for webRequest API.
 -ExtensionWebRequestApiTest.WebRequestBlocking
+-ExtensionWebRequestApiTest.WebRequestClientsGoogleComProtection
 -ExtensionWebRequestApiTest.WebRequestDeclarative2
 -ExtensionWebRequestApiTest.WebRequestDiceHeaderProtection
 -ExtensionWebRequestApiTest.WebRequestTypes
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 083ceaeb..59517a09 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1146,16 +1146,7 @@
   "load_library_perf_tests": {
     "label": "//chrome/test:load_library_perf_tests",
     "type": "script",
-    "script": "//testing/scripts/run_gtest_perf_test.py",
-    "args": [
-      "load_library_perf_tests",
-      "--test-launcher-print-test-stdio=always"
-    ],
-  },
-  "load_library_perf_tests_v2": {
-    "label": "//chrome/test:load_library_perf_tests_v2",
-    "type": "script",
-    "script": "//testing/scripts/run_performance_tests.py",
+    "script": "//testing/scripts/run_performance_tests_wrapper.py",
     "args": [
       "load_library_perf_tests",
       "--test-launcher-print-test-stdio=always"
@@ -1195,7 +1186,7 @@
   "angle_perftests": {
     "label": "//chrome/test:angle_perftests",
     "type": "script",
-    "script": "//testing/scripts/run_gtest_perf_test.py",
+    "script": "//testing/scripts/run_performance_tests_wrapper.py",
     "args": [
       "angle_perftests",
       "--test-launcher-print-test-stdio=always",
@@ -1216,7 +1207,7 @@
   "performance_browser_tests": {
     "label": "//chrome/test:performance_browser_tests",
     "type": "script",
-    "script": "//testing/scripts/run_gtest_perf_test.py",
+    "script": "//testing/scripts/run_performance_tests_wrapper.py",
     "args": [
       "performance_browser_tests",
       "--test-launcher-print-test-stdio=always",
diff --git a/testing/scripts/run_performance_tests_wrapper.py b/testing/scripts/run_performance_tests_wrapper.py
new file mode 100755
index 0000000..4924fb6
--- /dev/null
+++ b/testing/scripts/run_performance_tests_wrapper.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This script is a wrapper script used during migration of performance
+tests to use the new recipe.  See crbug.com/757933
+
+Non-telemetry tests now will all run with this script.  The flag
+--migrated-test will indicate if this test is using the new recipe or not.
+By default this script runs the legacy testing/scripts/run_gtest_perf_test.py.
+
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(
+  __file__))))
+
+GTEST = os.path.join(SRC_DIR, 'testing', 'scripts', 'run_gtest_perf_test.py')
+PERF = os.path.join(SRC_DIR, 'testing', 'scripts', 'run_performance_tests.py')
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--migrated-test', type=bool, default=False)
+
+  args, rest_args = parser.parse_known_args()
+
+  if args.migrated_test:
+    return subprocess.call([sys.executable, PERF] + rest_args)
+  else:
+    return subprocess.call([sys.executable, GTEST] + rest_args)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1fdeba4f..8038d6d3 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1683,6 +1683,22 @@
             ]
         }
     ],
+    "IncompatibleApplicationsWarning": [
+        {
+            "platforms": [
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "IncompatibleApplicationsWarning",
+                        "ModuleDatabase"
+                    ]
+                }
+            ]
+        }
+    ],
     "InstanceID": [
         {
             "platforms": [
@@ -3679,23 +3695,6 @@
             ]
         }
     ],
-    "TabSyncByRecency": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "win"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled"
-                }
-            ]
-        }
-    ],
     "ThrottleDelayable": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index f8a11d8..0c8d28d 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -10,6 +10,7 @@
 Bug(none) external/wpt/cookies/secure/set-from-wss.https.sub.html [ Failure ]
 Bug(none) external/wpt/css/css-fonts/font-display/font-display.html [ Failure Timeout ]
 Bug(none) external/wpt/html/browsers/offline/appcache/workers/appcache-worker.html [ Timeout ]
+crbug.com/829417 external/wpt/html/browsers/offline/appcache/workers/appcache-worker.https.html [ Timeout ]
 Bug(none) external/wpt/service-workers/service-worker/claim-shared-worker-fetch.https.html [ Failure ]
 Bug(none) external/wpt/service-workers/service-worker/clients-get-client-types.https.html [ Failure ]
 crbug.com/807271 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video-cache.https.html [ Failure ]
@@ -89,3 +90,9 @@
 crbug.com/825687 http/tests/devtools/websocket/websocket-handshake.js [ Failure ]
 
 crbug.com/816556 external/wpt/html/semantics/text-level-semantics/the-a-element/a-download-click-404.html [ Failure ]
+crbug.com/816556 external/wpt/html/semantics/embedded-content/the-area-element/area-download-click.html [ Crash ]
+crbug.com/816556 external/wpt/html/semantics/text-level-semantics/the-a-element/a-download-click.html [ Crash ]
+crbug.com/816556 fast/dom/HTMLAnchorElement/anchor-download.html [ Crash ]
+crbug.com/816556 fast/dom/HTMLAnchorElement/anchor-nodownload-set.html [ Crash ]
+crbug.com/816556 fast/dom/HTMLAreaElement/area-download.html [ Crash ]
+crbug.com/816556 http/tests/security/anchor-download-allow-blob.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 85513a0..96557c5 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2286,6 +2286,11 @@
 crbug.com/805463 external/wpt/acid/acid3/numbered-tests.html [ Skip ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html [ Failure ]
 crbug.com/626703 [ Win7 ] external/wpt/css/css-fonts/variations/font-opentype-collections.html [ Timeout ]
 crbug.com/626703 external/wpt/html/editing/focus/focus-01.html [ Timeout ]
 crbug.com/626703 external/wpt/html/editing/dnd/the-datatransfer-interface/DataTransfer-types-manual.html [ Skip ]
@@ -2906,7 +2911,7 @@
 
 # Crashes with DCHECK enabled, but not on normal Release builds.
 crbug.com/809935 external/wpt/css/css-fonts/variations/font-style-interpolation.html [ Timeout ]
-crbug.com/828748 external/wpt/css/css-fonts/variations/font-weight-lighter-bolder.html [ Pass Crash Failure ]
+crbug.com/809956 external/wpt/css/css-fonts/variations/font-weight-lighter-bolder.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-ui/text-overflow-011.html [ Failure Crash Pass ]
 crbug.com/626703 external/wpt/workers/opaque-origin.html [ Failure Crash Timeout ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html [ Pass Crash ]
@@ -3348,6 +3353,11 @@
 crbug.com/736177 [ Mac10.12 Retina ] tables/mozilla_expected_failures/core/captions2.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 Retina ] virtual/prefer_compositing_to_lcd_text/compositing/overflow/theme-affects-visual-overflow.html [ Failure Pass ]
 crbug.com/736177 [ Mac10.12 Retina ] virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/gpu/fast/canvas/canvas-composite-shadow.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/gpu/fast/canvas/canvas-composite-video.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/gpu/fast/canvas/fillrect_gradient.html [ Failure Pass ]
+crbug.com/736177 [ Mac ] virtual/gpu/fast/canvas/canvas-composite-video-shadow.html [ Failure Pass ]
 
 crbug.com/734762 inspector-protocol/timeline/page-frames.js [ Failure ]
 
@@ -3790,6 +3800,7 @@
 
 # Test failing on WebKit Trusty
 crbug.com/829228 [ Linux Debug ] virtual/modern-media-controls/media/controls/modern/doubletap-to-jump-backwards.html [ Failure ]
+crbug.com/829228 [ Win7 Linux ] virtual/modern-media-controls/media/controls/modern/doubletap-to-jump-forwards-too-short.html [ Failure ]
 
 crbug.com/802915 css3/blending/isolation-should-include-non-local-background.html [ Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/accessibility/focusable-span.html b/third_party/WebKit/LayoutTests/accessibility/focusable-span.html
new file mode 100644
index 0000000..419e8ea
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/accessibility/focusable-span.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<p id="p"><span tabindex="0">hello</span>there</p>
+
+<script>
+test(() => {
+  let axParagraph = accessibilityController.accessibleElementById('p');
+  assert_not_equals(axParagraph, null);
+  let axSpan = axParagraph.childAtIndex(0);
+  assert_not_equals(axSpan, null);
+        assert_equals(axSpan.role, "AXRole: AXGenericContainer");
+        assert_true(axSpan.isFocusable);
+}, 'Ensure that spans with a tabindex get a focusable state.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-before.html
index 0ac5d73..31100c2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-device-disconnects-before.html
@@ -7,29 +7,20 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-              filters: [{services: ['health_thermometer']}],
-              optionalServices: [request_disconnection_service_uuid]
-            }))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        let measurement_interval;
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(ht => ht.getCharacteristic('measurement_interval'))
-            .then(mi => measurement_interval = mi)
-            .then(() => get_request_disconnection(gattServer))
-            .then(requestDisconnection => requestDisconnection())
-            .then(
-                () => assert_promise_rejects_with_message(
-                    measurement_interval.getDescriptor(user_description.name),
-                    new DOMException(
-                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                            '(Re)connect first with `device.gatt.connect`.',
-                        'NetworkError')));
-      });
-}, 'Device disconnects before getDescriptor. Reject with NetworkError.');
+const test_desc = 'Device disconnects before getDescriptor. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException(
+    'GATT Server is disconnected. Cannot retrieve descriptors. (Re)connect ' +
+    'first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ =>  ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+        characteristic.getDescriptor(user_description.name),
+        expected)),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-before.html
index 9c08c8c..8fa4e59c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/gen-descriptor-disconnect-called-before.html
@@ -7,26 +7,21 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithTrustedClick(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(service => service.getCharacteristic('measurement_interval'))
-            .then(measurement_interval => {
-              gattServer.disconnect();
-              return assert_promise_rejects_with_message(
-                  measurement_interval.getDescriptor(user_description.name),
-                  new DOMException(
-                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                          '(Re)connect first with `device.gatt.connect`.',
-                      'NetworkError'));
-            });
-      });
-}, 'disconnect() called before getDescriptor. Reject with NetworkError.');
+const test_desc = 'disconnect() called before getDescriptor. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'retrieve descriptors. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.getDescriptor(user_description.name),
+        expected);
+    }),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before-with-uuid.html
index 99e03927..d918e0e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before-with-uuid.html
@@ -7,29 +7,20 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-              filters: [{services: ['health_thermometer']}],
-              optionalServices: [request_disconnection_service_uuid]
-            }))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        let measurement_interval;
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(ht => ht.getCharacteristic('measurement_interval'))
-            .then(mi => measurement_interval = mi)
-            .then(() => get_request_disconnection(gattServer))
-            .then(requestDisconnection => requestDisconnection())
-            .then(
-                () => assert_promise_rejects_with_message(
-                    measurement_interval.getDescriptors(user_description.name),
-                    new DOMException(
-                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                            '(Re)connect first with `device.gatt.connect`.',
-                        'NetworkError')));
-      });
-}, 'Device disconnects before getDescriptors. Reject with NetworkError.');
+const test_desc = 'Device disconnects before getDescriptors. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException(
+    'GATT Server is disconnected. Cannot retrieve descriptors. (Re)connect ' +
+    'first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ =>  ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+        characteristic.getDescriptors(user_description.name),
+        expected)),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before.html
index 0493e3a1..a67e0cbe 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-device-disconnects-before.html
@@ -7,29 +7,20 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-              filters: [{services: ['health_thermometer']}],
-              optionalServices: [request_disconnection_service_uuid]
-            }))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        let measurement_interval;
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(ht => ht.getCharacteristic('measurement_interval'))
-            .then(mi => measurement_interval = mi)
-            .then(() => get_request_disconnection(gattServer))
-            .then(requestDisconnection => requestDisconnection())
-            .then(
-                () => assert_promise_rejects_with_message(
-                    measurement_interval.getDescriptors(),
-                    new DOMException(
-                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                            '(Re)connect first with `device.gatt.connect`.',
-                        'NetworkError')));
-      });
-}, 'Device disconnects before getDescriptors. Reject with NetworkError.');
+const test_desc = 'Device disconnects before getDescriptors. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException(
+    'GATT Server is disconnected. Cannot retrieve descriptors. (Re)connect ' +
+    'first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ =>  ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+        characteristic.getDescriptors(),
+        expected)),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before-with-uuid.html
index a4250319..b1e18fa 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before-with-uuid.html
@@ -7,26 +7,21 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithTrustedClick(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(service => service.getCharacteristic('measurement_interval'))
-            .then(measurement_interval => {
-              gattServer.disconnect();
-              return assert_promise_rejects_with_message(
-                  measurement_interval.getDescriptors(user_description.name),
-                  new DOMException(
-                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                          '(Re)connect first with `device.gatt.connect`.',
-                      'NetworkError'));
-            });
-      });
-}, 'disconnect() called before getDescriptors. Reject with NetworkError.');
+const test_desc = 'disconnect() called before getDescriptors. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'retrieve descriptors. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.getDescriptors(user_description.name),
+        expected);
+    }),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before.html
index c4f2ba69..49eebaf 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptors/gen-descriptor-disconnect-called-before.html
@@ -7,26 +7,21 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithTrustedClick(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(service => service.getCharacteristic('measurement_interval'))
-            .then(measurement_interval => {
-              gattServer.disconnect();
-              return assert_promise_rejects_with_message(
-                  measurement_interval.getDescriptors(),
-                  new DOMException(
-                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                          '(Re)connect first with `device.gatt.connect`.',
-                      'NetworkError'));
-            });
-      });
-}, 'disconnect() called before getDescriptors. Reject with NetworkError.');
+const test_desc = 'disconnect() called before getDescriptors. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'retrieve descriptors. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.getDescriptors(),
+        expected);
+    }),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-stop-start-stop.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-stop-start-stop.html
index 7a0090d..c6a924c 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-stop-start-stop.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/start-stop-start-stop.html
@@ -6,20 +6,26 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  return setBluetoothFakeAdapter('HeartRateAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('heart_rate'))
-    .then(service => service.getCharacteristic('heart_rate_measurement'))
-    .then(characteristic => {
-      return characteristic.startNotifications()
-        .then(() => characteristic.stopNotifications())
-        .then(() => characteristic.startNotifications())
-        .then(() => characteristic.stopNotifications());
-    });
-  // TODO(ortuno): Assert that notifications are not active.
-  // http://crbug.com/600762
-}, 'Start -> stop -> start -> stop.');
+const test_desc = 'The characteristic should be able to start and stop ' +
+    'notifications multiple times in a row.';
+let characteristic, fake_characteristic;
+let startStopNotifications = () => {
+  return fake_characteristic.setNextSubscribeToNotificationsResponse(
+          HCI_SUCCESS)
+      .then(() => characteristic.startNotifications())
+      .then(() => fake_characteristic.isNotifying())
+      .then(isNotifying => assert_true(isNotifying))
+      .then(() =>
+          fake_characteristic.setNextUnsubscribeFromNotificationsResponse(
+              HCI_SUCCESS))
+      .then(() => characteristic.stopNotifications())
+      .then(() => fake_characteristic.isNotifying())
+      .then(isNotifying => assert_false(isNotifying));
+}
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({characteristic, fake_characteristic} = _))
+    .then(() => startStopNotifications())
+    .then(() => startStopNotifications()),
+    test_desc);
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-before.html
index d48fdb8..2e5d16b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-device-disconnects-before.html
@@ -7,28 +7,19 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.readValue(),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before readValue. Reject with NetworkError.');
+const test_desc = 'Device disconnects before readValue. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+          characteristic.readValue(),
+          expected)),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-before.html
index 116a778..4b8e541 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/readValue/gen-gatt-op-disconnect-called-before.html
@@ -7,25 +7,22 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.readValue(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before readValue. Reject with NetworkError.');
+const test_desc = 'disconnect() called before readValue. ' +
+    'Reject with NetworkError.';
+const value = new Uint8Array([1]);
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.readValue(),
+        expected);
+    }),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-before.html
index 9e59e33..d7fb360 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-device-disconnects-before.html
@@ -7,28 +7,19 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.startNotifications(),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before startNotifications. Reject with NetworkError.');
+const test_desc = 'Device disconnects before startNotifications. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+          characteristic.startNotifications(),
+          expected)),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-before.html
index de76b6a..39873b4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/startNotifications/gen-gatt-op-disconnect-called-before.html
@@ -7,25 +7,22 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.startNotifications(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before startNotifications. Reject with NetworkError.');
+const test_desc = 'disconnect() called before startNotifications. ' +
+    'Reject with NetworkError.';
+const value = new Uint8Array([1]);
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.startNotifications(),
+        expected);
+    }),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html
index cd29be37..b07c3fc4e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-device-disconnects-before.html
@@ -7,28 +7,19 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.stopNotifications(),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before stopNotifications. Reject with NetworkError.');
+const test_desc = 'Device disconnects before stopNotifications. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+          characteristic.stopNotifications(),
+          expected)),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html
index 8363837..99cf7ee 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/stopNotifications/gen-gatt-op-disconnect-called-before.html
@@ -7,25 +7,22 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.stopNotifications(),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before stopNotifications. Reject with NetworkError.');
+const test_desc = 'disconnect() called before stopNotifications. ' +
+    'Reject with NetworkError.';
+const value = new Uint8Array([1]);
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.stopNotifications(),
+        expected);
+    }),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-before.html
index b3281161..74d6bba 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-device-disconnects-before.html
@@ -7,28 +7,19 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.writeValue(val),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before writeValue. Reject with NetworkError.');
+const test_desc = 'Device disconnects before writeValue. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+          characteristic.writeValue(new Uint8Array(1 /*length */)),
+          expected)),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-before.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-before.html
index 80f1435..6db99d2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-before.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/writeValue/gen-gatt-op-disconnect-called-before.html
@@ -7,25 +7,22 @@
 <script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.writeValue(val),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before writeValue. Reject with NetworkError.');
+const test_desc = 'disconnect() called before writeValue. ' +
+    'Reject with NetworkError.';
+const value = new Uint8Array([1]);
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.writeValue(value),
+        expected);
+    }),
+    test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-before.js
index 11dac5d..08ab868 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-before.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-device-disconnects-before.js
@@ -1,28 +1,20 @@
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(() => requestDeviceWithTrustedClick({
-              filters: [{services: ['health_thermometer']}],
-              optionalServices: [request_disconnection_service_uuid]
-            }))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        let measurement_interval;
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(ht => ht.getCharacteristic('measurement_interval'))
-            .then(mi => measurement_interval = mi)
-            .then(() => get_request_disconnection(gattServer))
-            .then(requestDisconnection => requestDisconnection())
-            .then(
-                () => assert_promise_rejects_with_message(
-                    measurement_interval.CALLS(
-                        [getDescriptor(user_description.name) |
-                         getDescriptors(user_description.name)[UUID] |
-                         getDescriptors()]),
-                    new DOMException(
-                        'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                            '(Re)connect first with `device.gatt.connect`.',
-                        'NetworkError')));
-      });
-}, 'Device disconnects before FUNCTION_NAME. Reject with NetworkError.');
+const test_desc = 'Device disconnects before FUNCTION_NAME. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException(
+    'GATT Server is disconnected. Cannot retrieve descriptors. (Re)connect ' +
+    'first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ =>  ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+        characteristic.CALLS([
+          getDescriptor(user_description.name) |
+          getDescriptors(user_description.name)[UUID] |
+          getDescriptors()
+        ]),
+        expected)),
+    test_desc);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-before.js
index 974f437..bacf0d91 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-before.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/descriptor-disconnect-called-before.js
@@ -1,25 +1,21 @@
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-      .then(
-          () => requestDeviceWithTrustedClick(
-              {filters: [{services: ['health_thermometer']}]}))
-      .then(device => device.gatt.connect())
-      .then(gattServer => {
-        return gattServer.getPrimaryService('health_thermometer')
-            .then(service => service.getCharacteristic('measurement_interval'))
-            .then(measurement_interval => {
-              gattServer.disconnect();
-              return assert_promise_rejects_with_message(
-                  measurement_interval.CALLS(
-                      [getDescriptor(user_description.name) |
-                       getDescriptors(user_description.name)[UUID] |
-                       getDescriptors()]),
-                  new DOMException(
-                      'GATT Server is disconnected. Cannot retrieve descriptors. ' +
-                          '(Re)connect first with `device.gatt.connect`.',
-                      'NetworkError'));
-            });
-      });
-}, 'disconnect() called before FUNCTION_NAME. Reject with NetworkError.');
+const test_desc = 'disconnect() called before FUNCTION_NAME. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'retrieve descriptors. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError');
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.CALLS([
+          getDescriptor(user_description.name) |
+          getDescriptors(user_description.name)[UUID] |
+          getDescriptors()
+        ]),
+        expected);
+    }),
+    test_desc);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
index 965fbad..12f7a9b 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-device-disconnects-before.js
@@ -1,28 +1,20 @@
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}],
-      optionalServices: [request_disconnection_service_uuid]
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      let measurement_interval;
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(ht=> ht.getCharacteristic('measurement_interval'))
-        .then(mi => measurement_interval = mi)
-        .then(() => get_request_disconnection(gattServer))
-        .then(requestDisconnection => requestDisconnection())
-        .then(() => assert_promise_rejects_with_message(
-          measurement_interval.CALLS([
+const test_desc = 'Device disconnects before FUNCTION_NAME. ' +
+    'Reject with NetworkError.';
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => simulateGATTDisconnectionAndWait(device, fake_peripheral))
+    .then(() => assert_promise_rejects_with_message(
+          characteristic.CALLS([
             readValue()|
-            writeValue(val)|
+            writeValue(new Uint8Array(1 /*length */))|
             startNotifications()|
-            stopNotifications()]),
-          new DOMException(
-            'GATT Server is disconnected. Cannot perform GATT operations. ' +
-            '(Re)connect first with `device.gatt.connect`.',
-            'NetworkError')));
-    });
-}, 'Device disconnects before FUNCTION_NAME. Reject with NetworkError.');
+            stopNotifications()
+          ]),
+          expected)),
+    test_desc);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
index ef99764..fedf712 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/characteristic/gatt-op-disconnect-called-before.js
@@ -1,25 +1,23 @@
 'use strict';
-bluetooth_test(() => {
-  let val = new Uint8Array([1]);
-  return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => {
-      return gattServer.getPrimaryService('health_thermometer')
-        .then(service => service.getCharacteristic('measurement_interval'))
-        .then(measurement_interval => {
-          gattServer.disconnect();
-          return assert_promise_rejects_with_message(
-            measurement_interval.CALLS([
-              readValue()|
-              writeValue(val)|
-              startNotifications()|
-              stopNotifications()]),
-            new DOMException(
-              'GATT Server is disconnected. Cannot perform GATT operations. ' +
-              '(Re)connect first with `device.gatt.connect`.',
-              'NetworkError'));
-        });
-    });
-}, 'disconnect() called before FUNCTION_NAME. Reject with NetworkError.');
+const test_desc = 'disconnect() called before FUNCTION_NAME. ' +
+    'Reject with NetworkError.';
+const value = new Uint8Array([1]);
+const expected = new DOMException('GATT Server is disconnected. Cannot ' +
+    'perform GATT operations. (Re)connect first with `device.gatt.connect`.',
+    'NetworkError')
+let device, characteristic, fake_peripheral;
+
+bluetooth_test(() => getMeasurementIntervalCharacteristic()
+    .then(_ => ({device, characteristic, fake_peripheral} = _))
+    .then(() => {
+      device.gatt.disconnect();
+      return assert_promise_rejects_with_message(
+        characteristic.CALLS([
+          readValue()|
+          writeValue(value)|
+          startNotifications()|
+          stopNotifications()
+        ]),
+        expected);
+    }),
+    test_desc);
diff --git a/third_party/WebKit/LayoutTests/css3/background/background-border-image-auto-margins-expected.html b/third_party/WebKit/LayoutTests/css3/background/background-border-image-auto-margins-expected.html
new file mode 100644
index 0000000..3c5ef2a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/background/background-border-image-auto-margins-expected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>

+<style>

+  .bordered {

+	  margin: auto;

+	  margin-top: -40px;

+	  width: 260px;

+	  height: 260px;

+	  background-color: red;

+	  background-image: linear-gradient(green, green);

+	  border: 40px solid green;

+  }

+</style>

+<div id="spacer" style="height: 100px; width: 100px;"></div>

+<!-- Test should show no red background color around the gradient. -->

+<div class="bordered"></div>

diff --git a/third_party/WebKit/LayoutTests/css3/background/background-border-image-auto-margins.html b/third_party/WebKit/LayoutTests/css3/background/background-border-image-auto-margins.html
new file mode 100644
index 0000000..9b6fb5be
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/background/background-border-image-auto-margins.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>

+<style>

+  .bordered {

+	  margin: auto;

+	  width: 200px;

+	  height: 200px;

+	  background-color: red;

+	  background-image: linear-gradient(green, green);

+	  border: 30px solid red;

+	  border-image: repeat stretch 40 / 40px / 40px linear-gradient(green, green);

+  }

+</style>

+<div id="spacer" style="height: 100px; width: 100px;"></div>

+<!-- Test should show no red background color around the gradient. -->

+<div class="bordered"></div>

diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index bcb1087..bf8e4e04 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -5137,6 +5137,30 @@
      {}
     ]
    ],
+   "pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual.html": [
+    [
+     "/pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual.html",
+     {}
+    ]
+   ],
+   "pointerevents/extension/pointerevent_touch-action-pan-left-css_touch-manual.html": [
+    [
+     "/pointerevents/extension/pointerevent_touch-action-pan-left-css_touch-manual.html",
+     {}
+    ]
+   ],
+   "pointerevents/extension/pointerevent_touch-action-pan-right-css_touch-manual.html": [
+    [
+     "/pointerevents/extension/pointerevent_touch-action-pan-right-css_touch-manual.html",
+     {}
+    ]
+   ],
+   "pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual.html": [
+    [
+     "/pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual.html",
+     {}
+    ]
+   ],
    "pointerevents/html/pointerevent_drag_interaction-manual.html": [
     [
      "/pointerevents/html/pointerevent_drag_interaction-manual.html",
@@ -5431,30 +5455,6 @@
      {}
     ]
    ],
-   "pointerevents/pointerevent_touch-action-pan-down-css_touch-manual.html": [
-    [
-     "/pointerevents/pointerevent_touch-action-pan-down-css_touch-manual.html",
-     {}
-    ]
-   ],
-   "pointerevents/pointerevent_touch-action-pan-left-css_touch-manual.html": [
-    [
-     "/pointerevents/pointerevent_touch-action-pan-left-css_touch-manual.html",
-     {}
-    ]
-   ],
-   "pointerevents/pointerevent_touch-action-pan-right-css_touch-manual.html": [
-    [
-     "/pointerevents/pointerevent_touch-action-pan-right-css_touch-manual.html",
-     {}
-    ]
-   ],
-   "pointerevents/pointerevent_touch-action-pan-up-css_touch-manual.html": [
-    [
-     "/pointerevents/pointerevent_touch-action-pan-up-css_touch-manual.html",
-     {}
-    ]
-   ],
    "pointerevents/pointerevent_touch-action-pan-x-css_touch-manual.html": [
     [
      "/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual.html",
@@ -48535,6 +48535,18 @@
      {}
     ]
    ],
+   "css/css-scoping/shadow-reassign-dynamic-001.html": [
+    [
+     "/css/css-scoping/shadow-reassign-dynamic-001.html",
+     [
+      [
+       "/css/css-scoping/reference/green-box.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-scoping/shadow-root-insert-into-document.html": [
     [
      "/css/css-scoping/shadow-root-insert-into-document.html",
@@ -83579,6 +83591,18 @@
      {}
     ]
    ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002.html": [
+    [
+     "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002.html",
+     [
+      [
+       "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml": [
     [
      "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml",
@@ -84491,6 +84515,54 @@
      {}
     ]
    ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html": [
+    [
+     "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html",
+     [
+      [
+       "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html": [
+    [
+     "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html",
+     [
+      [
+       "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html": [
+    [
+     "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html",
+     [
+      [
+       "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html": [
+    [
+     "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html",
+     [
+      [
+       "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001.html": [
     [
      "/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001.html",
@@ -100517,6 +100589,11 @@
      {}
     ]
    ],
+   "content-security-policy/support/file-prefetch-allowed.html": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/support/fonts.css": [
     [
      {}
@@ -116917,6 +116994,11 @@
      {}
     ]
    ],
+   "css/css-fonts/variations/font-shorthand-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-fonts/variations/font-stretch-expected.txt": [
     [
      {}
@@ -116932,6 +117014,11 @@
      {}
     ]
    ],
+   "css/css-fonts/variations/font-weight-parsing-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-fonts/variations/resources/ahem.ttc": [
     [
      {}
@@ -119102,6 +119189,11 @@
      {}
     ]
    ],
+   "css/css-shadow-parts/support/shadow-helper.js": [
+    [
+     {}
+    ]
+   ],
    "css/css-shapes/OWNERS": [
     [
      {}
@@ -125482,6 +125574,21 @@
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-gap-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-template-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/image-rendering-expected.txt": [
     [
      {}
@@ -129947,6 +130054,11 @@
      {}
     ]
    ],
+   "css/cssom-view/cssom-getBoxQuads-001-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/cssom-view/cssom-getClientRects-002-expected.txt": [
     [
      {}
@@ -132972,6 +133084,11 @@
      {}
     ]
    ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001-ref.xhtml": [
     [
      {}
@@ -133272,6 +133389,16 @@
      {}
     ]
    ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001-ref.html": [
     [
      {}
@@ -133927,6 +134054,11 @@
      {}
     ]
    ],
+   "custom-elements/HTMLElement-constructor-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "custom-elements/OWNERS": [
     [
      {}
@@ -133947,6 +134079,11 @@
      {}
     ]
    ],
+   "custom-elements/htmlconstructor/newtarget-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "custom-elements/parser/parser-uses-registry-of-owner-document-expected.txt": [
     [
      {}
@@ -140377,6 +140514,11 @@
      {}
     ]
    ],
+   "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt": [
+    [
+     {}
+    ]
+   ],
    "html/dom/elements-embedded.js": [
     [
      {}
@@ -146522,6 +146664,11 @@
      {}
     ]
    ],
+   "html/semantics/document-metadata/the-link-element/resources/link-rel-attribute.css": [
+    [
+     {}
+    ]
+   ],
    "html/semantics/document-metadata/the-link-element/resources/stylesheet.css": [
     [
      {}
@@ -151407,6 +151554,11 @@
      {}
     ]
    ],
+   "infrastructure/testdriver/send_keys-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "innerText/OWNERS": [
     [
      {}
@@ -161297,11 +161449,6 @@
      {}
     ]
    ],
-   "url/urlsearchparams-foreach-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "url/urltestdata.json": [
     [
      {}
@@ -161602,11 +161749,6 @@
      {}
     ]
    ],
-   "web-animations/interfaces/Document/getAnimations-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "web-animations/interfaces/KeyframeEffect/constructor-expected.txt": [
     [
      {}
@@ -166332,6 +166474,11 @@
      {}
     ]
    ],
+   "xhr/resources/header-user-agent.py": [
+    [
+     {}
+    ]
+   ],
    "xhr/resources/headers-basic.asis": [
     [
      {}
@@ -177124,13 +177271,17 @@
    "content-security-policy/navigate-to/child-navigates-parent-allowed.html": [
     [
      "/content-security-policy/navigate-to/child-navigates-parent-allowed.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/child-navigates-parent-blocked.html": [
     [
      "/content-security-policy/navigate-to/child-navigates-parent-blocked.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/form-action/form-action-allows-navigate-to-allows.html": [
@@ -177232,37 +177383,49 @@
    "content-security-policy/navigate-to/link-click-allowed.html": [
     [
      "/content-security-policy/navigate-to/link-click-allowed.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/link-click-blocked.html": [
     [
      "/content-security-policy/navigate-to/link-click-blocked.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/link-click-cross-origin-allowed.sub.html": [
     [
      "/content-security-policy/navigate-to/link-click-cross-origin-allowed.sub.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/link-click-cross-origin-blocked.sub.html": [
     [
      "/content-security-policy/navigate-to/link-click-cross-origin-blocked.sub.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/link-click-redirected-allowed.html": [
     [
      "/content-security-policy/navigate-to/link-click-redirected-allowed.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/link-click-redirected-blocked.sub.html": [
     [
      "/content-security-policy/navigate-to/link-click-redirected-blocked.sub.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "content-security-policy/navigate-to/meta-refresh-allowed.html": [
@@ -181675,6 +181838,12 @@
      {}
     ]
    ],
+   "css/css-multicol/going-out-of-flow-after-spanner.html": [
+    [
+     "/css/css-multicol/going-out-of-flow-after-spanner.html",
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-gap-animation-001.html": [
     [
      "/css/css-multicol/multicol-gap-animation-001.html",
@@ -182053,6 +182222,54 @@
      {}
     ]
    ],
+   "css/css-shadow-parts/all-hosts.html": [
+    [
+     "/css/css-shadow-parts/all-hosts.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/chaining-invalid-selector.html": [
+    [
+     "/css/css-shadow-parts/chaining-invalid-selector.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/complex-matching.html": [
+    [
+     "/css/css-shadow-parts/complex-matching.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/complex-non-matching.html": [
+    [
+     "/css/css-shadow-parts/complex-non-matching.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/different-host.html": [
+    [
+     "/css/css-shadow-parts/different-host.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/host-stylesheet.html": [
+    [
+     "/css/css-shadow-parts/host-stylesheet.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/inner-host.html": [
+    [
+     "/css/css-shadow-parts/inner-host.html",
+     {}
+    ]
+   ],
+   "css/css-shadow-parts/simple.html": [
+    [
+     "/css/css-shadow-parts/simple.html",
+     {}
+    ]
+   ],
    "css/css-shapes/basic-shape-circle-ellipse-serialization.html": [
     [
      "/css/css-shapes/basic-shape-circle-ellipse-serialization.html",
@@ -185257,6 +185474,60 @@
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-area.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-area.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-auto-columns-rows.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-auto-columns-rows.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-auto-flow.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-auto-flow.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-gap.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-gap.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-start-end.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-start-end.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-template-areas.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-template-areas.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-template-columns-rows.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-template-columns-rows.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid-template.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid-template.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/grid.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/grid.html",
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/height.html": [
     [
      "/css/css-typed-om/the-stylepropertymap/properties/height.html",
@@ -186409,6 +186680,12 @@
      {}
     ]
    ],
+   "css/cssom-view/cssom-getBoxQuads-001.html": [
+    [
+     "/css/cssom-view/cssom-getBoxQuads-001.html",
+     {}
+    ]
+   ],
    "css/cssom-view/cssom-getClientRects-002.html": [
     [
      "/css/cssom-view/cssom-getClientRects-002.html",
@@ -196693,6 +196970,12 @@
      {}
     ]
    ],
+   "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js": [
+    [
+     "/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.html",
+     {}
+    ]
+   ],
    "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js": [
     [
      "/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.html",
@@ -197649,6 +197932,12 @@
      {}
     ]
    ],
+   "html/semantics/document-metadata/the-link-element/link-rel-attribute.html": [
+    [
+     "/html/semantics/document-metadata/the-link-element/link-rel-attribute.html",
+     {}
+    ]
+   ],
    "html/semantics/document-metadata/the-link-element/link-rellist.html": [
     [
      "/html/semantics/document-metadata/the-link-element/link-rellist.html",
@@ -218195,6 +218484,12 @@
      {}
     ]
    ],
+   "pointerevents/extension/pointerevent_touch-action-verification.html": [
+    [
+     "/pointerevents/extension/pointerevent_touch-action-verification.html",
+     {}
+    ]
+   ],
    "pointerevents/idlharness.html": [
     [
      "/pointerevents/idlharness.html",
@@ -237055,6 +237350,12 @@
      {}
     ]
    ],
+   "workers/interfaces/WorkerUtils/WindowTimers/005.html": [
+    [
+     "/workers/interfaces/WorkerUtils/WindowTimers/005.html",
+     {}
+    ]
+   ],
    "workers/interfaces/WorkerUtils/importScripts/001.worker.js": [
     [
      "/workers/interfaces/WorkerUtils/importScripts/001.worker.html",
@@ -238187,6 +238488,18 @@
      {}
     ]
    ],
+   "xhr/header-user-agent-async.htm": [
+    [
+     "/xhr/header-user-agent-async.htm",
+     {}
+    ]
+   ],
+   "xhr/header-user-agent-sync.htm": [
+    [
+     "/xhr/header-user-agent-sync.htm",
+     {}
+    ]
+   ],
    "xhr/headers-normalize-response.htm": [
     [
      "/xhr/headers-normalize-response.htm",
@@ -252677,19 +252990,19 @@
    "testharness"
   ],
   "bluetooth/device/gattserverdisconnected-event/disconnected.https.html": [
-   "ee14803683f17133c6147c2eaf250befbaaf0be1",
+   "709429f27f57c27319fd14bf419dca46e5f8cee8",
    "testharness"
   ],
   "bluetooth/device/gattserverdisconnected-event/disconnected_gc.https.html": [
-   "6b4676f3b9b34ffa45ff616eb9d3c514e33ad471",
+   "4414277635ef6d8fb5fc941635e89587f3615436",
    "testharness"
   ],
   "bluetooth/device/gattserverdisconnected-event/one-event-per-disconnection.https.html": [
-   "a0fbabe94fe246c2031fe4baf30ba52b58a17b36",
+   "fbb48d3173b0a35aa517b4c2ffb649a5a9280870",
    "testharness"
   ],
   "bluetooth/device/gattserverdisconnected-event/reconnect-during-disconnected-event.https.html": [
-   "a66e1df2d870876d06ad0990ea523153539eff0e",
+   "c0142db0ee3a784184fd78b5255054539ae8b880",
    "testharness"
   ],
   "bluetooth/generate.py": [
@@ -252705,7 +253018,7 @@
    "testharness"
   ],
   "bluetooth/idl/idl-BluetoothDevice.https.html": [
-   "b4c78ec760b61420a82f4a1a2951fb2eaa6af10d",
+   "19d0c7b6e08ada8f3ed58921e662fe2d06ed55a4",
    "testharness"
   ],
   "bluetooth/idl/idl-BluetoothUUID.html": [
@@ -252725,7 +253038,7 @@
    "testharness"
   ],
   "bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.html": [
-   "c6e28d2c8509ea2f0e3633b2517ad68631119ffe",
+   "1e4a408d0497db72b1f1dc201f278ae7a701e0ab",
    "testharness"
   ],
   "bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.html": [
@@ -252737,7 +253050,7 @@
    "testharness"
   ],
   "bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.html": [
-   "c3fbd786e7cf05f72eb0afadba8924c43f2fdc11",
+   "5e3c7ac81089d9a44d07812b43e2ed63e1d39f11",
    "testharness"
   ],
   "bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.html": [
@@ -252821,11 +253134,11 @@
    "testharness"
   ],
   "bluetooth/requestDevice/cross-origin-iframe.sub.https.html": [
-   "dc9df7886d4a020b1853d7a54d67a8b1249c56c7",
+   "e5e22c1b7811699bb8bfd2b6593edf78a270a658",
    "testharness"
   ],
   "bluetooth/requestDevice/discovery-succeeds.https.html": [
-   "da1be25f068cdb9602d6207b6af0170e232e68cd",
+   "0b75576a4742cc7496a890b273f2bb1c1f56017c",
    "testharness"
   ],
   "bluetooth/requestDevice/filter-matches.https.html": [
@@ -252853,7 +253166,7 @@
    "testharness"
   ],
   "bluetooth/requestDevice/request-from-sandboxed-iframe.https.html": [
-   "917a1be9111f04e67231c92921b1f856685deb55",
+   "fe1db81b730c5facaf45718e4536793f6c10f72a",
    "testharness"
   ],
   "bluetooth/requestDevice/same-device.https.html": [
@@ -252865,7 +253178,7 @@
    "testharness"
   ],
   "bluetooth/resources/bluetooth-helpers.js": [
-   "f19d8ab872de44f50c37aac3e3ad97ab0e502b59",
+   "c86347e3b26f369907ec7f0c3adaee20b630ade8",
    "support"
   ],
   "bluetooth/resources/health-thermometer-iframe.html": [
@@ -252893,7 +253206,7 @@
    "support"
   ],
   "bluetooth/script-tests/server/disconnect-called-before.js": [
-   "3ea0ed09ee2197401437b4d89c335181b2983b17",
+   "e8c31819a1250ec08217766f4b8228aa195a2d97",
    "support"
   ],
   "bluetooth/script-tests/server/disconnect-called-during-error.js": [
@@ -252937,19 +253250,19 @@
    "support"
   ],
   "bluetooth/script-tests/server/invalid-service-name.js": [
-   "0c2b3d759291d4e247df131f82daa2be847cb1d9",
+   "b7f6b5db7334b6467ec446fa5b0ea637306c8911",
    "support"
   ],
   "bluetooth/script-tests/server/no-permission-absent-service.js": [
-   "78388409359e9e5bc36b304865e6e14d30769e12",
+   "8b0fb54dfffb7855923c51031969d0b5f9276918",
    "support"
   ],
   "bluetooth/script-tests/server/no-permission-for-any-service.js": [
-   "f9230d7d66815561f575ba55409f2ca4fc0a67b8",
+   "81d9589206bbe46c784b415e3b250f9b05443e60",
    "support"
   ],
   "bluetooth/script-tests/server/no-permission-present-service.js": [
-   "0f2e002be9842eb06f31f63de31c96fcd2820258",
+   "3200cee17040440cfe67a354c1fbc90fe537de81",
    "support"
   ],
   "bluetooth/script-tests/server/service-not-found.js": [
@@ -253017,7 +253330,7 @@
    "testharness"
   ],
   "bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.html": [
-   "d1f9a8a640ae96589e7406e6c296137a08c4ea01",
+   "f4d97a6bd746c3d127378ad9c692b542c114d4e7",
    "testharness"
   ],
   "bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.html": [
@@ -253061,19 +253374,19 @@
    "testharness"
   ],
   "bluetooth/server/getPrimaryService/gen-invalid-service-name.https.html": [
-   "5728e27aba6861ddfc7cb4278b116ee9d94fbb7a",
+   "4886cfde2f041edb7445c170b1a6c1a61b37f075",
    "testharness"
   ],
   "bluetooth/server/getPrimaryService/gen-no-permission-absent-service.https.html": [
-   "ed7a5e4ab959f5d8b5e72154c68e397128c4d071",
+   "e40e8af7de38257160e5e4ef38f9c85667c5c274",
    "testharness"
   ],
   "bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.html": [
-   "86fab4a334c37e61ff9acbeafba071fd0aab5d30",
+   "1b87eb365022aec9c1f30fea557c996f232f6331",
    "testharness"
   ],
   "bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.html": [
-   "46717e75f676ff426773a79c4c01b2412b2c70ee",
+   "2ea2cff3588b10a387a182b31dac40dea52ab7b4",
    "testharness"
   ],
   "bluetooth/server/getPrimaryService/gen-service-not-found.https.html": [
@@ -253089,7 +253402,7 @@
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.html": [
-   "a58240662db7a01866c144e95d828cbeb6340a49",
+   "cb92cf7ba078851ccda73c6b069de825af05286b",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/blocklisted-services.https.html": [
@@ -253101,11 +253414,11 @@
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.html": [
-   "ff1af939fa2b5b77523712a4281839349f00e43b",
+   "18aca22776caae36b47a58866f78a72ecc7f9f79",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.html": [
-   "b579d5e4ea1762930d2831b082fa6d92810e59c3",
+   "c98a723569159c5a139fd74fac0672590106f258",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.html": [
@@ -253181,23 +253494,23 @@
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-invalid-service-name.https.html": [
-   "878c93bcea918a0d008ab44552f7b26ff6f7a151",
+   "819b61b51d4f9ed60fdedcb3a719aa81c11d0d1b",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.https.html": [
-   "f96b80beb4c79a2bda3cc06bc11d6aa5fb392ada",
+   "bec664d2805d7c98dccf83b93a6d49a145f06e19",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.https.html": [
-   "96a195a1b25cf1985ccbc949431a28226f3ee684",
+   "4cda505124cfe20c8b8e3630774b4f734eea10fe",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.html": [
-   "618cf409fffba7679abe515982695db4bb1a5f68",
+   "3a9ed522982b890296f926759954d463915818e9",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.html": [
-   "3126e3b157670506442b0032de53e6d29e4c30f6",
+   "6148df8814a7f9bc7bbad6cb278cffded99bcb9d",
    "testharness"
   ],
   "bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.html": [
@@ -254617,7 +254930,7 @@
    "support"
   ],
   "content-security-policy/navigate-to/child-navigates-parent-allowed.html": [
-   "bacb9cd495aa4309349a4f80b3f886ee94542027",
+   "54f517e54260d444bce9866204172e6272c8f284",
    "testharness"
   ],
   "content-security-policy/navigate-to/child-navigates-parent-allowed.html.headers": [
@@ -254629,7 +254942,7 @@
    "support"
   ],
   "content-security-policy/navigate-to/child-navigates-parent-blocked.html": [
-   "fceb74d2ed4e5279da6455bff9abafb00c4f97cb",
+   "1e35f9b195940f9134d53849de6fafef370f0744",
    "testharness"
   ],
   "content-security-policy/navigate-to/child-navigates-parent-blocked.html.headers": [
@@ -254725,7 +255038,7 @@
    "testharness"
   ],
   "content-security-policy/navigate-to/link-click-allowed.html": [
-   "a4200a991a2e6d93ccf9f6dfedcaa5e5d75ead9e",
+   "133c29674edc3a53e5bc65f5f2e8b72a8ccc7bdf",
    "testharness"
   ],
   "content-security-policy/navigate-to/link-click-blocked-expected.txt": [
@@ -254733,11 +255046,11 @@
    "support"
   ],
   "content-security-policy/navigate-to/link-click-blocked.html": [
-   "04fafa60b6934d119997c37058605007cc8f2632",
+   "14861dffd89d889059d30f54a698ecff60a6c950",
    "testharness"
   ],
   "content-security-policy/navigate-to/link-click-cross-origin-allowed.sub.html": [
-   "b1805390dada0f11c4dc45adf647133237934c45",
+   "55f36a1c3965f6acdc93fa23dba543608d9912d2",
    "testharness"
   ],
   "content-security-policy/navigate-to/link-click-cross-origin-blocked.sub-expected.txt": [
@@ -254745,11 +255058,11 @@
    "support"
   ],
   "content-security-policy/navigate-to/link-click-cross-origin-blocked.sub.html": [
-   "1f1def5a103caf71d8cd5ddc870f3050d98ec9f2",
+   "7247d779fa3729a9118859dd41e94aae7505afbd",
    "testharness"
   ],
   "content-security-policy/navigate-to/link-click-redirected-allowed.html": [
-   "d89312685c3022438a2e55a025d330bb73eeedc0",
+   "f8a0f0646b5493be62ffeb541edbb039c1321d06",
    "testharness"
   ],
   "content-security-policy/navigate-to/link-click-redirected-blocked.sub-expected.txt": [
@@ -254757,7 +255070,7 @@
    "support"
   ],
   "content-security-policy/navigate-to/link-click-redirected-blocked.sub.html": [
-   "0b0c2c02fde1ea8aad7c25db210ea5fdd6f391e5",
+   "46ac39a0a5eb10fbb18ac6b19a087c81b66427f6",
    "testharness"
   ],
   "content-security-policy/navigate-to/meta-refresh-allowed.html": [
@@ -254973,7 +255286,7 @@
    "testharness"
   ],
   "content-security-policy/prefetch-src/prefetch-allowed.html": [
-   "8c88fefbffb6f5270b64cd8d81b405857a9c1123",
+   "45e002cde6f0e61cfc4876fad6d5af7341fec346",
    "testharness"
   ],
   "content-security-policy/prefetch-src/prefetch-blocked.html": [
@@ -255928,6 +256241,10 @@
    "13c3f17f61fa30ac6d7c5c54bd73a6f93d1611c1",
    "support"
   ],
+  "content-security-policy/support/file-prefetch-allowed.html": [
+   "13e9e4ef7ad82d1f9404381a3eeb2a00c319b52a",
+   "support"
+  ],
   "content-security-policy/support/fonts.css": [
    "4577fb4f580bfd9c723e1a5ee0f9d8438ce41ac9",
    "support"
@@ -286448,6 +286765,10 @@
    "b9aa593e5fcba0d7af8f66446d473608a7025f1c",
    "testharness"
   ],
+  "css/css-fonts/variations/font-shorthand-expected.txt": [
+   "ebc1d4d3ccac459d39cf416f0caccb6cb512d048",
+   "support"
+  ],
   "css/css-fonts/variations/font-shorthand.html": [
    "7e193d5e25424699652e64b419e1d1384370e3a4",
    "testharness"
@@ -286500,6 +286821,10 @@
    "3e40c3f6d48096cbebe2a5c5becc627b17ed35a2",
    "testharness"
   ],
+  "css/css-fonts/variations/font-weight-parsing-expected.txt": [
+   "936c28d1116cda3fb055133e8b46728bbdc85d55",
+   "support"
+  ],
   "css/css-fonts/variations/font-weight-parsing.html": [
    "3cf6476257a47f8518ad19079cab4923598dc7da",
    "testharness"
@@ -289480,6 +289805,10 @@
    "435614e6d5dcdbd8325687f70014bea0e3dea5f7",
    "reftest"
   ],
+  "css/css-multicol/going-out-of-flow-after-spanner.html": [
+   "d76366e15dd552c4c013c98e2de70ce6b38f0202",
+   "testharness"
+  ],
   "css/css-multicol/multicol-basic-001.html": [
    "7cb568d75ea3fec9046ec69770fe59b8539beb02",
    "reftest"
@@ -289833,7 +290162,7 @@
    "support"
   ],
   "css/css-multicol/multicol-gap-001.xht": [
-   "df60ea8599762b30a4b2c8e9d1e8164e5275a8fa",
+   "3d38feef824413b93cf65b2955044b1929390138",
    "reftest"
   ],
   "css/css-multicol/multicol-gap-002-ref.xht": [
@@ -289845,7 +290174,7 @@
    "reftest"
   ],
   "css/css-multicol/multicol-gap-003.xht": [
-   "16067366a3c1922e9c8415b21e513edd47c2305f",
+   "2cca63f39c97fd2f4de46bc0f892f07ad12b91ef",
    "reftest"
   ],
   "css/css-multicol/multicol-gap-animation-001.html": [
@@ -291748,6 +292077,10 @@
    "ab3c3d205e59df800ba5b4217245b83685521c31",
    "reftest"
   ],
+  "css/css-scoping/shadow-reassign-dynamic-001.html": [
+   "11ed4da2e6ce88d8a2b98a8f1c814417ef7770dd",
+   "reftest"
+  ],
   "css/css-scoping/shadow-root-insert-into-document.html": [
    "2cee9fff35c9222074f4ef78dcfcb8a3e02bbc98",
    "reftest"
@@ -291880,6 +292213,42 @@
    "dfaf8675bec557c9f2178ad48b29c803f94056b5",
    "testharness"
   ],
+  "css/css-shadow-parts/all-hosts.html": [
+   "a92019bba916ed3242dcdf66184d73b915f2689d",
+   "testharness"
+  ],
+  "css/css-shadow-parts/chaining-invalid-selector.html": [
+   "f578d6766e7e20c31c677b6dfe1aea201f24ef65",
+   "testharness"
+  ],
+  "css/css-shadow-parts/complex-matching.html": [
+   "abb39240874b2354afd80486da48285add6962b8",
+   "testharness"
+  ],
+  "css/css-shadow-parts/complex-non-matching.html": [
+   "776604de1368139bc460e6376372ecfcea4ccdb5",
+   "testharness"
+  ],
+  "css/css-shadow-parts/different-host.html": [
+   "040c22ffe6521d29be545e00a2b80d0e870426c3",
+   "testharness"
+  ],
+  "css/css-shadow-parts/host-stylesheet.html": [
+   "fe3affed81f2edea4e72f9e006ef81ab360f9555",
+   "testharness"
+  ],
+  "css/css-shadow-parts/inner-host.html": [
+   "4330af8042042c73a6cc590c38cd32613e2b9db3",
+   "testharness"
+  ],
+  "css/css-shadow-parts/simple.html": [
+   "76bea5867f75428345b105a813b7510f0492b2d2",
+   "testharness"
+  ],
+  "css/css-shadow-parts/support/shadow-helper.js": [
+   "00de7ed5660dd4e1b37710aba7cf2664c4773749",
+   "support"
+  ],
   "css/css-shapes/OWNERS": [
    "440928017de5f0747eb891e3f8374018ef1e57d4",
    "support"
@@ -306601,7 +306970,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/background-expected.txt": [
-   "2678d8c6a0464fa6ce365f81fcbd81d5b8d3aa6f",
+   "90f4bbb49e747b7bc35067faaed5175bcf2bf8aa",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/background-image.html": [
@@ -306645,7 +307014,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/break-expected.txt": [
-   "9a8a88f3b60ba9d731e1816bb3c3a769f4357905",
+   "e686f54d53e430604f8df475e7977d083f07b8ff",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/break.html": [
@@ -306701,7 +307070,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/flex-basis-expected.txt": [
-   "b2bf34ab42aa154894eb40dc5a3e58f137459ae1",
+   "68bfdcebfc98430d988ae79be2d47f46c11690a9",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/flex-basis.html": [
@@ -306741,7 +307110,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-language-override-expected.txt": [
-   "59ffb7920ddeeb2b8595b5fea5dd320085c12b0e",
+   "edc0dcd11fb4e8b1db684349b4af5ff805777dfa",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-language-override.html": [
@@ -306749,7 +307118,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-optical-sizing-expected.txt": [
-   "678282ff775bfaf48f96b73925ba9fc3e938493c",
+   "a5ff71f72a9eca63412230e8279f1f136ecea892",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-optical-sizing.html": [
@@ -306757,7 +307126,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-palette-expected.txt": [
-   "a12c3242888ed3152ce24f82a7678d7d214e2602",
+   "6689f333ca7d25c1ef60946b1e1dc5ab1af10e33",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-palette.html": [
@@ -306765,7 +307134,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-presentation-expected.txt": [
-   "8c6dc676d593cfb5d1b86d70b7eb1d4bf92a1e18",
+   "84e5dd1e1d7089000ae0ed631abb46a35f973704",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-presentation.html": [
@@ -306777,7 +307146,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-size-expected.txt": [
-   "7199fdfa6f0559e3bb92c3cdb5a93e31d6f31b23",
+   "48f05066f666fba865c27573ecca86e50c9f9ed6",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-size.html": [
@@ -306785,7 +307154,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-stretch-expected.txt": [
-   "ce411f253e6c8b2872d5a20e74d417ffbd9f5587",
+   "ebedb322ba599aec29cbe111cdc79e8a905654db",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-stretch.html": [
@@ -306793,7 +307162,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-style-expected.txt": [
-   "f51e7f0bff63be8c8eee471953b935ca4eab2e6f",
+   "914bafe45bc60bc6b5c8149d4f5c52e548dfb8d5",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-style.html": [
@@ -306801,7 +307170,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-synthesis-expected.txt": [
-   "a593ae0e8a3f8e0df2303b61b59c0bbd9194ae85",
+   "a0abddd2484fdecea5eb6a9f23e561bf79d4e731",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-synthesis.html": [
@@ -306809,7 +307178,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-variant-alternates-expected.txt": [
-   "dba98f248c2860fba2db9eb4966cd65767887e5d",
+   "507d4e36d6a1532f9e3882081ac2a0dc815cee29",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-variant-alternates.html": [
@@ -306817,7 +307186,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-variant-emoji-expected.txt": [
-   "5d0bf66e0005650cdf47deb462aa73d9dbed9c29",
+   "cda2f64b09d57b58d6433791ac4ba59e37e2a14b",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-variant-emoji.html": [
@@ -306833,7 +307202,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-weight-expected.txt": [
-   "48ca2e6c7e9fd594328b7490ad18a5a65c8c1902",
+   "00597b616a928087bb62547aff0b107619d1cf63",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-weight.html": [
@@ -306848,12 +307217,60 @@
    "e113dfd152548c6ba612d40af13ba5877673b043",
    "testharness"
   ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-area.html": [
+   "3f139dfec90a4ed0a460530fe5736475b6c25540",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-auto-columns-rows.html": [
+   "ff3e9e2b1186aed0e9ecf17cebe40316880d447d",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-auto-flow.html": [
+   "25af714f7993ad00a36cf9a96cc0218aaed9f53f",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-expected.txt": [
+   "677d787bc4e2efd09f17907709416449de0b2785",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-gap-expected.txt": [
+   "b1c12d725c1d9869754caadc174759fb6fa22472",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-gap.html": [
+   "1fbe50656f4d598f3baed2116c51b47fe3187b92",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-start-end.html": [
+   "12c4d77ab5df2b6e9f673a9f83ffcb583ccb9fe1",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-template-areas.html": [
+   "5bcd38d13e904ca4e74aef5701f10f325c1f702f",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-template-columns-rows.html": [
+   "1f6e412eb1a862b8c3bdd2d4fa30e6495ebfdbb0",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-template-expected.txt": [
+   "b4b075932313f2c7d12f871536470750b1e75b08",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid-template.html": [
+   "f1bb1330c555ffdedcd0b53c879d18f0b4ddedea",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/grid.html": [
+   "13ec601464ba9935b2167493c77d6189737f5380",
+   "testharness"
+  ],
   "css/css-typed-om/the-stylepropertymap/properties/height.html": [
    "617ec941ab1cbd02b31b8a9bb7ce6da311109476",
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/image-rendering-expected.txt": [
-   "2b10e5f9dfdc1cc499d9bea6a7e4c3cb966bbdf8",
+   "7522b7cef80d8eeac97ceefc4585fc6326fc2c34",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/image-rendering.html": [
@@ -306873,7 +307290,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/line-height-expected.txt": [
-   "022684735db5c3aaee60fc3c340864d583b120ba",
+   "43fb859a614ed3efff4052b365eee41df69724a0",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/line-height.html": [
@@ -306889,7 +307306,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/margin-expected.txt": [
-   "418a278215425057cd7b158dd2f0d143a8615628",
+   "adb3b909a22bec9017f599ac4e3c7cbb44c52ab9",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/margin.html": [
@@ -306901,7 +307318,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/mask-image-expected.txt": [
-   "0cc719048e458d2454d85d9ce9a6e197148ca07b",
+   "36d4768ce5ba6c03284ea59ccc9def2a433d8bfb",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/mask-image.html": [
@@ -306953,7 +307370,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/outline-style-expected.txt": [
-   "981154baad2963b7ab6e06e1950e1d3511878532",
+   "ac303bed8b63d8a7f0cff2cfcc9b3c4962ac1a79",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/outline-style.html": [
@@ -306965,7 +307382,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/overflow-expected.txt": [
-   "b3092b55eea27cd358380db1127a688bf7c40724",
+   "51cf73e8e62c5e8f009764822f9a1f21bfac061c",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/overflow.html": [
@@ -306993,7 +307410,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/resources/testsuite.js": [
-   "a11458935f17ace154a68314677a4a868ba55273",
+   "9e7a4bb8fa2d8ad491353117b409a2a7ff314d7f",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/right.html": [
@@ -307009,7 +307426,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/shape-outside-expected.txt": [
-   "db3780a372263495200ba69d978c60d43ccb2aa1",
+   "ea8f992a222281db2a864895df647591f76dca67",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/shape-outside.html": [
@@ -307029,7 +307446,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-combine-upright-expected.txt": [
-   "cdabdc0dd6cfb006a7f97d88f5adf24a3a67b181",
+   "19aee5b2ffaa769d11fae78c9de4c41688f047e9",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-combine-upright.html": [
@@ -307041,7 +307458,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-decoration-line-expected.txt": [
-   "ab2faf1d519b3fad46aa6eb11a623d54c9ee9191",
+   "7c3181683af1d002346575143a8fe606f01e9844",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-decoration-line.html": [
@@ -307049,7 +307466,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-decoration-skip-expected.txt": [
-   "b77aafbe21874ecb5e5bfe1c2c60b410e9d37afe",
+   "3643827beefeefea86abaf5508bddefc9ce17eef",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-decoration-skip-ink.html": [
@@ -307065,7 +307482,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-decoration-width-expected.txt": [
-   "72c8831b783d79b5fef43f3221461bbac76cf097",
+   "04b1d2dff135aaeae50e46f502b9fcf7b71e63b6",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-decoration-width.html": [
@@ -307077,7 +307494,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-emphasis-color-expected.txt": [
-   "1c109f27ed4ef58da4ba6a95232e70988704b712",
+   "4f81b23b27e8a2f5c33845f177e8ae10274e4dac",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-emphasis-color.html": [
@@ -307089,7 +307506,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-overflow-expected.txt": [
-   "ecd1db7a12ee95c8ca1a1e5a1aa960af8204036c",
+   "7561fd04d53b20a6d5e1a85dd89e3a982aab01c0",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-overflow.html": [
@@ -307101,7 +307518,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-transform-expected.txt": [
-   "3999d23023fb88ad5e47879574f7b3bd92d91528",
+   "66ec6be5f3c050ea2093537e25d35b141a623cec",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/text-transform.html": [
@@ -307153,7 +307570,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/user-select-expected.txt": [
-   "b626905dec44c166f27bf2b6546d49a0b246b7ba",
+   "01766d2927fe93aa54a9f67ef427f1dec427554a",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/user-select.html": [
@@ -307177,7 +307594,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/writing-mode-expected.txt": [
-   "44d90fb5d8c6dcbf82cfe7b99baf554fb6195ba8",
+   "beee8ef4c10d15f816b1cfa233a0980de09d6d78",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/writing-mode.html": [
@@ -316264,6 +316681,14 @@
    "8dfaa313b4abad30281d07ce22ac06a61754cc06",
    "testharness"
   ],
+  "css/cssom-view/cssom-getBoxQuads-001-expected.txt": [
+   "d4ba483519cb637ec729a96ba7180744c2f21a96",
+   "support"
+  ],
+  "css/cssom-view/cssom-getBoxQuads-001.html": [
+   "6236946f2eb29b2fdb3e7b3c1152ef275d921759",
+   "testharness"
+  ],
   "css/cssom-view/cssom-getClientRects-002-expected.txt": [
    "9a34de1b85869d6354083854633b2b2c800dd784",
    "support"
@@ -320044,6 +320469,14 @@
    "4eff7ada840bc140d4ba2a115169f19855e0acec",
    "reftest"
   ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002-ref.html": [
+   "5e341aaf001f17249f7307552d0bc639021a4a25",
+   "support"
+  ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002.html": [
+   "5ba69f0e2b5d2308c553a5a6e1c6a6eeb19528a4",
+   "reftest"
+  ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001-ref.xhtml": [
    "812712adfc932b89afaae066b464562f4bddf7a9",
    "support"
@@ -320588,6 +321021,30 @@
    "95154acaff08d7d3f999afe1ea1979017fdcc115",
    "reftest"
   ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001-ref.html": [
+   "89fd3533c50deabebc3742ee839e7ac820da2a0e",
+   "support"
+  ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html": [
+   "777b5ec16a9e01bcb24aaba36aa4f427ed76b666",
+   "reftest"
+  ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html": [
+   "0c94fe7294a1b2f9e629f86281cdb479776516bb",
+   "reftest"
+  ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002-ref.html": [
+   "79b75f4761f6433fd02b9595afd652e22e2887c2",
+   "support"
+  ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html": [
+   "2516ae40d90a58510a89caabce2ce80cd1eccea3",
+   "reftest"
+  ],
+  "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html": [
+   "6dc70e9530748c19bcff41bdaaee961adf327e5d",
+   "reftest"
+  ],
   "css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-flow-001-ref.html": [
    "b1e21a6a3e12afbf1602aec0888f6f9c7ba9e97d",
    "support"
@@ -322356,8 +322813,12 @@
    "da90b2a1c13cf18fd5cade85dcae2dadef6243c9",
    "testharness"
   ],
+  "custom-elements/HTMLElement-constructor-expected.txt": [
+   "4d971d82102ff68de3140a7e766ef0c62c678b68",
+   "support"
+  ],
   "custom-elements/HTMLElement-constructor.html": [
-   "64522527ef425b90c704b20b000c8feef0d1ca25",
+   "4dc04a8b026538bddee52586f2df50206abc9334",
    "testharness"
   ],
   "custom-elements/OWNERS": [
@@ -322416,8 +322877,12 @@
    "991ebcff1d8b59a7a4d959f3061a17e0e8a83704",
    "testharness"
   ],
+  "custom-elements/htmlconstructor/newtarget-expected.txt": [
+   "db2562ea508f54ad20b7d41b908200a807dbb155",
+   "support"
+  ],
   "custom-elements/htmlconstructor/newtarget.html": [
-   "11b7927c9a2946c752f56e5b44cfb4051ab8b6d6",
+   "39a7331d37056d0203fd771d797fd8da3d2bbefb",
    "testharness"
   ],
   "custom-elements/microtasks-and-constructors.html": [
@@ -327525,7 +327990,7 @@
    "support"
   ],
   "fetch/api/abort/general.any.js": [
-   "a0b775a19b3046336c45289f5a87ea57e476f084",
+   "e7a735978af073066872021580ececcb7f465512",
    "testharness"
   ],
   "fetch/api/abort/general.any.worker-expected.txt": [
@@ -333536,6 +334001,14 @@
    "a2a5acc9dfe53c7482eeaa4be3a4819238f8e120",
    "testharness"
   ],
+  "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt": [
+   "8d06cea05d408d70c59b1dbc5df3bda374d869a4",
+   "support"
+  ],
+  "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js": [
+   "1f440ff93300a0ab715982feb067dd3162c8fce9",
+   "testharness"
+  ],
   "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js": [
    "0f0020e1d7d8050892ef146d687178cfe8eedcd2",
    "testharness"
@@ -340044,6 +340517,10 @@
    "628527090c78d90d831cb80ea8eee7ffa66722f5",
    "testharness"
   ],
+  "html/semantics/document-metadata/the-link-element/link-rel-attribute.html": [
+   "45824076fb8696f775582c7bcfab093ebfb3055c",
+   "testharness"
+  ],
   "html/semantics/document-metadata/the-link-element/link-rellist.html": [
    "e18655ffb90bb039c50b262d145d63ca5efc22ca",
    "testharness"
@@ -340072,6 +340549,10 @@
    "1c5d8fe0b94b033b8e6ee7bcf54bd35ba2febd36",
    "support"
   ],
+  "html/semantics/document-metadata/the-link-element/resources/link-rel-attribute.css": [
+   "7439a846bece4561d9b17494aaae4c7b2aef6a2f",
+   "support"
+  ],
   "html/semantics/document-metadata/the-link-element/resources/stylesheet.css": [
    "2829167c82bafef6cfea06071007a231aa4277f6",
    "support"
@@ -349552,6 +350033,10 @@
    "afb1a08faa639bdd1ee4387069d76803c5e38d54",
    "testharness"
   ],
+  "infrastructure/testdriver/send_keys-expected.txt": [
+   "44a260387704a7a9943182a7561a46d222bae650",
+   "support"
+  ],
   "infrastructure/testdriver/send_keys.html": [
    "f2ede647416d150d7cd3720719f386b9f4d5fb6c",
    "testharness"
@@ -350997,7 +351482,7 @@
    "testharness"
   ],
   "mediasession/setactionhandler.html": [
-   "70a1f9e81faa3dbbe320a71a008e4594a29878a8",
+   "ab3482a22dd713ce7c71ce4b5efeb3cb201e76e6",
    "testharness"
   ],
   "mimesniff/README.md": [
@@ -351009,7 +351494,7 @@
    "support"
   ],
   "mimesniff/mime-types/charset-parameter.window-expected.txt": [
-   "f60ed8f628e6c2688572b2a30fc7d4fff13f63ac",
+   "307de11484a2c6ca3f0ef757057e3ade7576c01f",
    "support"
   ],
   "mimesniff/mime-types/charset-parameter.window.js": [
@@ -351017,7 +351502,7 @@
    "testharness"
   ],
   "mimesniff/mime-types/parsing.any-expected.txt": [
-   "102170f15a37599e9e4346371fe6c6a6626540ea",
+   "ce5e71c028a601fecbdc3adeb69925266b651287",
    "support"
   ],
   "mimesniff/mime-types/parsing.any.js": [
@@ -351025,7 +351510,7 @@
    "testharness"
   ],
   "mimesniff/mime-types/parsing.any.worker-expected.txt": [
-   "102170f15a37599e9e4346371fe6c6a6626540ea",
+   "ce5e71c028a601fecbdc3adeb69925266b651287",
    "support"
   ],
   "mimesniff/mime-types/resources/generated-mime-types.json": [
@@ -351041,7 +351526,7 @@
    "support"
   ],
   "mimesniff/mime-types/resources/mime-types.json": [
-   "e036eb74e27d8f35c411bb4d29d0b0621778e882",
+   "9271134541b22f4af44b6d179606c7a6cb8f3227",
    "support"
   ],
   "mixed-content/OWNERS": [
@@ -359696,6 +360181,26 @@
    "11d5baa9206313270be6289205b002b623af85db",
    "testharness"
   ],
+  "pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual.html": [
+   "4743453e64f6d2006f7e4034a5e2e6729b2f3ff8",
+   "manual"
+  ],
+  "pointerevents/extension/pointerevent_touch-action-pan-left-css_touch-manual.html": [
+   "07c68a3e30fed012a6eb5a33e9548e516e4ea027",
+   "manual"
+  ],
+  "pointerevents/extension/pointerevent_touch-action-pan-right-css_touch-manual.html": [
+   "dd4ad4a8060ae08d3fdd013b299e5cc8ca0906bc",
+   "manual"
+  ],
+  "pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual.html": [
+   "5470d896c20335be39b3276d4dc6a49b00923e25",
+   "manual"
+  ],
+  "pointerevents/extension/pointerevent_touch-action-verification.html": [
+   "f3188cc408b651f05ad946768c8233d0f7586b38",
+   "testharness"
+  ],
   "pointerevents/html/pointerevent_drag_interaction-manual.html": [
    "1eb570dbed48373f9e840b7694b774448b689879",
    "manual"
@@ -359916,22 +360421,6 @@
    "d60ccd5a174204dd5baf8ad1db690b1b67b969fe",
    "manual"
   ],
-  "pointerevents/pointerevent_touch-action-pan-down-css_touch-manual.html": [
-   "e400167599193b73aca776bb49fa372094b560ec",
-   "manual"
-  ],
-  "pointerevents/pointerevent_touch-action-pan-left-css_touch-manual.html": [
-   "e940ad98c2e63c82ec2977e0bbc87364d24aaf88",
-   "manual"
-  ],
-  "pointerevents/pointerevent_touch-action-pan-right-css_touch-manual.html": [
-   "48ed6d4e7966fad84479eaf5990136154e31242c",
-   "manual"
-  ],
-  "pointerevents/pointerevent_touch-action-pan-up-css_touch-manual.html": [
-   "9f63a5efe3fe642ce6787fdb1bf9d68184d7ce96",
-   "manual"
-  ],
   "pointerevents/pointerevent_touch-action-pan-x-css_touch-manual.html": [
    "e3adda44b02879ead5681d0e1bb8fc6bc47e4f39",
    "manual"
@@ -359965,7 +360454,7 @@
    "manual"
   ],
   "pointerevents/pointerevent_touch-action-verification.html": [
-   "c2c952d562f601f2104014f6236368a7f6b84801",
+   "66e0cde3be274124cc2a015bbce59e8e42e5de69",
    "testharness"
   ],
   "pointerevents/pointerlock/pointerevent_movementxy-manual.html": [
@@ -368761,7 +369250,7 @@
    "support"
   ],
   "server-timing/cross_origin.html": [
-   "74333db517babacbb84bb20c91ccfc8b2627b934",
+   "661cf7f329590a89aa2644a102a10d1ba34feb75",
    "testharness"
   ],
   "server-timing/resources/blue.png": [
@@ -368781,7 +369270,7 @@
    "support"
   ],
   "server-timing/resources/parsing/0.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/0.js.sub.headers": [
@@ -368789,7 +369278,7 @@
    "support"
   ],
   "server-timing/resources/parsing/1.js": [
-   "1a6924ba411a7f92aef048336bd117ece499f90a",
+   "db03fe36244bd2c07fc92720fd1d24825f9a4d11",
    "support"
   ],
   "server-timing/resources/parsing/1.js.sub.headers": [
@@ -368797,7 +369286,7 @@
    "support"
   ],
   "server-timing/resources/parsing/10.js": [
-   "1a6924ba411a7f92aef048336bd117ece499f90a",
+   "db03fe36244bd2c07fc92720fd1d24825f9a4d11",
    "support"
   ],
   "server-timing/resources/parsing/10.js.sub.headers": [
@@ -368805,7 +369294,7 @@
    "support"
   ],
   "server-timing/resources/parsing/11.js": [
-   "1a6924ba411a7f92aef048336bd117ece499f90a",
+   "db03fe36244bd2c07fc92720fd1d24825f9a4d11",
    "support"
   ],
   "server-timing/resources/parsing/11.js.sub.headers": [
@@ -368813,7 +369302,7 @@
    "support"
   ],
   "server-timing/resources/parsing/12.js": [
-   "1c6ca0aa58035fba39cfe8284c04468787149469",
+   "aca5ff6a4ec3da6fcd8f537e75f189ab52396576",
    "support"
   ],
   "server-timing/resources/parsing/12.js.sub.headers": [
@@ -368821,7 +369310,7 @@
    "support"
   ],
   "server-timing/resources/parsing/13.js": [
-   "266ee41ae7b440ab43e87a087d10fa2b1e193a8f",
+   "3e2173bd78dca0e60e6fee7ad9e893010195234e",
    "support"
   ],
   "server-timing/resources/parsing/13.js.sub.headers": [
@@ -368829,7 +369318,7 @@
    "support"
   ],
   "server-timing/resources/parsing/14.js": [
-   "b45df4c8581d127f4738887a009611092e06fae9",
+   "2016e06fed61d3375251e0cb832bb1141cf62a2e",
    "support"
   ],
   "server-timing/resources/parsing/14.js.sub.headers": [
@@ -368837,7 +369326,7 @@
    "support"
   ],
   "server-timing/resources/parsing/15.js": [
-   "57529824e71a124c2ff69e9153163de9f4c3433d",
+   "e3a2150ea44b2abb27b5640cd85bf299973dafcf",
    "support"
   ],
   "server-timing/resources/parsing/15.js.sub.headers": [
@@ -368845,7 +369334,7 @@
    "support"
   ],
   "server-timing/resources/parsing/16.js": [
-   "8d82b5abff0bfc1884a350836cb27c8b124129bd",
+   "010f58074c2169b7cec8fcf175bd8168182ba7a0",
    "support"
   ],
   "server-timing/resources/parsing/16.js.sub.headers": [
@@ -368853,7 +369342,7 @@
    "support"
   ],
   "server-timing/resources/parsing/17.js": [
-   "b45df4c8581d127f4738887a009611092e06fae9",
+   "2016e06fed61d3375251e0cb832bb1141cf62a2e",
    "support"
   ],
   "server-timing/resources/parsing/17.js.sub.headers": [
@@ -368861,7 +369350,7 @@
    "support"
   ],
   "server-timing/resources/parsing/18.js": [
-   "e37a27e8de7cb0d7da20de4b4738f868321f4f7d",
+   "c818182cc29f9bbefa7b3ebd403b7b1520fee25d",
    "support"
   ],
   "server-timing/resources/parsing/18.js.sub.headers": [
@@ -368869,7 +369358,7 @@
    "support"
   ],
   "server-timing/resources/parsing/19.js": [
-   "945c0786a390786f6bc648fb80ffe2da2c14c4b1",
+   "0d95ec03d3f26555f3063a0f41eb643e83a1ed0d",
    "support"
   ],
   "server-timing/resources/parsing/19.js.sub.headers": [
@@ -368877,7 +369366,7 @@
    "support"
   ],
   "server-timing/resources/parsing/2.js": [
-   "1bc5932490ce45826ee11e124924c9588b3e8c4d",
+   "b40f75f2522ae8277f5c391472fe285a9b504a02",
    "support"
   ],
   "server-timing/resources/parsing/2.js.sub.headers": [
@@ -368885,7 +369374,7 @@
    "support"
   ],
   "server-timing/resources/parsing/20.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/20.js.sub.headers": [
@@ -368893,7 +369382,7 @@
    "support"
   ],
   "server-timing/resources/parsing/21.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/21.js.sub.headers": [
@@ -368901,7 +369390,7 @@
    "support"
   ],
   "server-timing/resources/parsing/22.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/22.js.sub.headers": [
@@ -368909,7 +369398,7 @@
    "support"
   ],
   "server-timing/resources/parsing/23.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/23.js.sub.headers": [
@@ -368917,7 +369406,7 @@
    "support"
   ],
   "server-timing/resources/parsing/24.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/24.js.sub.headers": [
@@ -368925,7 +369414,7 @@
    "support"
   ],
   "server-timing/resources/parsing/25.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/25.js.sub.headers": [
@@ -368933,7 +369422,7 @@
    "support"
   ],
   "server-timing/resources/parsing/26.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/26.js.sub.headers": [
@@ -368941,7 +369430,7 @@
    "support"
   ],
   "server-timing/resources/parsing/27.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/27.js.sub.headers": [
@@ -368949,7 +369438,7 @@
    "support"
   ],
   "server-timing/resources/parsing/28.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/28.js.sub.headers": [
@@ -368957,7 +369446,7 @@
    "support"
   ],
   "server-timing/resources/parsing/29.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/29.js.sub.headers": [
@@ -368965,7 +369454,7 @@
    "support"
   ],
   "server-timing/resources/parsing/3.js": [
-   "1bc5932490ce45826ee11e124924c9588b3e8c4d",
+   "b40f75f2522ae8277f5c391472fe285a9b504a02",
    "support"
   ],
   "server-timing/resources/parsing/3.js.sub.headers": [
@@ -368973,7 +369462,7 @@
    "support"
   ],
   "server-timing/resources/parsing/30.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/30.js.sub.headers": [
@@ -368981,7 +369470,7 @@
    "support"
   ],
   "server-timing/resources/parsing/31.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/31.js.sub.headers": [
@@ -368989,7 +369478,7 @@
    "support"
   ],
   "server-timing/resources/parsing/32.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/32.js.sub.headers": [
@@ -368997,7 +369486,7 @@
    "support"
   ],
   "server-timing/resources/parsing/33.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/33.js.sub.headers": [
@@ -369005,7 +369494,7 @@
    "support"
   ],
   "server-timing/resources/parsing/34.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/34.js.sub.headers": [
@@ -369013,7 +369502,7 @@
    "support"
   ],
   "server-timing/resources/parsing/35.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/35.js.sub.headers": [
@@ -369021,7 +369510,7 @@
    "support"
   ],
   "server-timing/resources/parsing/36.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/36.js.sub.headers": [
@@ -369029,7 +369518,7 @@
    "support"
   ],
   "server-timing/resources/parsing/37.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/37.js.sub.headers": [
@@ -369037,7 +369526,7 @@
    "support"
   ],
   "server-timing/resources/parsing/38.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/38.js.sub.headers": [
@@ -369045,7 +369534,7 @@
    "support"
   ],
   "server-timing/resources/parsing/39.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/39.js.sub.headers": [
@@ -369053,7 +369542,7 @@
    "support"
   ],
   "server-timing/resources/parsing/4.js": [
-   "b45df4c8581d127f4738887a009611092e06fae9",
+   "2016e06fed61d3375251e0cb832bb1141cf62a2e",
    "support"
   ],
   "server-timing/resources/parsing/4.js.sub.headers": [
@@ -369061,7 +369550,7 @@
    "support"
   ],
   "server-timing/resources/parsing/40.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/40.js.sub.headers": [
@@ -369069,7 +369558,7 @@
    "support"
   ],
   "server-timing/resources/parsing/41.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/41.js.sub.headers": [
@@ -369077,7 +369566,7 @@
    "support"
   ],
   "server-timing/resources/parsing/42.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/42.js.sub.headers": [
@@ -369085,7 +369574,7 @@
    "support"
   ],
   "server-timing/resources/parsing/43.js": [
-   "dcf2d8477422cfa60c7eb5da68629925fcc653e2",
+   "26b6479e14f035125f95f6a979d65883e16c2755",
    "support"
   ],
   "server-timing/resources/parsing/43.js.sub.headers": [
@@ -369093,7 +369582,7 @@
    "support"
   ],
   "server-timing/resources/parsing/44.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/44.js.sub.headers": [
@@ -369101,7 +369590,7 @@
    "support"
   ],
   "server-timing/resources/parsing/45.js": [
-   "72dcb467c3d7fb4a2491a0ba68b612a4033738f3",
+   "2d92b3d068a2be9688eb5897c12c852c58fbb684",
    "support"
   ],
   "server-timing/resources/parsing/45.js.sub.headers": [
@@ -369109,7 +369598,7 @@
    "support"
   ],
   "server-timing/resources/parsing/46.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/46.js.sub.headers": [
@@ -369117,7 +369606,7 @@
    "support"
   ],
   "server-timing/resources/parsing/47.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/47.js.sub.headers": [
@@ -369125,7 +369614,7 @@
    "support"
   ],
   "server-timing/resources/parsing/48.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/48.js.sub.headers": [
@@ -369133,7 +369622,7 @@
    "support"
   ],
   "server-timing/resources/parsing/49.js": [
-   "6c2c1b45ab69ed8b1da28849accaa69a99d07340",
+   "d8de868c77f5926b438c4740c8f30e01bc6a714f",
    "support"
   ],
   "server-timing/resources/parsing/49.js.sub.headers": [
@@ -369141,7 +369630,7 @@
    "support"
   ],
   "server-timing/resources/parsing/5.js": [
-   "b45df4c8581d127f4738887a009611092e06fae9",
+   "2016e06fed61d3375251e0cb832bb1141cf62a2e",
    "support"
   ],
   "server-timing/resources/parsing/5.js.sub.headers": [
@@ -369149,7 +369638,7 @@
    "support"
   ],
   "server-timing/resources/parsing/50.js": [
-   "71068640ee55f176c283ce788237e26059c043c5",
+   "020df20a8afdf2d67ff37f017e400a90247f510e",
    "support"
   ],
   "server-timing/resources/parsing/50.js.sub.headers": [
@@ -369157,7 +369646,7 @@
    "support"
   ],
   "server-timing/resources/parsing/51.js": [
-   "1c6ca0aa58035fba39cfe8284c04468787149469",
+   "aca5ff6a4ec3da6fcd8f537e75f189ab52396576",
    "support"
   ],
   "server-timing/resources/parsing/51.js.sub.headers": [
@@ -369165,7 +369654,7 @@
    "support"
   ],
   "server-timing/resources/parsing/52.js": [
-   "d0a406b076d345dc36d1792781bd078fb9b65ef3",
+   "84e265d2e41da883a09eacc7c0448fb55068cfe2",
    "support"
   ],
   "server-timing/resources/parsing/52.js.sub.headers": [
@@ -369173,7 +369662,7 @@
    "support"
   ],
   "server-timing/resources/parsing/53.js": [
-   "235f1ee6973489be765e8edec753b94c9f6d5773",
+   "0ab0b4d6a9b95a749e9292da467f0fb58a729012",
    "support"
   ],
   "server-timing/resources/parsing/53.js.sub.headers": [
@@ -369181,7 +369670,7 @@
    "support"
   ],
   "server-timing/resources/parsing/54.js": [
-   "235f1ee6973489be765e8edec753b94c9f6d5773",
+   "0ab0b4d6a9b95a749e9292da467f0fb58a729012",
    "support"
   ],
   "server-timing/resources/parsing/54.js.sub.headers": [
@@ -369189,7 +369678,7 @@
    "support"
   ],
   "server-timing/resources/parsing/55.js": [
-   "2209fc9eff24dc4f0d7df2f31e5911dd4e05d78d",
+   "ad58eaac1a11e8b6daa4a6f29fa33e85d6a1003e",
    "support"
   ],
   "server-timing/resources/parsing/55.js.sub.headers": [
@@ -369197,7 +369686,7 @@
    "support"
   ],
   "server-timing/resources/parsing/56.js": [
-   "1bc5932490ce45826ee11e124924c9588b3e8c4d",
+   "b40f75f2522ae8277f5c391472fe285a9b504a02",
    "support"
   ],
   "server-timing/resources/parsing/56.js.sub.headers": [
@@ -369205,7 +369694,7 @@
    "support"
   ],
   "server-timing/resources/parsing/57.js": [
-   "235f1ee6973489be765e8edec753b94c9f6d5773",
+   "0ab0b4d6a9b95a749e9292da467f0fb58a729012",
    "support"
   ],
   "server-timing/resources/parsing/57.js.sub.headers": [
@@ -369213,7 +369702,7 @@
    "support"
   ],
   "server-timing/resources/parsing/58.js": [
-   "3fddff4eee055bf54f23be4ddd0e277cb79d492d",
+   "c708c93be1d25301433f97ff41973115f5441990",
    "support"
   ],
   "server-timing/resources/parsing/58.js.sub.headers": [
@@ -369221,7 +369710,7 @@
    "support"
   ],
   "server-timing/resources/parsing/59.js": [
-   "63d1c9fc862e586f387e1715c47c14075b9dc39e",
+   "888df69076ddb4e5f86219fad670dbe7df290716",
    "support"
   ],
   "server-timing/resources/parsing/59.js.sub.headers": [
@@ -369229,7 +369718,7 @@
    "support"
   ],
   "server-timing/resources/parsing/6.js": [
-   "1c6ca0aa58035fba39cfe8284c04468787149469",
+   "aca5ff6a4ec3da6fcd8f537e75f189ab52396576",
    "support"
   ],
   "server-timing/resources/parsing/6.js.sub.headers": [
@@ -369237,7 +369726,7 @@
    "support"
   ],
   "server-timing/resources/parsing/60.js": [
-   "63d1c9fc862e586f387e1715c47c14075b9dc39e",
+   "888df69076ddb4e5f86219fad670dbe7df290716",
    "support"
   ],
   "server-timing/resources/parsing/60.js.sub.headers": [
@@ -369245,7 +369734,7 @@
    "support"
   ],
   "server-timing/resources/parsing/61.js": [
-   "3560177cc96b27c6ca4dfd3f4755685f067a9c77",
+   "f81cfcef6ff2f5166110945a56050d1a6749cfa8",
    "support"
   ],
   "server-timing/resources/parsing/61.js.sub.headers": [
@@ -369253,7 +369742,7 @@
    "support"
   ],
   "server-timing/resources/parsing/62.js": [
-   "3560177cc96b27c6ca4dfd3f4755685f067a9c77",
+   "f81cfcef6ff2f5166110945a56050d1a6749cfa8",
    "support"
   ],
   "server-timing/resources/parsing/62.js.sub.headers": [
@@ -369261,7 +369750,7 @@
    "support"
   ],
   "server-timing/resources/parsing/63.js": [
-   "5c07fca48386d2d35c248c2053a7cb426ade6b7b",
+   "71ee194e6955b8b9b4ee4069f4a14df89b2cf1bc",
    "support"
   ],
   "server-timing/resources/parsing/63.js.sub.headers": [
@@ -369269,7 +369758,7 @@
    "support"
   ],
   "server-timing/resources/parsing/64.js": [
-   "df2fb9e4e7999d2bda0c6e435b541fa37a55c788",
+   "f617f0f8f7cf9040e6409b0ce74b9d0574674a1f",
    "support"
   ],
   "server-timing/resources/parsing/64.js.sub.headers": [
@@ -369277,7 +369766,7 @@
    "support"
   ],
   "server-timing/resources/parsing/65.js": [
-   "5c07fca48386d2d35c248c2053a7cb426ade6b7b",
+   "71ee194e6955b8b9b4ee4069f4a14df89b2cf1bc",
    "support"
   ],
   "server-timing/resources/parsing/65.js.sub.headers": [
@@ -369285,7 +369774,7 @@
    "support"
   ],
   "server-timing/resources/parsing/66.js": [
-   "df2fb9e4e7999d2bda0c6e435b541fa37a55c788",
+   "f617f0f8f7cf9040e6409b0ce74b9d0574674a1f",
    "support"
   ],
   "server-timing/resources/parsing/66.js.sub.headers": [
@@ -369293,7 +369782,7 @@
    "support"
   ],
   "server-timing/resources/parsing/67.js": [
-   "1a6924ba411a7f92aef048336bd117ece499f90a",
+   "db03fe36244bd2c07fc92720fd1d24825f9a4d11",
    "support"
   ],
   "server-timing/resources/parsing/67.js.sub.headers": [
@@ -369301,7 +369790,7 @@
    "support"
   ],
   "server-timing/resources/parsing/68.js": [
-   "b5b95d8349cbbc3b92edb61891608b04d004527b",
+   "6101bd375a67052cb449c83494dc32691b6e6146",
    "support"
   ],
   "server-timing/resources/parsing/68.js.sub.headers": [
@@ -369309,7 +369798,7 @@
    "support"
   ],
   "server-timing/resources/parsing/69.js": [
-   "235f1ee6973489be765e8edec753b94c9f6d5773",
+   "0ab0b4d6a9b95a749e9292da467f0fb58a729012",
    "support"
   ],
   "server-timing/resources/parsing/69.js.sub.headers": [
@@ -369317,7 +369806,7 @@
    "support"
   ],
   "server-timing/resources/parsing/7.js": [
-   "266ee41ae7b440ab43e87a087d10fa2b1e193a8f",
+   "3e2173bd78dca0e60e6fee7ad9e893010195234e",
    "support"
   ],
   "server-timing/resources/parsing/7.js.sub.headers": [
@@ -369325,7 +369814,7 @@
    "support"
   ],
   "server-timing/resources/parsing/70.js": [
-   "1a6924ba411a7f92aef048336bd117ece499f90a",
+   "db03fe36244bd2c07fc92720fd1d24825f9a4d11",
    "support"
   ],
   "server-timing/resources/parsing/70.js.sub.headers": [
@@ -369333,7 +369822,7 @@
    "support"
   ],
   "server-timing/resources/parsing/71.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/71.js.sub.headers": [
@@ -369341,7 +369830,7 @@
    "support"
   ],
   "server-timing/resources/parsing/72.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/72.js.sub.headers": [
@@ -369349,7 +369838,7 @@
    "support"
   ],
   "server-timing/resources/parsing/73.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/73.js.sub.headers": [
@@ -369357,7 +369846,7 @@
    "support"
   ],
   "server-timing/resources/parsing/74.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/74.js.sub.headers": [
@@ -369365,7 +369854,7 @@
    "support"
   ],
   "server-timing/resources/parsing/75.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/75.js.sub.headers": [
@@ -369373,7 +369862,7 @@
    "support"
   ],
   "server-timing/resources/parsing/76.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/76.js.sub.headers": [
@@ -369381,7 +369870,7 @@
    "support"
   ],
   "server-timing/resources/parsing/77.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/77.js.sub.headers": [
@@ -369389,7 +369878,7 @@
    "support"
   ],
   "server-timing/resources/parsing/78.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/78.js.sub.headers": [
@@ -369397,7 +369886,7 @@
    "support"
   ],
   "server-timing/resources/parsing/79.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/79.js.sub.headers": [
@@ -369405,7 +369894,7 @@
    "support"
   ],
   "server-timing/resources/parsing/8.js": [
-   "e30f08cfab17bcd0229ebbf0da701f65a193485b",
+   "e049716bbac11f558b9fc61af72941a702f8cfb6",
    "support"
   ],
   "server-timing/resources/parsing/8.js.sub.headers": [
@@ -369413,7 +369902,7 @@
    "support"
   ],
   "server-timing/resources/parsing/80.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/80.js.sub.headers": [
@@ -369421,7 +369910,7 @@
    "support"
   ],
   "server-timing/resources/parsing/81.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/81.js.sub.headers": [
@@ -369429,7 +369918,7 @@
    "support"
   ],
   "server-timing/resources/parsing/82.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/82.js.sub.headers": [
@@ -369437,7 +369926,7 @@
    "support"
   ],
   "server-timing/resources/parsing/83.js": [
-   "4b374f4e2751e3c75af68df671c0287e41d617ae",
+   "5d6b847c554a9c38b97f1a540db9375b47b6208f",
    "support"
   ],
   "server-timing/resources/parsing/83.js.sub.headers": [
@@ -369445,7 +369934,7 @@
    "support"
   ],
   "server-timing/resources/parsing/9.js": [
-   "30695307f7a50b46a799f3cf6f5763a359acc0ce",
+   "5660bb21eb42f322a58f80c6c001d14c6e0fc0c9",
    "support"
   ],
   "server-timing/resources/parsing/9.js.sub.headers": [
@@ -369453,7 +369942,7 @@
    "support"
   ],
   "server-timing/server_timing_header-parsing.html": [
-   "15768941cac8f5adeed09e6d204715d5fa59e5aa",
+   "bde9c39ce610bd7178800f92f3181bef3cd55918",
    "testharness"
   ],
   "server-timing/test_server_timing.html": [
@@ -375164,12 +375653,8 @@
    "4713dfec8b4277cc29520dbd25958bd74e440c19",
    "testharness"
   ],
-  "url/urlsearchparams-foreach-expected.txt": [
-   "fce6b61e524374ce234726c4e21063b99b646757",
-   "support"
-  ],
   "url/urlsearchparams-foreach.html": [
-   "5bdc17d623884a8916681f19df55c4bef5965ff2",
+   "604154351de9db97183b3cfc149d3ca6c5132723",
    "testharness"
   ],
   "url/urlsearchparams-get.html": [
@@ -375860,12 +376345,8 @@
    "d9fc177ebbc3fa0317125912e38a4bfd65f727c8",
    "testharness"
   ],
-  "web-animations/interfaces/Document/getAnimations-expected.txt": [
-   "a61dc959961b5e6c389e2bb04c1930f04c4ff66a",
-   "support"
-  ],
   "web-animations/interfaces/Document/getAnimations.html": [
-   "7fbd5eed47955fdaeccd329f82f0884b86654784",
+   "41edbdf1f03889156068f38d87875387f129924f",
    "testharness"
   ],
   "web-animations/interfaces/Document/timeline.html": [
@@ -377693,7 +378174,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-getStats.https-expected.txt": [
-   "47ec6999d09c69c52a69e71104ea7bbc6f4cea36",
+   "dde1a0f72c91a106ed9b6bc05da5a287a3e2fb86",
    "support"
   ],
   "webrtc/RTCPeerConnection-getStats.https.html": [
@@ -377873,11 +378354,11 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-track-stats.https-expected.txt": [
-   "0125406c581e8a3a0f309575374ff6e26a83d947",
+   "e0a6ac0951476e880e81895409db5c97a97063ec",
    "support"
   ],
   "webrtc/RTCPeerConnection-track-stats.https.html": [
-   "55ab0ca3d364b020320fa45a14e50d2897dda13a",
+   "512e40d1b6f95c86b90ba7b536b6ae049f6a04c0",
    "testharness"
   ],
   "webrtc/RTCPeerConnectionIceEvent-constructor-expected.txt": [
@@ -383056,6 +383537,10 @@
    "12d12036aa5937ae79aa152468cc574ac4bf4e94",
    "testharness"
   ],
+  "workers/interfaces/WorkerUtils/WindowTimers/005.html": [
+   "11f0d7b6ea4a3c883d6c5cc8bde52c8ae9f7091b",
+   "testharness"
+  ],
   "workers/interfaces/WorkerUtils/importScripts/001.worker.js": [
    "138e3be5d6375c8a784faabb4f2f4c82c2423bc3",
    "testharness"
@@ -384296,6 +384781,14 @@
    "9a27f71e6e5738d2625ed30f91f3d514fc3646e8",
    "testharness"
   ],
+  "xhr/header-user-agent-async.htm": [
+   "07833728cf33836e854681d8e53c2b5357f8f810",
+   "testharness"
+  ],
+  "xhr/header-user-agent-sync.htm": [
+   "e7f02cf698647785d332e4ad517ff0df357eea70",
+   "testharness"
+  ],
   "xhr/headers-normalize-response-expected.txt": [
    "beeb26685c597767e2b460f6b62ea3f0a9966554",
    "support"
@@ -384732,6 +385225,10 @@
    "a191134704e09ff9bb6591dc8c6aa78307edf6c9",
    "support"
   ],
+  "xhr/resources/header-user-agent.py": [
+   "26af60bcffd2c78dd7ee89a98a02fde12aee41a9",
+   "support"
+  ],
   "xhr/resources/headers-basic.asis": [
    "718e90fc73ec596127d26fba5433c5355e93fa3f",
    "support"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/FileAPI/url/sandboxed-iframe-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/FileAPI/url/sandboxed-iframe-expected.txt
index b40122b..41e6cc0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/FileAPI/url/sandboxed-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/FileAPI/url/sandboxed-iframe-expected.txt
@@ -6,7 +6,7 @@
 PASS Blob URL parses correctly
 PASS Origin of Blob URL matches our origin for Files
 PASS Blob URLs can be used in XHR
-FAIL XHR with a fragment should succeed promise_test: Unhandled rejection with value: "Got unexpected error event"
+PASS XHR with a fragment should succeed
 PASS XHR of a revoked URL should fail
 PASS Only exact matches should revoke URLs, using XHR
 PASS Appending a query string should cause XHR to fail
@@ -20,7 +20,7 @@
 PASS XHR should return Content-Type from Blob
 FAIL Revoke blob URL after open(), will fetch assert_unreached: Got unexpected error event Reached unreachable code
 PASS Blob URLs can be used in fetch
-FAIL fetch with a fragment should succeed promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
+PASS fetch with a fragment should succeed
 PASS fetch of a revoked URL should fail
 PASS Only exact matches should revoke URLs, using fetch
 PASS Appending a query string should cause fetch to fail
diff --git a/third_party/WebKit/LayoutTests/external/wpt/bluetooth/idl/idl-Bluetooth.html b/third_party/WebKit/LayoutTests/external/wpt/bluetooth/idl/idl-Bluetooth.https.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/bluetooth/idl/idl-Bluetooth.html
rename to third_party/WebKit/LayoutTests/external/wpt/bluetooth/idl/idl-Bluetooth.https.html
diff --git a/third_party/WebKit/LayoutTests/external/wpt/bluetooth/resources/bluetooth-helpers.js b/third_party/WebKit/LayoutTests/external/wpt/bluetooth/resources/bluetooth-helpers.js
index f3dc8b7..725e56d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/bluetooth/resources/bluetooth-helpers.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/bluetooth/resources/bluetooth-helpers.js
@@ -469,6 +469,15 @@
   }];
 }
 
+// Causes |fake_peripheral| to disconnect and returns a promise that resolves
+// once `gattserverdisconnected` has been fired on |device|.
+function simulateGATTDisconnectionAndWait(device, fake_peripheral) {
+  return Promise.all([
+    eventPromise(device, 'gattserverdisconnected'),
+    fake_peripheral.simulateGATTDisconnection(),
+  ]);
+}
+
 // Simulates a pre-connected device with |address|, |name| and
 // |knownServiceUUIDs|.
 function setUpPreconnectedDevice({
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-allowed.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-allowed.html
index 3848c2f..6c05478 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-allowed.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-allowed.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -15,4 +16,4 @@
 
 <iframe srcdoc="<iframe src='support/navigate_parent.sub.html?csp=navigate-to%20%27self%27'>">
 
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-blocked.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-blocked.html
index a5bea3ff..b665b7a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-blocked.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/child-navigates-parent-blocked.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -16,4 +17,4 @@
 
 <iframe srcdoc="<iframe src='support/navigate_parent.sub.html?csp=navigate-to%20%27none%27'>">
 
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-allowed.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-allowed.html
index 977b85d..3f7f678 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-allowed.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-allowed.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -13,4 +14,4 @@
   });
 </script>
 <iframe src="support/link_click_navigation.sub.html?csp=navigate-to%20%27self%27&target=post_message_to_frame_owner.html">
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-blocked.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-blocked.html
index 6817da2..65de768 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-blocked.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-blocked.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -14,4 +15,4 @@
   });
 </script>
 <iframe src="support/link_click_navigation.sub.html?csp=navigate-to%20%27none%27&target=post_message_to_frame_owner.html">
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-allowed.sub.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-allowed.sub.html
index 4381bcb..360ede37 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-allowed.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-allowed.sub.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -13,4 +14,4 @@
   });
 </script>
 <iframe src="support/link_click_navigation.sub.html?csp=navigate-to%20http%3A%2F%2F{{domains[www1]}}:{{ports[http][0]}}&target=http%3A%2F%2F{{domains[www1]}}:{{ports[http][0]}}%2Fcontent-security-policy%2Fnavigate-to%2Fsupport%2Fpost_message_to_frame_owner.html">
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-blocked.sub.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-blocked.sub.html
index 1041506e..50f258ba 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-blocked.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-cross-origin-blocked.sub.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -14,4 +15,4 @@
   });
 </script>
 <iframe src="support/link_click_navigation.sub.html?csp=navigate-to%20%27self%27&target=http%3A%2F%2F{{domains[www1]}}:{{ports[http][0]}}%2Fcontent-security-policy%2Fnavigate-to%2Fsupport%2Fpost_message_to_frame_owner.html">
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-allowed.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-allowed.html
index 87dea95..e75a7e5a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-allowed.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-allowed.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -13,4 +14,4 @@
   });
 </script>
 <iframe src="support/link_click_navigation.sub.html?csp=navigate-to%20%27self%27&target=redirect_to_post_message_to_frame_owner.py">
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-blocked.sub.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-blocked.sub.html
index f0bf4df..355e2c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-blocked.sub.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/navigate-to/link-click-redirected-blocked.sub.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 
 <head>
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
@@ -14,4 +15,4 @@
   });
 </script>
 <iframe src="support/link_click_navigation.sub.html?csp=navigate-to%20{{location[server]}}/content-security-policy/navigate-to/support/redirect_to_post_message_to_frame_owner.py&target=redirect_to_post_message_to_frame_owner.py">
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/prefetch-src/prefetch-allowed.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/prefetch-src/prefetch-allowed.html
index e103a9f..95177c1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/prefetch-src/prefetch-allowed.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/prefetch-src/prefetch-allowed.html
@@ -1,20 +1,27 @@
 <!DOCTYPE html>
 <html>
 <head>
-  <meta http-equiv="Content-Security-Policy" content="prefetch-src 'self'">
   <script src='/resources/testharness.js'></script>
   <script src='/resources/testharnessreport.js'></script>
   <script src='/content-security-policy/support/testharness-helper.js'></script>
   <script src='/content-security-policy/support/prefetch-helper.js'></script>
   <script>
     async_test(t => {
-      let url = window.origin + '/content-security-policy/support/pass.png';
+      var win = window.open('/content-security-policy/support/' +
+                            'file-prefetch-allowed.html');
+      win.addEventListener('load', function () {
+        // Cache control headers are added,since they are needed 
+        // to enable prefetching.
+        let url = '/content-security-policy/support/pass.png' +
+                                  '?pipe=header(Cache-Control, max-age=604800)';
 
-      let link = document.createElement('link');
-      link.rel = 'prefetch';
-      link.href = url;
-
-      assert_link_prefetches(t, link);
+        // Link element is created on the new opened window.
+        let link = win.document.createElement('link');
+        link.rel = 'prefetch';
+        link.href = url;
+        assert_link_prefetches(t, link);
+        win.close();
+      }, false);
     }, 'Prefetch succeeds when allowed by prefetch-src');
   </script>
 </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/support/file-prefetch-allowed.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/support/file-prefetch-allowed.html
new file mode 100644
index 0000000..bd60d262
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/support/file-prefetch-allowed.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <!-- CSP directive 'prefetch-src' is not supported via meta tag though -->
+  <meta http-equiv="Content-Security-Policy" content="prefetch-src 'self'">
+</head>
+<body>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-001.xht b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-001.xht
index 28ff467..ce5d4c6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-001.xht
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-001.xht
@@ -6,8 +6,8 @@
   <link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-08-05 -->
   <link rel="help" href="http://www.w3.org/TR/css3-multicol/#column-gap" title="4.1. 'column-gap'" />
   <link rel="match" href="multicol-gap-001-ref.xht" />
-  <meta name="flags" content="ahem may" />
-  <meta name="assert" content="This test checks that the 'normal' column gap is 1em, which is suggested -- and not prescribed -- by the specification." />
+  <meta name="flags" content="ahem" />
+  <meta name="assert" content="This test checks that the 'normal' column gap is 1em." />
   <style type="text/css"><![CDATA[
   div
   {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-003.xht b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-003.xht
index 180fd3c..33011b8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-003.xht
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-gap-003.xht
@@ -6,7 +6,7 @@
   <link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-08-05 -->
   <link rel="help" href="http://www.w3.org/TR/css3-multicol/#column-gap" title="4.1. 'column-gap'" />
   <link rel="match" href="multicol-gap-002-ref.xht" />
-  <meta name="flags" content="ahem may" />
+  <meta name="flags" content="ahem" />
   <style type="text/css"><![CDATA[
   div
   {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping/shadow-reassign-dynamic-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping/shadow-reassign-dynamic-001.html
new file mode 100644
index 0000000..e8fe49a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping/shadow-reassign-dynamic-001.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Scoping: Dynamic reassignment of a slot.</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors-data-model">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1435632">
+<link rel="match" href="reference/green-box.html"/>
+<div id="host">
+  <div id="green" style="background: green"></div>
+  <div id="red" style="background: red" slot="myslot"></div>
+</div>
+<script>
+  let root = host.attachShadow({ mode: "open" });
+  root.innerHTML = `
+    <style>::slotted(div) { width: 100px; height: 100px }</style>
+    <p>Test passes if you see a single 100px by 100px green box below.</p>
+    <slot name="myslot">FAIL</slot>
+  `;
+  document.body.offsetTop;
+  green.setAttribute("slot", "myslot");
+  red.removeAttribute("slot");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/cssom-getBoxQuads-001-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/cssom-getBoxQuads-001-expected.txt
new file mode 100644
index 0000000..900edfa8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/cssom-getBoxQuads-001-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL CSSOM View - getBoxQuads() returns proper border and margin boxes for block and flex bb.getBoxQuads is not a function
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/cssom-getBoxQuads-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/cssom-getBoxQuads-001.html
new file mode 100644
index 0000000..813c245
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom-view/cssom-getBoxQuads-001.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <title>CSSOM View - getBoxQuads() returns proper border and margin boxes for block and flex</title>
+  <link rel="help" href="https://drafts.csswg.org/cssom-view/#the-geometryutils-interface">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+
+  <style>
+    .container {
+      width: 100px;
+      height: 50px;
+      background-color: gray;
+    }
+    span {
+      display: block;
+      background: gold;
+      height: 4px;
+      width: 14px;
+      margin: auto;
+      padding: 0px;
+      border: 3px solid blue;
+    }
+  </style>
+ </head>
+ <body>
+  <div class="container">
+  <span id="block-block"></span>
+  </div>
+
+  <div class="container" style="display:flex">
+  <span id="flex-block"></span>
+  </div>
+
+  <script>
+    test(function() {
+      let bb = document.getElementById("block-block");
+      assert_equals(bb.getBoxQuads({box: "border"})[0].bounds.width,  20, "Block layout border box is expected width.");
+      assert_equals(bb.getBoxQuads({box: "margin"})[0].bounds.width, 100, "Block layout margin box is expected width.");
+
+      // For containers that expand items to fill block-axis space, measure the box heights also.
+      let fb = document.getElementById("flex-block");
+      assert_equals(fb.getBoxQuads({box: "border"})[0].bounds.width,  20, "Flex layout border box is expected width.");
+      assert_equals(fb.getBoxQuads({box: "margin"})[0].bounds.width, 100, "Flex layout margin box is expected width.");
+
+      assert_equals(fb.getBoxQuads({box: "border"})[0].bounds.height, 10, "Flex layout border box is expected height.");
+      assert_equals(fb.getBoxQuads({box: "margin"})[0].bounds.height, 50, "Flex layout margin box is expected height.");
+    });
+  </script>
+ </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002-ref.html
new file mode 100644
index 0000000..876e5d6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <style>
+    .tealBlock {
+      background: teal;
+      width: 10px;
+      height: 10px;
+      margin-bottom: 5px;
+    }
+    .purpleBlock {
+      background: purple;
+      width: 10px;
+      height: 10px;
+      margin-bottom: 5px;
+    }
+  </style>
+</head>
+<body>
+
+  <div class="purpleBlock"></div>
+  <div class="purpleBlock"></div>
+  <div class="purpleBlock"></div>
+  <div class="purpleBlock"></div>
+
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002.html
new file mode 100644
index 0000000..2bbb5f0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-abspos-child-002.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>
+    CSS Test: Test that "flex-basis" doesn't affect layout of abspos flex child
+  </title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#abspos-items">
+  <link rel="match" href="flexbox-abspos-child-001-ref.html">
+  <style>
+    .flex {
+      display: flex;
+      height: 10px;
+      width: 10px;
+      background: purple;
+      margin-bottom: 5px;
+      position: relative;
+    }
+    .flex > * {
+      position: absolute;
+      background: teal;
+      height: 10px;
+    }
+    .sized {
+      width: 10px;
+    }
+    .implied {
+      left: 0;
+      right: 0;
+    }
+  </style>
+</head>
+<body>
+  <!-- In all cases below, flex-basis should have no effect on layout (because
+       it's on an element that is abspos and hence not a flex item). -->
+
+  <!-- Abspos child has auto width (which should end up 0): -->
+  <div class="flex"><div style="flex-basis: 2px"></div></div>
+  <div class="flex"><div style="flex-basis: 100px"></div></div>
+  <div class="flex"><div style="flex-basis: 80%"></div></div>
+  <div class="flex"><div style="flex-basis: content"></div></div>
+
+  <!-- Abspos child has explicit 10px width: -->
+  <div class="flex"><div class="sized" style="flex-basis: 2px"></div></div>
+  <div class="flex"><div class="sized" style="flex-basis: 100px"></div></div>
+  <div class="flex"><div class="sized" style="flex-basis: 80%"></div></div>
+  <div class="flex"><div class="sized" style="flex-basis: content"></div></div>
+
+  <!-- Abspos child has implicit 10px width (implied by auto width and
+       constrained left/right properties): -->
+  <div class="flex"><div class="implied" style="flex-basis: 2px"></div></div>
+  <div class="flex"><div class="implied" style="flex-basis: 100px"></div></div>
+  <div class="flex"><div class="implied" style="flex-basis: 80%"></div></div>
+  <div class="flex"><div class="implied" style="flex-basis: content"></div></div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001-ref.html
new file mode 100644
index 0000000..b537711
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001-ref.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 50px;
+    width: 200px;
+  }
+
+  .container > * {
+    flex-shrink: 0;
+    min-width: 0;
+    border: 2px solid teal;
+  }
+
+  .smallText { font: 10px Ahem; }
+  .bigText   { font: 20px Ahem; }
+  .spacerChild::before {
+    content: '';
+    display: block;
+    background: brown;
+    height: 10px;
+    width: 10px;
+  }
+  .justPadding {
+    /* Empty div with 5px padding on each side */
+    padding: 5px;
+    background: cyan;
+  }
+  canvas { background: fuchsia }
+  </style>
+</head>
+<body>
+<!-- Flex items have unspecified size properties: -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas width="20"></canvas>
+</div>
+
+<!-- Various specified main-size values, in testcase
+     (removed here in reference case, because they shouldn't affect sizing): -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas width="20"></canvas>
+</div>
+
+<!-- Various specified cross-size values (should be honored): -->
+<div class="container">
+  <div class="smallText"    style="height: 0px">a b</div>
+  <div class="bigText"      style="height: 40px">c</div>
+  <div class="spacerChild"  style="height: 20px"></div>
+  <div class="justPadding"  style="height: 10px"></div>
+  <canvas width="20"        style="height: 8px"></canvas>
+</div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html
new file mode 100644
index 0000000..4227f68
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing "flex-basis: content" in a row-oriented flex container
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#propdef-flex-basis">
+  <link rel="match" href="flexbox-flex-basis-content-001-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 50px;
+    width: 200px;
+  }
+
+  .container > * {
+    /* All flex items have "flex-basis: content" (and zero flex-shrink and
+       min-main-size, to avoid any influence from those). */
+    flex-basis: content;
+    flex-shrink: 0;
+    min-width: 0;
+    border: 2px solid teal;
+  }
+
+  .smallText { font: 10px Ahem; }
+  .bigText   { font: 20px Ahem; }
+  .spacerChild::before {
+    content: '';
+    display: block;
+    background: brown;
+    height: 10px;
+    width: 10px;
+  }
+  .justPadding {
+    /* Empty div with 5px padding on each side */
+    padding: 5px;
+    background: cyan;
+  }
+  canvas { background: fuchsia }
+  </style>
+</head>
+<body>
+<!-- Flex items have unspecified size properties: -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas width="20"></canvas>
+</div>
+
+<!-- Various specified main-size values (should be ignored): -->
+<div class="container">
+  <div class="smallText"    style="width: 0px">a b</div>
+  <div class="bigText"      style="width: 40px">c</div>
+  <div class="spacerChild"  style="width: 20px"></div>
+  <div class="justPadding"  style="width: 10px"></div>
+  <canvas width="20"        style="width: 8px"></canvas>
+</div>
+
+<!-- Various specified cross-size values (should be honored): -->
+<div class="container">
+  <div class="smallText"    style="height: 0px">a b</div>
+  <div class="bigText"      style="height: 40px">c</div>
+  <div class="spacerChild"  style="height: 20px"></div>
+  <div class="justPadding"  style="height: 10px"></div>
+  <canvas width="20"        style="height: 8px"></canvas>
+</div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html
new file mode 100644
index 0000000..489ce65
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing "flex-basis: content" (set via the "flex" shorthand)
+    in a row-oriented flex container.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#propdef-flex-basis">
+  <link rel="match" href="flexbox-flex-basis-content-001-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-bottom: 2em;
+    height: 50px;
+    width: 200px;
+  }
+
+  .container > * {
+    /* All flex items have "flex-basis: content" (and zero flex-shrink and
+       min-main-size, to avoid any influence from those). */
+    flex: 0 0 content;
+    min-width: 0;
+    border: 2px solid teal;
+  }
+
+  .smallText { font: 10px Ahem; }
+  .bigText   { font: 20px Ahem; }
+  .spacerChild::before {
+    content: '';
+    display: block;
+    background: brown;
+    height: 10px;
+    width: 10px;
+  }
+  .justPadding {
+    /* Empty div with 5px padding on each side */
+    padding: 5px;
+    background: cyan;
+  }
+  canvas { background: fuchsia }
+  </style>
+</head>
+<body>
+<!-- Flex items have unspecified size properties: -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas width="20"></canvas>
+</div>
+
+<!-- Various specified main-size values (should be ignored): -->
+<div class="container">
+  <div class="smallText"    style="width: 0px">a b</div>
+  <div class="bigText"      style="width: 40px">c</div>
+  <div class="spacerChild"  style="width: 20px"></div>
+  <div class="justPadding"  style="width: 10px"></div>
+  <canvas width="20"        style="width: 8px"></canvas>
+</div>
+
+<!-- Various specified cross-size values (should be honored): -->
+<div class="container">
+  <div class="smallText"    style="height: 0px">a b</div>
+  <div class="bigText"      style="height: 40px">c</div>
+  <div class="spacerChild"  style="height: 20px"></div>
+  <div class="justPadding"  style="height: 10px"></div>
+  <canvas width="20"        style="height: 8px"></canvas>
+</div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002-ref.html
new file mode 100644
index 0000000..a7d1bcf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002-ref.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>CSS Reftest Reference</title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-right: 2em;
+    width: 50px;
+    height: 200px;
+    float: left;
+  }
+
+  .container > * {
+    flex-shrink: 0;
+    min-height: 0;
+    border: 2px solid teal;
+  }
+
+  .smallText { font: 10px Ahem; }
+  .bigText   { font: 20px Ahem; }
+  .spacerChild::before {
+    content: '';
+    display: block;
+    background: brown;
+    height: 10px;
+    width: 10px;
+  }
+  .justPadding {
+    /* Empty div with 5px padding on each side */
+    padding: 5px;
+    background: cyan;
+  }
+  canvas { background: fuchsia }
+  </style>
+</head>
+<body>
+<!-- Flex items have unspecified size properties: -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas height="20"></canvas>
+</div>
+
+<!-- Various specified main-size values, in testcase
+     (removed here in reference case, because they shouldn't affect sizing): -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas height="20"></canvas>
+</div>
+
+<!-- Various specified cross-size values (should be honored): -->
+<div class="container">
+  <div class="smallText"    style="width: 0px">a b</div>
+  <div class="bigText"      style="width: 40px">c</div>
+  <div class="spacerChild"  style="width: 20px"></div>
+  <div class="justPadding"  style="width: 10px"></div>
+  <canvas height="20"       style="width: 8px"></canvas>
+</div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html
new file mode 100644
index 0000000..481a3f22
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing "flex-basis: content" in a column-oriented flex container
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#propdef-flex-basis">
+  <link rel="match" href="flexbox-flex-basis-content-002-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-right: 2em;
+    width: 50px;
+    height: 200px;
+    float: left;
+  }
+
+  .container > * {
+    /* All flex items have "flex-basis: content" (and zero flex-shrink and
+       min-main-size, to avoid any influence from those). */
+    flex-basis: content;
+    flex-shrink: 0;
+    min-height: 0;
+    border: 2px solid teal;
+  }
+
+  .smallText { font: 10px Ahem; }
+  .bigText   { font: 20px Ahem; }
+  .spacerChild::before {
+    content: '';
+    display: block;
+    background: brown;
+    height: 10px;
+    width: 10px;
+  }
+  .justPadding {
+    /* Empty div with 5px padding on each side */
+    padding: 5px;
+    background: cyan;
+  }
+  canvas { background: fuchsia }
+  </style>
+</head>
+<body>
+<!-- Flex items have unspecified size properties: -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas height="20"></canvas>
+</div>
+
+<!-- Various specified main-size values (should be ignored): -->
+<div class="container">
+  <div class="smallText"    style="height: 0px">a b</div>
+  <div class="bigText"      style="height: 40px">c</div>
+  <div class="spacerChild"  style="height: 20px"></div>
+  <div class="justPadding"  style="height: 10px"></div>
+  <canvas height="20"       style="height: 8px"></canvas>
+</div>
+
+<!-- Various specified cross-size values (should be honored): -->
+<div class="container">
+  <div class="smallText"    style="width: 0px">a b</div>
+  <div class="bigText"      style="width: 40px">c</div>
+  <div class="spacerChild"  style="width: 20px"></div>
+  <div class="justPadding"  style="width: 10px"></div>
+  <canvas height="20"       style="width: 8px"></canvas>
+</div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html
new file mode 100644
index 0000000..694e672
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>
+    CSS Test: Testing "flex-basis: content" (set via the "flex" shorthand)
+    in a column-oriented flex container.
+  </title>
+  <meta charset="utf-8">
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/css-flexbox-1/#propdef-flex-basis">
+  <link rel="match" href="flexbox-flex-basis-content-002-ref.html">
+  <link rel="stylesheet" type="text/css" href="support/ahem.css">
+  <style>
+  .container {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    border: 2px solid purple;
+    padding: 2px;
+    margin-right: 2em;
+    width: 50px;
+    height: 200px;
+    float: left;
+  }
+
+  .container > * {
+    /* All flex items have "flex-basis: content" (and zero flex-shrink and
+       min-main-size, to avoid any influence from those). */
+    flex: 0 0 content;
+    min-height: 0;
+    border: 2px solid teal;
+  }
+
+  .smallText { font: 10px Ahem; }
+  .bigText   { font: 20px Ahem; }
+  .spacerChild::before {
+    content: '';
+    display: block;
+    background: brown;
+    height: 10px;
+    width: 10px;
+  }
+  .justPadding {
+    /* Empty div with 5px padding on each side */
+    padding: 5px;
+    background: cyan;
+  }
+  canvas { background: fuchsia }
+  </style>
+</head>
+<body>
+<!-- Flex items have unspecified size properties: -->
+<div class="container">
+  <div class="smallText">a b</div>
+  <div class="bigText">c</div>
+  <div class="spacerChild"></div>
+  <div class="justPadding"></div>
+  <canvas height="20"></canvas>
+</div>
+
+<!-- Various specified main-size values (should be ignored): -->
+<div class="container">
+  <div class="smallText"    style="height: 0px">a b</div>
+  <div class="bigText"      style="height: 40px">c</div>
+  <div class="spacerChild"  style="height: 20px"></div>
+  <div class="justPadding"  style="height: 10px"></div>
+  <canvas height="20"       style="height: 8px"></canvas>
+</div>
+
+<!-- Various specified cross-size values (should be honored): -->
+<div class="container">
+  <div class="smallText"    style="width: 0px">a b</div>
+  <div class="bigText"      style="width: 40px">c</div>
+  <div class="spacerChild"  style="width: 20px"></div>
+  <div class="justPadding"  style="width: 10px"></div>
+  <canvas height="20"       style="width: 8px"></canvas>
+</div>
+
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/custom-elements/HTMLElement-constructor-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/HTMLElement-constructor-expected.txt
new file mode 100644
index 0000000..ca5b35f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/HTMLElement-constructor-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS HTMLElement constructor must throw a TypeError when NewTarget is equal to itself
+PASS HTMLElement constructor must throw a TypeError when NewTarget is equal to itself via a Proxy object
+PASS HTMLElement constructor must throw TypeError when it has not been defined by customElements.define
+PASS Custom element constructor must throw TypeError when it does not extend HTMLElement
+PASS Custom element constructor must throw TypeError when it does not extend the proper element interface
+PASS HTMLElement constructor must infer the tag name from the element interface
+PASS HTMLElement constructor must allow subclassing a custom element
+PASS HTMLElement constructor must allow subclassing an user-defined subclass of HTMLElement
+FAIL HTMLElement constructor must only get .prototype once, calling proxy constructor directly assert_equals: Should have gotten .prototype once expected 1 but got 2
+FAIL HTMLElement constructor must only get .prototype once, calling proxy constructor via Reflect assert_equals: Should have gotten .prototype once expected 1 but got 2
+FAIL HTMLElement constructor must only get .prototype once, calling proxy constructor via Reflect with no inheritance assert_equals: Should have gotten .prototype once expected 1 but got 2
+FAIL HTMLElement constructor must not get .prototype until it finishes its extends sanity checks, calling proxy constructor directly assert_equals: Should never have gotten .prototype expected 0 but got 1
+FAIL HTMLElement constructor must not get .prototype until it finishes its extends sanity checks, calling via Reflect assert_equals: Should never have gotten .prototype expected 0 but got 1
+FAIL HTMLElement constructor must not get .prototype until it finishes its registration sanity checks, calling proxy constructor directly assert_equals: Should never have gotten .prototype expected 0 but got 1
+FAIL HTMLElement constructor must not get .prototype until it finishes its registration  sanity checks, calling via Reflect assert_equals: Should never have gotten .prototype expected 0 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/custom-elements/HTMLElement-constructor.html b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/HTMLElement-constructor.html
index cb6d540..a0bfa90 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/custom-elements/HTMLElement-constructor.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/HTMLElement-constructor.html
@@ -91,6 +91,159 @@
 
 }, 'HTMLElement constructor must allow subclassing an user-defined subclass of HTMLElement');
 
+test(function() {
+    class SomeCustomElement extends HTMLElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+    customElements.define("success-counting-element-1", countingProxy);
+    // define() gets the prototype of the constructor it's passed, so
+    // reset the counter.
+    getCount = 0;
+    var instance = new countingProxy();
+    assert_equals(getCount, 1, "Should have gotten .prototype once");
+    assert_true(instance instanceof countingProxy);
+    assert_true(instance instanceof HTMLElement);
+    assert_true(instance instanceof SomeCustomElement);
+    assert_equals(instance.localName, "success-counting-element-1");
+    assert_equals(instance.nodeName, "SUCCESS-COUNTING-ELEMENT-1");
+}, 'HTMLElement constructor must only get .prototype once, calling proxy constructor directly');
+
+test(function() {
+    class SomeCustomElement extends HTMLElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+    customElements.define("success-counting-element-2", countingProxy);
+    // define() gets the prototype of the constructor it's passed, so
+    // reset the counter.
+    getCount = 0;
+    var instance = Reflect.construct(HTMLElement, [], countingProxy);
+    assert_equals(getCount, 1, "Should have gotten .prototype once");
+    assert_true(instance instanceof countingProxy);
+    assert_true(instance instanceof HTMLElement);
+    assert_true(instance instanceof SomeCustomElement);
+    assert_equals(instance.localName, "success-counting-element-2");
+    assert_equals(instance.nodeName, "SUCCESS-COUNTING-ELEMENT-2");
+}, 'HTMLElement constructor must only get .prototype once, calling proxy constructor via Reflect');
+
+test(function() {
+    class SomeCustomElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+    customElements.define("success-counting-element-3", countingProxy);
+    // define() gets the prototype of the constructor it's passed, so
+    // reset the counter.
+    getCount = 0;
+    var instance = Reflect.construct(HTMLElement, [], countingProxy);
+    assert_equals(getCount, 1, "Should have gotten .prototype once");
+    assert_true(instance instanceof countingProxy);
+    assert_true(instance instanceof SomeCustomElement);
+    assert_equals(instance.localName, undefined);
+    assert_equals(instance.nodeName, undefined);
+}, 'HTMLElement constructor must only get .prototype once, calling proxy constructor via Reflect with no inheritance');
+
+test(function() {
+    class SomeCustomElement extends HTMLElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+    customElements.define("failure-counting-element-1", countingProxy,
+                          { extends: "button" });
+    // define() gets the prototype of the constructor it's passed, so
+    // reset the counter.
+    getCount = 0;
+    assert_throws({'name': 'TypeError'},
+                  function () { new countingProxy() },
+                  "Should not be able to construct an HTMLElement named 'button'");
+    assert_equals(getCount, 0, "Should never have gotten .prototype");
+}, 'HTMLElement constructor must not get .prototype until it finishes its extends sanity checks, calling proxy constructor directly');
+
+test(function() {
+    class SomeCustomElement extends HTMLElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+    customElements.define("failure-counting-element-2", countingProxy,
+                          { extends: "button" });
+    // define() gets the prototype of the constructor it's passed, so
+    // reset the counter.
+    getCount = 0;
+    assert_throws({'name': 'TypeError'},
+                  function () { Reflect.construct(HTMLElement, [], countingProxy) },
+                  "Should not be able to construct an HTMLElement named 'button'");
+    assert_equals(getCount, 0, "Should never have gotten .prototype");
+}, 'HTMLElement constructor must not get .prototype until it finishes its extends sanity checks, calling via Reflect');
+
+test(function() {
+    class SomeCustomElement extends HTMLElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+
+    // Purposefully don't register it.
+    assert_throws({'name': 'TypeError'},
+                  function () { new countingProxy() },
+                  "Should not be able to construct an HTMLElement named 'button'");
+    assert_equals(getCount, 0, "Should never have gotten .prototype");
+}, 'HTMLElement constructor must not get .prototype until it finishes its registration sanity checks, calling proxy constructor directly');
+
+test(function() {
+    class SomeCustomElement extends HTMLElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+
+    // Purposefully don't register it.
+    assert_throws({'name': 'TypeError'},
+                  function () { Reflect.construct(HTMLElement, [], countingProxy) },
+                  "Should not be able to construct an HTMLElement named 'button'");
+    assert_equals(getCount, 0, "Should never have gotten .prototype");
+}, 'HTMLElement constructor must not get .prototype until it finishes its registration  sanity checks, calling via Reflect');
 </script>
 </body>
 </html>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/custom-elements/htmlconstructor/newtarget-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/htmlconstructor/newtarget-expected.txt
new file mode 100644
index 0000000..46c1cf2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/htmlconstructor/newtarget-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS Use NewTarget's prototype, not the one stored at definition time
+PASS Rethrow any exceptions thrown while getting the prototype
+PASS If prototype is not object (null), derives the fallback from NewTarget's realm (autonomous custom elements)
+PASS If prototype is not object (undefined), derives the fallback from NewTarget's realm (autonomous custom elements)
+PASS If prototype is not object (5), derives the fallback from NewTarget's realm (autonomous custom elements)
+PASS If prototype is not object (string), derives the fallback from NewTarget's realm (autonomous custom elements)
+PASS If prototype is not object (null), derives the fallback from NewTarget's realm (customized built-in elements)
+PASS If prototype is not object (undefined), derives the fallback from NewTarget's realm (customized built-in elements)
+PASS If prototype is not object (5), derives the fallback from NewTarget's realm (customized built-in elements)
+PASS If prototype is not object (string), derives the fallback from NewTarget's realm (customized built-in elements)
+FAIL HTMLParagraphElement constructor must not get .prototype until it finishes its extends sanity checks, calling proxy constructor directly assert_equals: Should never have gotten .prototype expected 0 but got 1
+FAIL HTMLParagraphElement constructor must not get .prototype until it finishes its extends sanity checks, calling via Reflect assert_equals: Should never have gotten .prototype expected 0 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/custom-elements/htmlconstructor/newtarget.html b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/htmlconstructor/newtarget.html
index ab43803..7dad264 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/custom-elements/htmlconstructor/newtarget.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/custom-elements/htmlconstructor/newtarget.html
@@ -124,5 +124,46 @@
   }, "If prototype is not object (" + notAnObject + "), derives the fallback from NewTarget's realm (customized built-in elements)");
 });
 
+test_with_window(w => {
+    class SomeCustomElement extends HTMLParagraphElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+    w.customElements.define("failure-counting-element", countingProxy);
+    // define() gets the prototype of the constructor it's passed, so
+    // reset the counter.
+    getCount = 0;
+    assert_throws({'name': 'TypeError'},
+                  function () { new countingProxy() },
+                  "Should not be able to construct an HTMLParagraphElement not named 'p'");
+    assert_equals(getCount, 0, "Should never have gotten .prototype");
+}, 'HTMLParagraphElement constructor must not get .prototype until it finishes its extends sanity checks, calling proxy constructor directly');
+
+test_with_window(w => {
+    class SomeCustomElement extends HTMLParagraphElement {};
+    var getCount = 0;
+    var countingProxy = new Proxy(SomeCustomElement, {
+        get: function(target, prop, receiver) {
+            if (prop == "prototype") {
+                ++getCount;
+            }
+            return Reflect.get(target, prop, receiver);
+        }
+    });
+    w.customElements.define("failure-counting-element", countingProxy);
+    // define() gets the prototype of the constructor it's passed, so
+    // reset the counter.
+    getCount = 0;
+    assert_throws({'name': 'TypeError'},
+                  function () { Reflect.construct(HTMLParagraphElement, [], countingProxy) },
+                  "Should not be able to construct an HTMLParagraphElement not named 'p'");
+    assert_equals(getCount, 0, "Should never have gotten .prototype");
+}, 'HTMLParagraphElement constructor must not get .prototype until it finishes its extends sanity checks, calling via Reflect');
 </script>
-</body>
\ No newline at end of file
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt b/third_party/WebKit/LayoutTests/external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt
new file mode 100644
index 0000000..3e71550
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt
@@ -0,0 +1 @@
+Some text.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js b/third_party/WebKit/LayoutTests/external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js
new file mode 100644
index 0000000..52edd00
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js
@@ -0,0 +1,23 @@
+["replace",
+ "NOBODY",
+ "@ FD ;",
+ "it does not matter, you see \f",
+ "text/plain",
+ "text/xml",
+ "application/octet-stream",
+ "\0"].forEach(type => {
+  async_test(t => {
+    const frame = document.createElement("iframe");
+    frame.src = "type-argument-plaintext-subframe.txt";
+    document.body.appendChild(frame);
+    t.add_cleanup(() => frame.remove());
+    frame.onload = t.step_func_done(() => {
+      frame.contentDocument.open(type);
+      frame.contentDocument.write("<B>heya</b>");
+      frame.contentDocument.close();
+      assert_equals(frame.contentDocument.body.firstChild.localName, "b");
+      assert_equals(frame.contentDocument.body.textContent, "heya");
+      assert_equals(frame.contentDocument.contentType, "text/plain");
+    });
+  }, "document.open() on plaintext document with type set to: " + type + " (type argument is supposed to be ignored)");
+});
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/text-level-semantics/the-a-element/a-download-click-404-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/text-level-semantics/the-a-element/a-download-click-404-expected.txt
deleted file mode 100644
index d3c5fc6..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/text-level-semantics/the-a-element/a-download-click-404-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Do not navigate to 404 for anchor with download Blocked a frame with origin "http://web-platform.test:8001" from accessing a cross-origin frame.
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/charset-parameter.window-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/charset-parameter.window-expected.txt
index a6a7b25..5a6cb4b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/charset-parameter.window-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/charset-parameter.window-expected.txt
@@ -5,26 +5,34 @@
 FAIL text/html;charset=gbk( assert_equals: expected "UTF-8" but got "GBK"
 PASS text/html;x=(;charset=gbk
 FAIL text/html;charset=gbk;charset=windows-1255 assert_equals: expected "GBK" but got "windows-1255"
+FAIL text/html;charset=();charset=GBK assert_equals: expected "UTF-8" but got "GBK"
 PASS text/html;charset =gbk
 PASS text/html ;charset=gbk
 PASS text/html; charset=gbk
 PASS text/html;charset= gbk
+FAIL text/html;charset= "gbk" assert_equals: expected "UTF-8" but got "GBK"
 PASS text/html;charset='gbk'
 PASS text/html;charset='gbk
 PASS text/html;charset=gbk'
+FAIL text/html;charset=';charset=GBK assert_equals: expected "UTF-8" but got "GBK"
 PASS text/html;test;charset=gbk
 PASS text/html;test=;charset=gbk
 PASS text/html;';charset=gbk
 PASS text/html;";charset=gbk
 PASS text/html ; ; charset=gbk
 PASS text/html;;;;charset=gbk
+FAIL text/html;charset= ";charset=GBK assert_equals: expected "GBK" but got "UTF-8"
+PASS text/html;charset=";charset=foo";charset=GBK
 PASS text/html;charset="gbk"
 PASS text/html;charset="gbk
 PASS text/html;charset=gbk"
 FAIL text/html;charset=" gbk" assert_equals: expected "GBK" but got "UTF-8"
+FAIL text/html;charset="gbk " assert_equals: expected "GBK" but got "UTF-8"
 FAIL text/html;charset="\ gbk" assert_equals: expected "GBK" but got "UTF-8"
 PASS text/html;charset="\g\b\k"
 PASS text/html;charset="gbk"x
+PASS text/html;charset="";charset=GBK
+PASS text/html;charset=";charset=GBK
 PASS text/html;charset={gbk}
 PASS text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk
 PASS text/html;test=ÿ;charset=gbk
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any-expected.txt
index 77f8830..c81d9773 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 1857 tests; 697 PASS, 1160 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 1873 tests; 699 PASS, 1174 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS text/html;charset=gbk (Blob/File)
 PASS text/html;charset=gbk (Request/Response)
@@ -11,6 +11,8 @@
 FAIL text/html;x=(;charset=gbk (Request/Response) assert_equals: expected "text/html;x=\"(\";charset=gbk" but got "text/html;x=(;charset=gbk"
 FAIL text/html;charset=gbk;charset=windows-1255 (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=gbk;charset=windows-1255"
 FAIL text/html;charset=gbk;charset=windows-1255 (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=gbk;charset=windows-1255"
+FAIL text/html;charset=();charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=\"()\"" but got "text/html;charset=();charset=gbk"
+FAIL text/html;charset=();charset=GBK (Request/Response) assert_equals: expected "text/html;charset=\"()\"" but got "text/html;charset=();charset=gbk"
 FAIL text/html;charset =gbk (Blob/File) assert_equals: Blob expected "text/html" but got "text/html;charset =gbk"
 FAIL text/html;charset =gbk (Request/Response) assert_equals: expected "text/html" but got "text/html;charset =gbk"
 FAIL text/html ;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html ;charset=gbk"
@@ -19,12 +21,16 @@
 FAIL text/html; charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html; charset=gbk"
 FAIL text/html;charset= gbk (Blob/File) assert_equals: Blob expected "text/html;charset=\" gbk\"" but got "text/html;charset= gbk"
 FAIL text/html;charset= gbk (Request/Response) assert_equals: expected "text/html;charset=\" gbk\"" but got "text/html;charset= gbk"
+FAIL text/html;charset= "gbk" (Blob/File) assert_equals: Blob expected "text/html;charset=\" \\\"gbk\\"\"" but got "text/html;charset= \"gbk\""
+FAIL text/html;charset= "gbk" (Request/Response) assert_equals: expected "text/html;charset=\" \\\"gbk\\"\"" but got "text/html;charset= \"gbk\""
 PASS text/html;charset='gbk' (Blob/File)
 PASS text/html;charset='gbk' (Request/Response)
 PASS text/html;charset='gbk (Blob/File)
 PASS text/html;charset='gbk (Request/Response)
 PASS text/html;charset=gbk' (Blob/File)
 PASS text/html;charset=gbk' (Request/Response)
+FAIL text/html;charset=';charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset='" but got "text/html;charset=';charset=gbk"
+FAIL text/html;charset=';charset=GBK (Request/Response) assert_equals: expected "text/html;charset='" but got "text/html;charset=';charset=gbk"
 FAIL text/html;test;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;test;charset=gbk"
 FAIL text/html;test;charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;test;charset=gbk"
 FAIL text/html;test=;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;test=;charset=gbk"
@@ -37,6 +43,10 @@
 FAIL text/html ; ; charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html ; ; charset=gbk"
 FAIL text/html;;;;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;;;;charset=gbk"
 FAIL text/html;;;;charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;;;;charset=gbk"
+FAIL text/html;charset= ";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=GBK" but got ""
+FAIL text/html;charset= ";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=GBK" but got ""
+FAIL text/html;charset=";charset=foo";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=GBK" but got ""
+FAIL text/html;charset=";charset=foo";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=GBK" but got ""
 FAIL text/html;charset="gbk" (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\""
 FAIL text/html;charset="gbk" (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\""
 FAIL text/html;charset="gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"gbk"
@@ -45,12 +55,18 @@
 FAIL text/html;charset=gbk" (Request/Response) assert_equals: expected "text/html;charset=\"gbk\\\"\"" but got "text/html;charset=gbk\""
 PASS text/html;charset=" gbk" (Blob/File)
 PASS text/html;charset=" gbk" (Request/Response)
+PASS text/html;charset="gbk " (Blob/File)
+PASS text/html;charset="gbk " (Request/Response)
 FAIL text/html;charset="\ gbk" (Blob/File) assert_equals: Blob expected "text/html;charset=\" gbk\"" but got "text/html;charset=\"\\ gbk\""
 FAIL text/html;charset="\ gbk" (Request/Response) assert_equals: expected "text/html;charset=\" gbk\"" but got "text/html;charset=\"\\ gbk\""
 FAIL text/html;charset="\g\b\k" (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"\\g\b\k\""
 FAIL text/html;charset="\g\b\k" (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=\"\\g\b\k\""
 FAIL text/html;charset="gbk"x (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\"x"
 FAIL text/html;charset="gbk"x (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\"x"
+FAIL text/html;charset="";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=GBK" but got "text/html;charset=\"\";charset=gbk"
+FAIL text/html;charset="";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=GBK" but got "text/html;charset=\"\";charset=gbk"
+FAIL text/html;charset=";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=\";charset=GBK\"" but got "text/html;charset=\";charset=gbk"
+FAIL text/html;charset=";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=\";charset=GBK\"" but got "text/html;charset=\";charset=gbk"
 FAIL text/html;charset={gbk} (Blob/File) assert_equals: Blob expected "text/html;charset=\"{gbk}\"" but got "text/html;charset={gbk}"
 FAIL text/html;charset={gbk} (Request/Response) assert_equals: expected "text/html;charset=\"{gbk}\"" but got "text/html;charset={gbk}"
 PASS text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk (Blob/File)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any.worker-expected.txt
index 77f8830..c81d9773 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any.worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/parsing.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 1857 tests; 697 PASS, 1160 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 1873 tests; 699 PASS, 1174 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS text/html;charset=gbk (Blob/File)
 PASS text/html;charset=gbk (Request/Response)
@@ -11,6 +11,8 @@
 FAIL text/html;x=(;charset=gbk (Request/Response) assert_equals: expected "text/html;x=\"(\";charset=gbk" but got "text/html;x=(;charset=gbk"
 FAIL text/html;charset=gbk;charset=windows-1255 (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=gbk;charset=windows-1255"
 FAIL text/html;charset=gbk;charset=windows-1255 (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=gbk;charset=windows-1255"
+FAIL text/html;charset=();charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=\"()\"" but got "text/html;charset=();charset=gbk"
+FAIL text/html;charset=();charset=GBK (Request/Response) assert_equals: expected "text/html;charset=\"()\"" but got "text/html;charset=();charset=gbk"
 FAIL text/html;charset =gbk (Blob/File) assert_equals: Blob expected "text/html" but got "text/html;charset =gbk"
 FAIL text/html;charset =gbk (Request/Response) assert_equals: expected "text/html" but got "text/html;charset =gbk"
 FAIL text/html ;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html ;charset=gbk"
@@ -19,12 +21,16 @@
 FAIL text/html; charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html; charset=gbk"
 FAIL text/html;charset= gbk (Blob/File) assert_equals: Blob expected "text/html;charset=\" gbk\"" but got "text/html;charset= gbk"
 FAIL text/html;charset= gbk (Request/Response) assert_equals: expected "text/html;charset=\" gbk\"" but got "text/html;charset= gbk"
+FAIL text/html;charset= "gbk" (Blob/File) assert_equals: Blob expected "text/html;charset=\" \\\"gbk\\"\"" but got "text/html;charset= \"gbk\""
+FAIL text/html;charset= "gbk" (Request/Response) assert_equals: expected "text/html;charset=\" \\\"gbk\\"\"" but got "text/html;charset= \"gbk\""
 PASS text/html;charset='gbk' (Blob/File)
 PASS text/html;charset='gbk' (Request/Response)
 PASS text/html;charset='gbk (Blob/File)
 PASS text/html;charset='gbk (Request/Response)
 PASS text/html;charset=gbk' (Blob/File)
 PASS text/html;charset=gbk' (Request/Response)
+FAIL text/html;charset=';charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset='" but got "text/html;charset=';charset=gbk"
+FAIL text/html;charset=';charset=GBK (Request/Response) assert_equals: expected "text/html;charset='" but got "text/html;charset=';charset=gbk"
 FAIL text/html;test;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;test;charset=gbk"
 FAIL text/html;test;charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;test;charset=gbk"
 FAIL text/html;test=;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;test=;charset=gbk"
@@ -37,6 +43,10 @@
 FAIL text/html ; ; charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html ; ; charset=gbk"
 FAIL text/html;;;;charset=gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;;;;charset=gbk"
 FAIL text/html;;;;charset=gbk (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;;;;charset=gbk"
+FAIL text/html;charset= ";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=GBK" but got ""
+FAIL text/html;charset= ";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=GBK" but got ""
+FAIL text/html;charset=";charset=foo";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=GBK" but got ""
+FAIL text/html;charset=";charset=foo";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=GBK" but got ""
 FAIL text/html;charset="gbk" (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\""
 FAIL text/html;charset="gbk" (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\""
 FAIL text/html;charset="gbk (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"gbk"
@@ -45,12 +55,18 @@
 FAIL text/html;charset=gbk" (Request/Response) assert_equals: expected "text/html;charset=\"gbk\\\"\"" but got "text/html;charset=gbk\""
 PASS text/html;charset=" gbk" (Blob/File)
 PASS text/html;charset=" gbk" (Request/Response)
+PASS text/html;charset="gbk " (Blob/File)
+PASS text/html;charset="gbk " (Request/Response)
 FAIL text/html;charset="\ gbk" (Blob/File) assert_equals: Blob expected "text/html;charset=\" gbk\"" but got "text/html;charset=\"\\ gbk\""
 FAIL text/html;charset="\ gbk" (Request/Response) assert_equals: expected "text/html;charset=\" gbk\"" but got "text/html;charset=\"\\ gbk\""
 FAIL text/html;charset="\g\b\k" (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"\\g\b\k\""
 FAIL text/html;charset="\g\b\k" (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=\"\\g\b\k\""
 FAIL text/html;charset="gbk"x (Blob/File) assert_equals: Blob expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\"x"
 FAIL text/html;charset="gbk"x (Request/Response) assert_equals: expected "text/html;charset=gbk" but got "text/html;charset=\"gbk\"x"
+FAIL text/html;charset="";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=GBK" but got "text/html;charset=\"\";charset=gbk"
+FAIL text/html;charset="";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=GBK" but got "text/html;charset=\"\";charset=gbk"
+FAIL text/html;charset=";charset=GBK (Blob/File) assert_equals: Blob expected "text/html;charset=\";charset=GBK\"" but got "text/html;charset=\";charset=gbk"
+FAIL text/html;charset=";charset=GBK (Request/Response) assert_equals: expected "text/html;charset=\";charset=GBK\"" but got "text/html;charset=\";charset=gbk"
 FAIL text/html;charset={gbk} (Blob/File) assert_equals: Blob expected "text/html;charset=\"{gbk}\"" but got "text/html;charset={gbk}"
 FAIL text/html;charset={gbk} (Request/Response) assert_equals: expected "text/html;charset=\"{gbk}\"" but got "text/html;charset={gbk}"
 PASS text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk (Blob/File)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/resources/mime-types.json b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/resources/mime-types.json
index 6bee02d..3dcdf55e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/resources/mime-types.json
+++ b/third_party/WebKit/LayoutTests/external/wpt/mimesniff/mime-types/resources/mime-types.json
@@ -32,6 +32,12 @@
     "navigable": true,
     "encoding": "GBK"
   },
+  {
+    "input": "text/html;charset=();charset=GBK",
+    "output": "text/html;charset=\"()\"",
+    "navigable": true,
+    "encoding": null
+  },
   "Spaces",
   {
     "input": "text/html;charset =gbk",
@@ -57,6 +63,12 @@
     "navigable": true,
     "encoding": "GBK"
   },
+  {
+    "input": "text/html;charset= \"gbk\"",
+    "output": "text/html;charset=\" \\\"gbk\\\"\"",
+    "navigable": true,
+    "encoding": null
+  },
   "Single quotes are a token, not a delimiter",
   {
     "input": "text/html;charset='gbk'",
@@ -76,6 +88,12 @@
     "navigable": true,
     "encoding": null
   },
+  {
+    "input": "text/html;charset=';charset=GBK",
+    "output": "text/html;charset='",
+    "navigable": true,
+    "encoding": null
+  },
   "Invalid parameters",
   {
     "input": "text/html;test;charset=gbk",
@@ -113,6 +131,18 @@
     "navigable": true,
     "encoding": "GBK"
   },
+  {
+    "input": "text/html;charset= \"\u007F;charset=GBK",
+    "output": "text/html;charset=GBK",
+    "navigable": true,
+    "encoding": "GBK"
+  },
+  {
+    "input": "text/html;charset=\"\u007F;charset=foo\";charset=GBK",
+    "output": "text/html;charset=GBK",
+    "navigable": true,
+    "encoding": "GBK"
+  },
   "Double quotes",
   {
     "input": "text/html;charset=\"gbk\"",
@@ -139,6 +169,12 @@
     "encoding": "GBK"
   },
   {
+    "input": "text/html;charset=\"gbk \"",
+    "output": "text/html;charset=\"gbk \"",
+    "navigable": true,
+    "encoding": "GBK"
+  },
+  {
     "input": "text/html;charset=\"\\ gbk\"",
     "output": "text/html;charset=\" gbk\"",
     "navigable": true,
@@ -156,6 +192,18 @@
     "navigable": true,
     "encoding": "GBK"
   },
+  {
+    "input": "text/html;charset=\"\";charset=GBK",
+    "output": "text/html;charset=GBK",
+    "navigable": true,
+    "encoding": "GBK"
+  },
+  {
+    "input": "text/html;charset=\";charset=GBK",
+    "output": "text/html;charset=\";charset=GBK\"",
+    "navigable": true,
+    "encoding": null
+  },
   "Unexpected code points",
   {
     "input": "text/html;charset={gbk}",
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-insecure.http.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-insecure.http.html
index 0212220..c097bf1 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-insecure.http.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-request-insecure.http.html
@@ -1,5 +1,4 @@
 <!DOCTYPE html>
-<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
 <meta charset="utf-8">
 <title>Test for PaymentRequest Constructor (insecure)</title>
 <link rel="help" href="https://w3c.github.io/payment-request/#paymentrequest-interface">
@@ -8,6 +7,8 @@
 <script>
 test(() => {
   assert_false(isSecureContext);
-  assert_false("PaymentRequest" in window);
+  assert_false('PaymentRequest' in window);
+  assert_false('PaymentResponse' in window);
+  assert_false('PaymentRequestUpdateEvent' in window);
 }, "PaymentRequest constructor must not be exposed in insecure context");
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/fake_bluetooth.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/fake_bluetooth.mojom.js
index 08c26b6..a8d79556 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/fake_bluetooth.mojom.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/fake_bluetooth.mojom.js
@@ -2896,6 +2896,306 @@
     encoder.skip(1);
     encoder.skip(1);
   };
+  function FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.prototype.initDefaults_ = function() {
+    this.gattCode = 0;
+    this.characteristicId = null;
+    this.serviceId = null;
+    this.peripheralAddress = null;
+  };
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 40}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    // validate FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.characteristicId
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.serviceId
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 16, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.peripheralAddress
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 24, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.encodedSize = codec.kStructHeaderSize + 32;
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.decode = function(decoder) {
+    var packed;
+    var val = new FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.gattCode = decoder.decodeStruct(codec.Uint16);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.characteristicId = decoder.decodeStruct(codec.String);
+    val.serviceId = decoder.decodeStruct(codec.String);
+    val.peripheralAddress = decoder.decodeStruct(codec.String);
+    return val;
+  };
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint16, val.gattCode);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.String, val.characteristicId);
+    encoder.encodeStruct(codec.String, val.serviceId);
+    encoder.encodeStruct(codec.String, val.peripheralAddress);
+  };
+  function FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function FakeCentral_IsNotifying_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  FakeCentral_IsNotifying_Params.prototype.initDefaults_ = function() {
+    this.characteristicId = null;
+    this.serviceId = null;
+    this.peripheralAddress = null;
+  };
+  FakeCentral_IsNotifying_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  FakeCentral_IsNotifying_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 32}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate FakeCentral_IsNotifying_Params.characteristicId
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 0, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate FakeCentral_IsNotifying_Params.serviceId
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate FakeCentral_IsNotifying_Params.peripheralAddress
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 16, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  FakeCentral_IsNotifying_Params.encodedSize = codec.kStructHeaderSize + 24;
+
+  FakeCentral_IsNotifying_Params.decode = function(decoder) {
+    var packed;
+    var val = new FakeCentral_IsNotifying_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.characteristicId = decoder.decodeStruct(codec.String);
+    val.serviceId = decoder.decodeStruct(codec.String);
+    val.peripheralAddress = decoder.decodeStruct(codec.String);
+    return val;
+  };
+
+  FakeCentral_IsNotifying_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(FakeCentral_IsNotifying_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.String, val.characteristicId);
+    encoder.encodeStruct(codec.String, val.serviceId);
+    encoder.encodeStruct(codec.String, val.peripheralAddress);
+  };
+  function FakeCentral_IsNotifying_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  FakeCentral_IsNotifying_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+    this.isNotifying = false;
+  };
+  FakeCentral_IsNotifying_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  FakeCentral_IsNotifying_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    return validator.validationError.NONE;
+  };
+
+  FakeCentral_IsNotifying_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  FakeCentral_IsNotifying_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new FakeCentral_IsNotifying_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    val.isNotifying = (packed >> 1) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  FakeCentral_IsNotifying_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(FakeCentral_IsNotifying_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    packed |= (val.isNotifying & 1) << 1
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
   function FakeCentral_GetLastWrittenCharacteristicValue_Params(values) {
     this.initDefaults_();
     this.initFields_(values);
@@ -3786,10 +4086,12 @@
   var kFakeCentral_SetNextReadCharacteristicResponse_Name = 12;
   var kFakeCentral_SetNextWriteCharacteristicResponse_Name = 13;
   var kFakeCentral_SetNextSubscribeToNotificationsResponse_Name = 14;
-  var kFakeCentral_GetLastWrittenCharacteristicValue_Name = 15;
-  var kFakeCentral_SetNextReadDescriptorResponse_Name = 16;
-  var kFakeCentral_SetNextWriteDescriptorResponse_Name = 17;
-  var kFakeCentral_GetLastWrittenDescriptorValue_Name = 18;
+  var kFakeCentral_SetNextUnsubscribeFromNotificationsResponse_Name = 15;
+  var kFakeCentral_IsNotifying_Name = 16;
+  var kFakeCentral_GetLastWrittenCharacteristicValue_Name = 17;
+  var kFakeCentral_SetNextReadDescriptorResponse_Name = 18;
+  var kFakeCentral_SetNextWriteDescriptorResponse_Name = 19;
+  var kFakeCentral_GetLastWrittenDescriptorValue_Name = 20;
 
   function FakeCentralPtr(handleOrPtrInfo) {
     this.ptr = new bindings.InterfacePtrController(FakeCentral,
@@ -4211,6 +4513,61 @@
       });
     }.bind(this));
   };
+  FakeCentralPtr.prototype.setNextUnsubscribeFromNotificationsResponse = function() {
+    return FakeCentralProxy.prototype.setNextUnsubscribeFromNotificationsResponse
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  FakeCentralProxy.prototype.setNextUnsubscribeFromNotificationsResponse = function(gattCode, characteristicId, serviceId, peripheralAddress) {
+    var params = new FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params();
+    params.gattCode = gattCode;
+    params.characteristicId = characteristicId;
+    params.serviceId = serviceId;
+    params.peripheralAddress = peripheralAddress;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kFakeCentral_SetNextUnsubscribeFromNotificationsResponse_Name,
+          codec.align(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  FakeCentralPtr.prototype.isNotifying = function() {
+    return FakeCentralProxy.prototype.isNotifying
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  FakeCentralProxy.prototype.isNotifying = function(characteristicId, serviceId, peripheralAddress) {
+    var params = new FakeCentral_IsNotifying_Params();
+    params.characteristicId = characteristicId;
+    params.serviceId = serviceId;
+    params.peripheralAddress = peripheralAddress;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kFakeCentral_IsNotifying_Name,
+          codec.align(FakeCentral_IsNotifying_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(FakeCentral_IsNotifying_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(FakeCentral_IsNotifying_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
   FakeCentralPtr.prototype.getLastWrittenCharacteristicValue = function() {
     return FakeCentralProxy.prototype.getLastWrittenCharacteristicValue
         .apply(this.ptr.getProxy(), arguments);
@@ -4374,6 +4731,12 @@
   FakeCentralStub.prototype.setNextSubscribeToNotificationsResponse = function(gattCode, characteristicId, serviceId, peripheralAddress) {
     return this.delegate_ && this.delegate_.setNextSubscribeToNotificationsResponse && this.delegate_.setNextSubscribeToNotificationsResponse(gattCode, characteristicId, serviceId, peripheralAddress);
   }
+  FakeCentralStub.prototype.setNextUnsubscribeFromNotificationsResponse = function(gattCode, characteristicId, serviceId, peripheralAddress) {
+    return this.delegate_ && this.delegate_.setNextUnsubscribeFromNotificationsResponse && this.delegate_.setNextUnsubscribeFromNotificationsResponse(gattCode, characteristicId, serviceId, peripheralAddress);
+  }
+  FakeCentralStub.prototype.isNotifying = function(characteristicId, serviceId, peripheralAddress) {
+    return this.delegate_ && this.delegate_.isNotifying && this.delegate_.isNotifying(characteristicId, serviceId, peripheralAddress);
+  }
   FakeCentralStub.prototype.getLastWrittenCharacteristicValue = function(characteristicId, serviceId, peripheralAddress) {
     return this.delegate_ && this.delegate_.getLastWrittenCharacteristicValue && this.delegate_.getLastWrittenCharacteristicValue(characteristicId, serviceId, peripheralAddress);
   }
@@ -4637,6 +5000,39 @@
         responder.accept(message);
       });
       return true;
+    case kFakeCentral_SetNextUnsubscribeFromNotificationsResponse_Name:
+      var params = reader.decodeStruct(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params);
+      this.setNextUnsubscribeFromNotificationsResponse(params.gattCode, params.characteristicId, params.serviceId, params.peripheralAddress).then(function(response) {
+        var responseParams =
+            new FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kFakeCentral_SetNextUnsubscribeFromNotificationsResponse_Name,
+            codec.align(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kFakeCentral_IsNotifying_Name:
+      var params = reader.decodeStruct(FakeCentral_IsNotifying_Params);
+      this.isNotifying(params.characteristicId, params.serviceId, params.peripheralAddress).then(function(response) {
+        var responseParams =
+            new FakeCentral_IsNotifying_ResponseParams();
+        responseParams.success = response.success;
+        responseParams.isNotifying = response.isNotifying;
+        var builder = new codec.MessageV1Builder(
+            kFakeCentral_IsNotifying_Name,
+            codec.align(FakeCentral_IsNotifying_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(FakeCentral_IsNotifying_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
     case kFakeCentral_GetLastWrittenCharacteristicValue_Name:
       var params = reader.decodeStruct(FakeCentral_GetLastWrittenCharacteristicValue_Params);
       this.getLastWrittenCharacteristicValue(params.characteristicId, params.serviceId, params.peripheralAddress).then(function(response) {
@@ -4772,6 +5168,14 @@
         if (message.expectsResponse())
           paramsClass = FakeCentral_SetNextSubscribeToNotificationsResponse_Params;
       break;
+      case kFakeCentral_SetNextUnsubscribeFromNotificationsResponse_Name:
+        if (message.expectsResponse())
+          paramsClass = FakeCentral_SetNextUnsubscribeFromNotificationsResponse_Params;
+      break;
+      case kFakeCentral_IsNotifying_Name:
+        if (message.expectsResponse())
+          paramsClass = FakeCentral_IsNotifying_Params;
+      break;
       case kFakeCentral_GetLastWrittenCharacteristicValue_Name:
         if (message.expectsResponse())
           paramsClass = FakeCentral_GetLastWrittenCharacteristicValue_Params;
@@ -4858,6 +5262,14 @@
         if (message.isResponse())
           paramsClass = FakeCentral_SetNextSubscribeToNotificationsResponse_ResponseParams;
         break;
+      case kFakeCentral_SetNextUnsubscribeFromNotificationsResponse_Name:
+        if (message.isResponse())
+          paramsClass = FakeCentral_SetNextUnsubscribeFromNotificationsResponse_ResponseParams;
+        break;
+      case kFakeCentral_IsNotifying_Name:
+        if (message.isResponse())
+          paramsClass = FakeCentral_IsNotifying_ResponseParams;
+        break;
       case kFakeCentral_GetLastWrittenCharacteristicValue_Name:
         if (message.isResponse())
           paramsClass = FakeCentral_GetLastWrittenCharacteristicValue_ResponseParams;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web-bluetooth-test.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web-bluetooth-test.js
index e08c8c4a..1700b04 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web-bluetooth-test.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/web-bluetooth-test.js
@@ -393,6 +393,30 @@
     if (!success) throw 'setNextSubscribeToNotificationsResponse failed';
   }
 
+  // Sets the next unsubscribe to notifications response for characteristic with
+  // |characteristic_id| in |service_id| and in |peripheral_address| to
+  // |code|. |code| could be a GATT Error Response from BT 4.2 Vol 3 Part F
+  // 3.4.1.1 Error Response or a number outside that range returned by
+  // specific platforms e.g. Android returns 0x101 to signal a GATT failure.
+  async setNextUnsubscribeFromNotificationsResponse(gatt_code) {
+    let {success} =
+      await this.fake_central_ptr_.setNextUnsubscribeFromNotificationsResponse(
+        gatt_code, ...this.ids_);
+
+    if (!success) throw 'setNextUnsubscribeToNotificationsResponse failed';
+  }
+
+  // Returns true if notifications from the characteristic have been subscribed
+  // to.
+  async isNotifying() {
+    let {success, isNotifying} =
+        await this.fake_central_ptr_.isNotifying(...this.ids_);
+
+    if (!success) throw 'isNotifying failed';
+
+    return isNotifying;
+  }
+
   // Gets the last successfully written value to the characteristic.
   // Returns null if no value has yet been written to the characteristic.
   async getLastWrittenValue() {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/cross_origin.html b/third_party/WebKit/LayoutTests/external/wpt/server-timing/cross_origin.html
index 80035888..f525103 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/cross_origin.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/cross_origin.html
@@ -4,13 +4,16 @@
     <script src="/resources/testharness.js"></script>
     <script src='/resources/testharnessreport.js'></script>
     <script src="/common/performance-timeline-utils.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
     <script>
       setup({explicit_done: true})
 
-      const {location: {href}} = document
-      const urls = {}
-      urls['same-origin'] = `${href.substring(0, href.lastIndexOf('/'))}/resources/blue.png`
-      urls['cross-origin'] = urls['same-origin'].replace('://', '://www.')
+      const hostInfo = get_host_info()
+      const resourceUrl = 'server-timing/resources/blue.png'
+      const urls = {
+        'same-origin': `${hostInfo.HTTP_ORIGIN}/${resourceUrl}`,
+        'cross-origin': `${hostInfo.HTTP_REMOTE_ORIGIN}/${resourceUrl}`
+      }
       Object.keys(urls).forEach(function(key) {
         const img = document.createElement('img')
         img.src = urls[key]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/0.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/0.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/0.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/0.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/1.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/1.js
index 220cf1a..03b778b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/1.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/1.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric"}])
+testServerTiming(document.currentScript, [{"name":"metric"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/10.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/10.js
index 220cf1a..03b778b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/10.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/10.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric"}])
+testServerTiming(document.currentScript, [{"name":"metric"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/11.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/11.js
index 220cf1a..03b778b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/11.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/11.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric"}])
+testServerTiming(document.currentScript, [{"name":"metric"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/12.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/12.js
index c32491d..fc827f8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/12.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/12.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":123.4,"desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":123.4,"desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/13.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/13.js
index 9b767142..02f8c3c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/13.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/13.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"description","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"description","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/14.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/14.js
index 153607bf..966e963 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/14.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/14.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/15.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/15.js
index 311cabe..afef77d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/15.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/15.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric1","dur":12.3,"desc":"description1"},{"name":"metric2","dur":45.6,"desc":"description2"},{"name":"metric3","dur":78.9,"desc":"description3"}])
+testServerTiming(document.currentScript, [{"name":"metric1","dur":12.3,"desc":"description1"},{"name":"metric2","dur":45.6,"desc":"description2"},{"name":"metric3","dur":78.9,"desc":"description3"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/16.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/16.js
index ab8597f..b49b1785 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/16.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/16.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric1"},{"name":"metric2"},{"name":"metric3"},{"name":"metric4"},{"name":"metric5"}])
+testServerTiming(document.currentScript, [{"name":"metric1"},{"name":"metric2"},{"name":"metric3"},{"name":"metric4"},{"name":"metric5"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/17.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/17.js
index 153607bf..966e963 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/17.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/17.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/18.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/18.js
index 5924751..3c47d76 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/18.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/18.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"\t description \t"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"\t description \t"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/19.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/19.js
index d8cc6b72..83fb4f3 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/19.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/19.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"descr\"iption"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"descr\"iption"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/2.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/2.js
index 6fd97fa..b763b814 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/2.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/2.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/20.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/20.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/20.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/20.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/21.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/21.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/21.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/21.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/22.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/22.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/22.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/22.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/23.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/23.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/23.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/23.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/24.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/24.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/24.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/24.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/25.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/25.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/25.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/25.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/26.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/26.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/26.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/26.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/27.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/27.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/27.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/27.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/28.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/28.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/28.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/28.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/29.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/29.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/29.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/29.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/3.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/3.js
index 6fd97fa..b763b814 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/3.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/3.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/30.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/30.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/30.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/30.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/31.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/31.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/31.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/31.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/32.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/32.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/32.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/32.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/33.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/33.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/33.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/33.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/34.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/34.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/34.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/34.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/35.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/35.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/35.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/35.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/36.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/36.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/36.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/36.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/37.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/37.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/37.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/37.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/38.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/38.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/38.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/38.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/39.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/39.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/39.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/39.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/4.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/4.js
index 153607bf..966e963 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/4.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/4.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/40.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/40.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/40.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/40.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/41.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/41.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/41.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/41.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/42.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/42.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/42.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/42.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/43.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/43.js
index b318cb7..f3ac7dc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/43.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/43.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"\\"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"\\"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/44.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/44.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/44.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/44.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/45.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/45.js
index 993a690..82de6a40 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/45.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/45.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"\""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"\""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/46.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/46.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/46.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/46.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/47.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/47.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/47.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/47.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/48.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/48.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/48.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/48.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/49.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/49.js
index 107695e..349a7e0dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/49.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/49.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":""}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":""}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/5.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/5.js
index 153607bf..966e963 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/5.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/5.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/50.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/50.js
index 61ec691..413d9b6 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/50.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/50.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":12.3,"desc":"description1"},{"name":"metric","dur":45.6,"desc":"description2"}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":12.3,"desc":"description1"},{"name":"metric","dur":45.6,"desc":"description2"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/51.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/51.js
index c32491d..fc827f8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/51.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/51.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":123.4,"desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":123.4,"desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/52.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/52.js
index 6617af5..a97e9d0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/52.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/52.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"MeTrIc","desc":"DeScRiPtIoN"}])
+testServerTiming(document.currentScript, [{"name":"MeTrIc","desc":"DeScRiPtIoN"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/53.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/53.js
index 40faf41..adf74fa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/53.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/53.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":0}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":0}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/54.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/54.js
index 40faf41..adf74fa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/54.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/54.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":0}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":0}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/55.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/55.js
index 4e2b14b..429b528 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/55.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/55.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric1","desc":"description","dur":123.4},{"name":"metric2"}])
+testServerTiming(document.currentScript, [{"name":"metric1","desc":"description","dur":123.4},{"name":"metric2"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/56.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/56.js
index 6fd97fa..b763b814 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/56.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/56.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/57.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/57.js
index 40faf41..adf74fa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/57.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/57.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":0}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":0}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/58.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/58.js
index 13250bb..05004e5 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/58.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/58.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"description1"}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"description1"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/59.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/59.js
index 0282f7d..c9a9a98 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/59.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/59.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":0,"desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":0,"desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/6.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/6.js
index c32491d..fc827f8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/6.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/6.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":123.4,"desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":123.4,"desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/60.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/60.js
index 0282f7d..c9a9a98 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/60.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/60.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":0,"desc":"description"}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":0,"desc":"description"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/61.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/61.js
index 62b744cc..ce7d800 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/61.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/61.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/62.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/62.js
index 62b744cc..ce7d800 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/62.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/62.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/63.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/63.js
index 839f8064..d6842ba 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/63.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/63.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"d1","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"d1","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/64.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/64.js
index d64ea0a9..88037d15d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/64.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/64.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric1","desc":"d1"},{"name":"metric2"}])
+testServerTiming(document.currentScript, [{"name":"metric1","desc":"d1"},{"name":"metric2"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/65.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/65.js
index 839f8064..d6842ba 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/65.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/65.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"d1","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"d1","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/66.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/66.js
index d64ea0a9..88037d15d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/66.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/66.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric1","desc":"d1"},{"name":"metric2"}])
+testServerTiming(document.currentScript, [{"name":"metric1","desc":"d1"},{"name":"metric2"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/67.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/67.js
index 220cf1a..03b778b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/67.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/67.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric"}])
+testServerTiming(document.currentScript, [{"name":"metric"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/68.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/68.js
index 4077b26a..e6946c3 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/68.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/68.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric1"}])
+testServerTiming(document.currentScript, [{"name":"metric1"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/69.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/69.js
index 40faf41..adf74fa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/69.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/69.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","dur":0}])
+testServerTiming(document.currentScript, [{"name":"metric","dur":0}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/7.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/7.js
index 9b767142..02f8c3c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/7.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/7.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"description","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"description","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/70.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/70.js
index 220cf1a..03b778b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/70.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/70.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric"}])
+testServerTiming(document.currentScript, [{"name":"metric"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/71.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/71.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/71.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/71.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/72.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/72.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/72.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/72.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/73.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/73.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/73.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/73.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/74.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/74.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/74.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/74.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/75.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/75.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/75.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/75.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/76.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/76.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/76.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/76.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/77.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/77.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/77.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/77.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/78.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/78.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/78.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/78.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/79.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/79.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/79.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/79.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/8.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/8.js
index 8581efd9..711e381 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/8.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/8.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"aB3!#$%&'*+-.^_`|~"}])
+testServerTiming(document.currentScript, [{"name":"aB3!#$%&'*+-.^_`|~"}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/80.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/80.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/80.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/80.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/81.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/81.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/81.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/81.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/82.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/82.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/82.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/82.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/83.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/83.js
index 8de2c01..2848a1c8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/83.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/83.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [])
+testServerTiming(document.currentScript, [])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/9.js b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/9.js
index e03b07a2..3048f3fa 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/9.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/resources/parsing/9.js
@@ -1 +1 @@
-testServerTiming(document.currentScript.src, [{"name":"metric","desc":"descr;,=iption","dur":123.4}])
+testServerTiming(document.currentScript, [{"name":"metric","desc":"descr;,=iption","dur":123.4}])
diff --git a/third_party/WebKit/LayoutTests/external/wpt/server-timing/server_timing_header-parsing.html b/third_party/WebKit/LayoutTests/external/wpt/server-timing/server_timing_header-parsing.html
index a598a71..4049c3dd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/server-timing/server_timing_header-parsing.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/server-timing/server_timing_header-parsing.html
@@ -11,28 +11,40 @@
     <script src="/common/performance-timeline-utils.js"></script>
     <script>
       setup({explicit_done: true})
-      function testServerTiming(resource, expectedResults) {
-        const {serverTiming} = performance.getEntriesByName(resource)[0]
-        const fileName = resource.substring(resource.lastIndexOf('/') + 1)
-        test_equals(serverTiming.length, expectedResults.length, `${fileName} - count (${serverTiming.length} ?== ${expectedResults.length})`)
+      const tests = []
+      const urlToIndex = {}
+      function testServerTiming(script, expectedResults) {
+        const url = script.src
+        tests[urlToIndex[url]] = {url, expectedResults}
+      }
+      function runTests() {
+        tests.forEach(function({url, expectedResults}) {
+          debugger;
+          const {serverTiming} = performance.getEntriesByName(url)[0]
+          const fileName = url.substring(url.lastIndexOf('/') + 1)
 
-        expectedResults.forEach(function(expectedResult, i) {
-          const dur = expectedResult.dur || 0
-          const desc = expectedResult.desc || ''
-          const index = expectedResults.length === 1 ? '' : `[${i}].`
-          test_equals(expectedResult.name, serverTiming[i].name,
-            `${fileName} - ${index}name (${expectedResult.name} ?== ${serverTiming[i].name})`)
-          test_equals(dur, serverTiming[i].duration,
-            `${fileName} - ${index}duration (${dur} ?== ${serverTiming[i].duration})`)
-          test_equals(desc, serverTiming[i].description,
-            `${fileName} - ${index}description (${desc} ?== ${serverTiming[i].description})`)
+          test_equals(serverTiming.length, expectedResults.length, `${fileName} - count (${serverTiming.length} ?== ${expectedResults.length})`)
+
+          expectedResults.forEach(function(expectedResult, i) {
+            const dur = expectedResult.dur || 0
+            const desc = expectedResult.desc || ''
+            const index = expectedResults.length === 1 ? '' : `[${i}].`
+            test_equals(expectedResult.name, serverTiming[i].name,
+                `${fileName} - ${index}name (${expectedResult.name} ?== ${serverTiming[i].name})`)
+            test_equals(dur, serverTiming[i].duration,
+                `${fileName} - ${index}duration (${dur} ?== ${serverTiming[i].duration})`)
+            test_equals(desc, serverTiming[i].description,
+                `${fileName} - ${index}description (${desc} ?== ${serverTiming[i].description})`)
+          })
         })
+        done()
       }
       for (let i = 0; i <= 83; i++) {
-        var script = document.createElement('script')
+        const script = document.createElement('script')
         script.src = `./resources/parsing/${i}.js`
         document.getElementsByTagName('head')[0].appendChild(script)
+        urlToIndex[script.src] = i
       }
-      window.addEventListener('load', done)
+      window.addEventListener('load', runTests)
     </script>
 </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/interfaces/WorkerUtils/WindowTimers/005.html b/third_party/WebKit/LayoutTests/external/wpt/workers/interfaces/WorkerUtils/WindowTimers/005.html
new file mode 100644
index 0000000..b86eff1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/interfaces/WorkerUtils/WindowTimers/005.html
@@ -0,0 +1,23 @@
+<!--
+self.close();
+var t = setInterval(function() {}, 10);
+postMessage(t);
+/*
+-->
+<!doctype html>
+<title>setInterval when closing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+async_test(function() {
+  var worker = new Worker('#');
+  worker.onmessage = this.step_func(function(e) {
+    assert_equals(e.data, 1);
+    this.done();
+  });
+});
+</script>
+<!--
+*/
+//-->
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/header-user-agent-async.htm b/third_party/WebKit/LayoutTests/external/wpt/xhr/header-user-agent-async.htm
new file mode 100644
index 0000000..8c1d0b6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/header-user-agent-async.htm
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Test that async requests (both OPTIONS preflight and regular) are sent with the User-Agent header</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+</head>
+<body>
+<script type="text/javascript">
+  async_test((test) => {
+    let xhr = new XMLHttpRequest;
+    xhr.open("GET", get_host_info().HTTP_REMOTE_ORIGIN + "/xhr/resources/header-user-agent.py");
+    xhr.setRequestHeader("x-test", "foobar");
+
+    xhr.onerror = test.unreached_func("Unexpected error");
+
+    xhr.onload = test.step_func_done(() => {
+      assert_equals(xhr.responseText, "PASS");
+    });
+
+    xhr.send();
+  }, "Async request has User-Agent header");
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/header-user-agent-sync.htm b/third_party/WebKit/LayoutTests/external/wpt/xhr/header-user-agent-sync.htm
new file mode 100644
index 0000000..d88aac2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/header-user-agent-sync.htm
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Test that sync requests (both OPTIONS preflight and regular) are sent with the User-Agent header</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+</head>
+<body>
+<script type="text/javascript">
+  test(function() {
+    let xhr = new XMLHttpRequest;
+    xhr.open("post", get_host_info().HTTP_REMOTE_ORIGIN + "/xhr/resources/header-user-agent.py", false);
+    xhr.setRequestHeader("x-test", "foobar");
+    xhr.send();
+    assert_equals(xhr.responseText, "PASS");
+  }, "Sync request has User-Agent header");
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/xhr/resources/header-user-agent.py b/third_party/WebKit/LayoutTests/external/wpt/xhr/resources/header-user-agent.py
new file mode 100644
index 0000000..4778de4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/xhr/resources/header-user-agent.py
@@ -0,0 +1,15 @@
+def main(request, response):
+    response.headers.set("Access-Control-Allow-Origin", "*")
+    response.headers.set("Access-Control-Max-Age", 0)
+    response.headers.set('Access-Control-Allow-Headers', "x-test")
+
+    if request.method == "OPTIONS":
+        if not request.headers.get("User-Agent"):
+            response.content = "FAIL: User-Agent header missing in preflight request."
+            response.status = 400
+    else:
+        if request.headers.get("User-Agent"):
+            response.content = "PASS"
+        else:
+            response.content = "FAIL: User-Agent header missing in request"
+            response.status = 400
diff --git a/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-background-size-longhand.html b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-background-size-longhand.html
new file mode 100644
index 0000000..796b415
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-background-size-longhand.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+test(() => {
+    let NegativeBackgroundSize = 2402; // From web_feature.mojom
+
+    let isCounted = () => internals.isUseCounted(document, NegativeBackgroundSize);
+    var div = document.createElement('div');
+
+    div.style = 'background-size: 1px 2px;';
+    div.style = '-webkit-mask-size: -1px -2px;';
+    assert_false(isCounted(),
+                 '-webkit-mask-size should not be counted');
+
+    div.style = 'background-size: 1px -2px;';
+    assert_true(isCounted(),
+                'background-size should be counted');
+}, 'Negative size is use counted for background-size');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-background-size-shorthand.html b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-background-size-shorthand.html
new file mode 100644
index 0000000..e9cdaa4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-background-size-shorthand.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+test(() => {
+    let NegativeBackgroundSize = 2402; // From web_feature.mojom
+
+    let isCounted = () => internals.isUseCounted(document, NegativeBackgroundSize);
+    var div = document.createElement('div');
+
+    div.style = 'background: none, red none 50% 50% / 1px 2px round space local padding-box content-box;';
+    div.style = '-webkit-mask: none -1px -2px, none -3px -4px;';
+    assert_false(isCounted(),
+                 '-webkit-mask should not be counted');
+
+    div.style = 'background: none, red none 50% 50% / -1px 2px round space local padding-box content-box;';
+    assert_true(isCounted(),
+                'background should be counted');
+}, 'Negative size is use counted for background-size in shorthand');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-mask-size-longhand.html b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-mask-size-longhand.html
new file mode 100644
index 0000000..4c77f5b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-mask-size-longhand.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+test(() => {
+    let NegativeMaskSize = 2403; // From web_feature.mojom
+
+    let isCounted = () => internals.isUseCounted(document, NegativeMaskSize);
+    var div = document.createElement('div');
+
+    div.style = '-webkit-mask-size: 1px 2px;';
+    div.style = 'background-size: -1px -2px;';
+    assert_false(isCounted(),
+                 'background-size should not be counted');
+
+    div.style = '-webkit-mask-size: 1px -2px;';
+    assert_true(isCounted(),
+                '-webkit-mask-size should be counted');
+}, 'Negative size is use counted for -webkit-mask-size');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-mask-size-shorthand.html b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-mask-size-shorthand.html
new file mode 100644
index 0000000..8b3b516
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/usecounter-negative-mask-size-shorthand.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+'use strict';
+
+test(() => {
+    let NegativeMaskSize = 2403; // From web_feature.mojom
+
+    let isCounted = () => internals.isUseCounted(document, NegativeMaskSize);
+    var div = document.createElement('div');
+
+    div.style = '-webkit-mask: -3px -4px / 1px 2px;';
+    div.style = 'background: none, red none 50% 50% / -1px -2px round space local padding-box content-box;';
+    assert_false(isCounted(),
+                 'background should not be counted');
+
+    div.style = '-webkit-mask: 3px 4px / -1px 2px;';
+    assert_true(isCounted(),
+                '-webkit-mask should be counted');
+}, 'Negative size is use counted for -webkit-mask-size in shorthand');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/HTMLAreaElement/area-download-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/HTMLAreaElement/area-download-expected.txt
index edf7cec..f95efff 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/HTMLAreaElement/area-download-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/HTMLAreaElement/area-download-expected.txt
@@ -1,2 +1,2 @@
-Downloading URL with suggested filename "foo"
+Download started
  
diff --git a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-AddRemoveTrack.html b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-AddRemoveTrack.html
index 811f19e..f177172 100644
--- a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-AddRemoveTrack.html
+++ b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-AddRemoveTrack.html
@@ -44,25 +44,23 @@
     });
 }, 'addTrack() for a single track and a different stream.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  return createStreams({audio:true, video:false}, 2)
-    .then(function(streams) {
-      let streamA = streams[0];
-      let streamB = streams[1];
-      let track = streamA.getAudioTracks()[0];
-      let exception = null;
-      try {
-        pc.addTrack(track, streamA, streamB);
-      } catch (e) {
-        exception = e;
-      }
-      // The spec supports multiple streams per track but our implementation
-      // doesn't. Fix test when resolving https://crbug.com/webrtc/7932.
-      assert_true(exception != null);
-      assert_equals('NotSupportedError', exception.name);
-    });
-}, 'addTrack() for a single track and two streams throws NotSupportedError.');
+promise_test(async t => {
+  let pc = new RTCPeerConnection({sdpSemantics: "plan-b"});
+  let [streamA, streamB] = await createStreams({audio:true, video:false}, 2);
+  let track = streamA.getAudioTracks()[0];
+  assert_throws('NotSupportedError', () => {
+    pc.addTrack(track, streamA, streamB)
+  });
+}, 'addTrack() for a single track and two streams (Plan B) throws NotSupportedError.');
+
+promise_test(async t => {
+  let pc = new RTCPeerConnection({sdpSemantics: "unified-plan"});
+  let [streamA, streamB] = await createStreams({audio:true, video:false}, 2);
+  let track = streamA.getAudioTracks()[0];
+  let sender =  pc.addTrack(track, streamA, streamB);
+  assert_equals(sender.track, track);
+  assert_array_equals(pc.getLocalStreams(), [ streamA, streamB ]);
+}, 'addTrack() for a single track and two streams (Unified Plan).');
 
 promise_test(function() {
   let pc = new RTCPeerConnection();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-message-from-script-inside-svg-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-message-from-script-inside-svg-expected.txt
new file mode 100644
index 0000000..cb0a27e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-message-from-script-inside-svg-expected.txt
@@ -0,0 +1,4 @@
+Tests that message from script inside svg has correct source location.
+
+svg.html:2 42
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-message-from-script-inside-svg.js b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-message-from-script-inside-svg.js
new file mode 100644
index 0000000..35e4b0b09
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-message-from-script-inside-svg.js
@@ -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.
+
+(async function() {
+  TestRunner.addResult(
+    `Tests that message from script inside svg has correct source location.\n`);
+  await TestRunner.loadModule('console_test_runner');
+  await TestRunner.showPanel('console');
+  await TestRunner.navigatePromise('resources/svg.html');
+  ConsoleTestRunner.dumpConsoleMessages();
+  TestRunner.completeTest();
+})();
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/console/resources/svg.html b/third_party/WebKit/LayoutTests/http/tests/devtools/console/resources/svg.html
new file mode 100644
index 0000000..96329e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/console/resources/svg.html
@@ -0,0 +1,3 @@
+<svg>
+  <script>console.log(42);</script>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js b/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js
index f6796f1..ff36ed9 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js
@@ -33,7 +33,6 @@
       return;
 
     if (event.name === TimelineModel.TimelineModel.RecordType.TracingStartedInPage) {
-      TestRunner.assertEquals(PerformanceTestRunner.timelineModel()._sessionId, event.args['sessionId'] || event.args['data']['sessionId']);
       TestRunner.addResult('Got DevTools metadata event: ' + event.name);
       frameId = event.args['data']['frames'][0]['frame'];
     } else if (event.name === TimelineModel.TimelineModel.RecordType.SetLayerTreeId) {
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-blob-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-blob-expected.txt
index 7f241e4a..4062599 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-blob-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-blob-expected.txt
@@ -1,4 +1,4 @@
-Downloading URL with suggested filename "foo.pdf"
+Download started
 Tests that a suggested filename on a download attribute is allowed if the link is a blob URL.
 
 The suggested filename at the top should be foo.pdf.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-data-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-data-expected.txt
index d7ff96c..2514ead8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-data-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-data-expected.txt
@@ -1,4 +1,4 @@
-Downloading URL with suggested filename "foo.pdf"
+Download started
 Tests that a suggested filename on a download attribute is allowed if the link is a data URL.
 
 The suggested filename at the top should be foo.pdf.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-sameorigin-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-sameorigin-expected.txt
index 2ec4ee2..7eab87a1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-sameorigin-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-allow-sameorigin-expected.txt
@@ -1,4 +1,4 @@
-Downloading URL with suggested filename "foo.pdf"
+Download started
 Tests that a suggested filename on a download attribute is allowed if the link is in the same origin.
 
 The suggested filename at the top should be foo.pdf.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/bluetooth-on-insecure-origin.html b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/bluetooth-on-insecure-origin.html
index 18a902e..96aeec8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/bluetooth-on-insecure-origin.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/bluetooth-on-insecure-origin.html
@@ -7,11 +7,8 @@
 if (window.location.origin != get_host_info().UNAUTHENTICATED_ORIGIN) {
     window.location = get_host_info().UNAUTHENTICATED_ORIGIN + window.location.pathname;
 } else {
-    promise_test(function(test) {
-        return promise_rejects(test, 'SecurityError',
-          navigator.bluetooth.requestDevice({
-            filters: [{services: ['generic_access']}]
-          }));
-    }, "Requires secure context before user gesture");
+    test(t => {
+        assert_false('requestDevice' in navigator.bluetooth, 'navigator.bluetooth.requestDevice should not be present');
+    }, 'navigator.bluetooth.requestDevice requires a secure context');
 }
 </script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/script-tag-inside-svg-tag4-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/script-tag-inside-svg-tag4-expected.txt
index 3db4e687..49db853 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/script-tag-inside-svg-tag4-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/xssAuditor/script-tag-inside-svg-tag4-expected.txt
@@ -1,3 +1,3 @@
 CONSOLE ERROR: line 4: The XSS Auditor refused to execute a script in 'http://localhost:8000/security/xssAuditor/resources/echo-intertag.pl?q=%3Csvg%3E%3Cscript%3E/%3C1/%3Ealert(0)%3C/script%3E%3C/svg%3E' because its source code was found within the request. The server sent an 'X-XSS-Protection' header requesting this behavior.
-CONSOLE ERROR: line -1: Uncaught SyntaxError: Invalid regular expression: missing /
+CONSOLE ERROR: line 4: Uncaught SyntaxError: Invalid regular expression: missing /
  Ensures regexps are handled in even with nested script blocks.
diff --git a/third_party/WebKit/LayoutTests/media/content/counting.webm b/third_party/WebKit/LayoutTests/media/content/counting.webm
new file mode 100644
index 0000000..de2abba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/content/counting.webm
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/media/video-canvas.html b/third_party/WebKit/LayoutTests/media/video-canvas.html
index f3b1c88..550ddd0a 100644
--- a/third_party/WebKit/LayoutTests/media/video-canvas.html
+++ b/third_party/WebKit/LayoutTests/media/video-canvas.html
@@ -57,6 +57,6 @@
             video.currentTime = results.values[results.current].time;
     }
 
-    video.src = "content/counting.ogv";
+    video.src = "content/counting.webm";
 });
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/custom/use-events-crash-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/custom/use-events-crash-expected.txt
index 7eeda58..9303f95 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/custom/use-events-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/custom/use-events-crash-expected.txt
@@ -18,4 +18,4 @@
       LayoutSVGContainer {g} at (0,0) size 40x40 [transform={m=((1.00,0.00)(0.00,1.00)) t=(350.00,25.00)}]
         LayoutSVGContainer {use} at (0,0) size 40x40
           LayoutSVGRect {rect} at (0,0) size 40x40 [fill={[type=SOLID] [color=#00FF00]}] [x=0.00] [y=0.00] [width=40.00] [height=40.00]
-caret: position 47 of child 0 {#text} of child 1 {text} of child 1 {g} of child 3 {g} of child 3 {g} of child 1 {svg} of document
+caret: position 47 of child 0 {#text} of child 1 {text} of child 3 {g} of child 3 {g} of child 3 {g} of child 1 {svg} of document
diff --git a/third_party/WebKit/LayoutTests/svg/custom/use-events-crash.svg b/third_party/WebKit/LayoutTests/svg/custom/use-events-crash.svg
index 311f5913..ab3d105 100644
--- a/third_party/WebKit/LayoutTests/svg/custom/use-events-crash.svg
+++ b/third_party/WebKit/LayoutTests/svg/custom/use-events-crash.svg
@@ -6,7 +6,7 @@
 function test()
 {
     if (window.eventSender) {
-	eventSender.mouseMoveTo(370, 45);
+	eventSender.mouseMoveTo(370, 60);
 	eventSender.contextClick();
     }
 }
diff --git a/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.cpp b/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.cpp
index 6c29bad8..e263ccf69 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.cpp
+++ b/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.cpp
@@ -792,8 +792,28 @@
                                                 CSSValueLuminance>(range);
 }
 
+CSSPrimitiveValue* ConsumeLengthOrPercentCountNegative(
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    WTF::Optional<WebFeature> negativeSize) {
+  CSSPrimitiveValue* result =
+      ConsumeLengthOrPercent(range, context.Mode(), kValueRangeNonNegative,
+                             CSSPropertyParserHelpers::UnitlessQuirk::kForbid);
+  if (result || !negativeSize)
+    return result;
+
+  result =
+      ConsumeLengthOrPercent(range, context.Mode(), kValueRangeAll,
+                             CSSPropertyParserHelpers::UnitlessQuirk::kForbid);
+
+  if (result)
+    context.Count(*negativeSize);
+  return result;
+}
+
 CSSValue* ConsumeBackgroundSize(CSSParserTokenRange& range,
-                                CSSParserMode css_parser_mode,
+                                const CSSParserContext& context,
+                                WTF::Optional<WebFeature> negativeSize,
                                 ParsingStyle parsing_style) {
   if (CSSPropertyParserHelpers::IdentMatches<CSSValueContain, CSSValueCover>(
           range.Peek().Id())) {
@@ -803,9 +823,8 @@
   CSSValue* horizontal =
       CSSPropertyParserHelpers::ConsumeIdent<CSSValueAuto>(range);
   if (!horizontal) {
-    horizontal = CSSPropertyParserHelpers::ConsumeLengthOrPercent(
-        range, css_parser_mode, kValueRangeAll,
-        CSSPropertyParserHelpers::UnitlessQuirk::kForbid);
+    horizontal =
+        ConsumeLengthOrPercentCountNegative(range, context, negativeSize);
   }
 
   CSSValue* vertical = nullptr;
@@ -813,9 +832,8 @@
     if (range.Peek().Id() == CSSValueAuto) {  // `auto' is the default
       range.ConsumeIncludingWhitespace();
     } else {
-      vertical = CSSPropertyParserHelpers::ConsumeLengthOrPercent(
-          range, css_parser_mode, kValueRangeAll,
-          CSSPropertyParserHelpers::UnitlessQuirk::kForbid);
+      vertical =
+          ConsumeLengthOrPercentCountNegative(range, context, negativeSize);
     }
   } else if (parsing_style == ParsingStyle::kLegacy) {
     // Legacy syntax: "-webkit-background-size: 10px" is equivalent to
@@ -870,12 +888,12 @@
       ConsumeBackgroundBox, range);
 }
 
-CSSValue* ParseBackgroundOrMaskSize(
-    CSSParserTokenRange& range,
-    const CSSParserContext& context,
-    const CSSParserLocalContext& local_context) {
+CSSValue* ParseBackgroundOrMaskSize(CSSParserTokenRange& range,
+                                    const CSSParserContext& context,
+                                    const CSSParserLocalContext& local_context,
+                                    WTF::Optional<WebFeature> negativeSize) {
   return CSSPropertyParserHelpers::ConsumeCommaSeparatedList(
-      ConsumeBackgroundSize, range, context.Mode(),
+      ConsumeBackgroundSize, range, context, negativeSize,
       local_context.UseAliasParsing() ? ParsingStyle::kLegacy
                                       : ParsingStyle::kNotLegacy);
 }
@@ -904,8 +922,12 @@
       return ConsumePositionLonghand<CSSValueTop, CSSValueBottom>(
           range, context.Mode());
     case CSSPropertyBackgroundSize:
+      return ConsumeBackgroundSize(range, context,
+                                   WebFeature::kNegativeBackgroundSize,
+                                   ParsingStyle::kNotLegacy);
     case CSSPropertyWebkitMaskSize:
-      return ConsumeBackgroundSize(range, context.Mode(),
+      return ConsumeBackgroundSize(range, context,
+                                   WebFeature::kNegativeMaskSize,
                                    ParsingStyle::kNotLegacy);
     case CSSPropertyBackgroundColor:
       return CSSPropertyParserHelpers::ConsumeColor(range, context.Mode());
@@ -972,8 +994,12 @@
                    property.IDEquals(CSSPropertyWebkitMaskSize)) {
           if (!CSSPropertyParserHelpers::ConsumeSlashIncludingWhitespace(range))
             continue;
-          value = ConsumeBackgroundSize(range, context.Mode(),
-                                        ParsingStyle::kNotLegacy);
+          value =
+              ConsumeBackgroundSize(range, context,
+                                    property.IDEquals(CSSPropertyBackgroundSize)
+                                        ? WebFeature::kNegativeBackgroundSize
+                                        : WebFeature::kNegativeMaskSize,
+                                    ParsingStyle::kNotLegacy);
           if (!value ||
               !parsed_longhand[i - 1])  // Position must have been
                                         // parsed in the current layer.
diff --git a/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.h b/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.h
index a5c6606..02a9f52 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.h
+++ b/third_party/WebKit/Source/core/css/properties/CSSParsingUtils.h
@@ -77,9 +77,6 @@
 CSSValue* ConsumeBackgroundBox(CSSParserTokenRange&);
 CSSValue* ConsumeBackgroundComposite(CSSParserTokenRange&);
 CSSValue* ConsumeMaskSourceType(CSSParserTokenRange&);
-CSSValue* ConsumeBackgroundSize(CSSParserTokenRange&,
-                                CSSParserMode,
-                                ParsingStyle);
 bool ConsumeBackgroundPosition(CSSParserTokenRange&,
                                const CSSParserContext&,
                                CSSPropertyParserHelpers::UnitlessQuirk,
@@ -90,7 +87,8 @@
                              const CSSParserLocalContext&);
 CSSValue* ParseBackgroundOrMaskSize(CSSParserTokenRange&,
                                     const CSSParserContext&,
-                                    const CSSParserLocalContext&);
+                                    const CSSParserLocalContext&,
+                                    WTF::Optional<WebFeature> negativeSize);
 bool ParseBackgroundOrMask(bool,
                            CSSParserTokenRange&,
                            const CSSParserContext&,
diff --git a/third_party/WebKit/Source/core/css/properties/longhands/BackgroundSizeCustom.cpp b/third_party/WebKit/Source/core/css/properties/longhands/BackgroundSizeCustom.cpp
index 1f65e5d..5cdff7a 100644
--- a/third_party/WebKit/Source/core/css/properties/longhands/BackgroundSizeCustom.cpp
+++ b/third_party/WebKit/Source/core/css/properties/longhands/BackgroundSizeCustom.cpp
@@ -6,6 +6,7 @@
 
 #include "core/css/properties/CSSParsingUtils.h"
 #include "core/css/properties/ComputedStyleUtils.h"
+#include "core/frame/WebFeature.h"
 #include "core/style/ComputedStyle.h"
 
 namespace blink {
@@ -15,8 +16,8 @@
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     const CSSParserLocalContext& local_context) const {
-  return CSSParsingUtils::ParseBackgroundOrMaskSize(range, context,
-                                                    local_context);
+  return CSSParsingUtils::ParseBackgroundOrMaskSize(
+      range, context, local_context, WebFeature::kNegativeBackgroundSize);
 }
 
 const CSSValue* BackgroundSize::CSSValueFromComputedStyleInternal(
diff --git a/third_party/WebKit/Source/core/css/properties/longhands/WebkitMaskSizeCustom.cpp b/third_party/WebKit/Source/core/css/properties/longhands/WebkitMaskSizeCustom.cpp
index 9fd89cb7..6d78891 100644
--- a/third_party/WebKit/Source/core/css/properties/longhands/WebkitMaskSizeCustom.cpp
+++ b/third_party/WebKit/Source/core/css/properties/longhands/WebkitMaskSizeCustom.cpp
@@ -6,6 +6,7 @@
 
 #include "core/css/properties/CSSParsingUtils.h"
 #include "core/css/properties/ComputedStyleUtils.h"
+#include "core/frame/WebFeature.h"
 #include "core/style/ComputedStyle.h"
 
 namespace blink {
@@ -15,8 +16,8 @@
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     const CSSParserLocalContext& local_context) const {
-  return CSSParsingUtils::ParseBackgroundOrMaskSize(range, context,
-                                                    local_context);
+  return CSSParsingUtils::ParseBackgroundOrMaskSize(
+      range, context, local_context, WebFeature::kNegativeMaskSize);
 }
 
 const CSSValue* WebkitMaskSize::CSSValueFromComputedStyleInternal(
diff --git a/third_party/WebKit/Source/core/exported/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/core/exported/LocalFrameClientImpl.cpp
index 836f371d..3eb8ef9 100644
--- a/third_party/WebKit/Source/core/exported/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/LocalFrameClientImpl.cpp
@@ -394,7 +394,7 @@
         WebHistoryItem(item), static_cast<WebHistoryCommitType>(commit_type),
         content_initiated);
   }
-  virtual_time_pauser_.PauseVirtualTime(false);
+  virtual_time_pauser_.UnpauseVirtualTime();
 }
 
 void LocalFrameClientImpl::DispatchWillCommitProvisionalLoad() {
@@ -412,7 +412,7 @@
   }
   if (WebDevToolsAgentImpl* dev_tools = DevToolsAgent())
     dev_tools->DidStartProvisionalLoad(web_frame_->GetFrame());
-  virtual_time_pauser_.PauseVirtualTime(true);
+  virtual_time_pauser_.PauseVirtualTime();
 }
 
 void LocalFrameClientImpl::DispatchDidReceiveTitle(const String& title) {
@@ -444,14 +444,14 @@
   if (WebDevToolsAgentImpl* dev_tools = DevToolsAgent())
     dev_tools->DidCommitLoadForLocalFrame(web_frame_->GetFrame());
 
-  virtual_time_pauser_.PauseVirtualTime(false);
+  virtual_time_pauser_.UnpauseVirtualTime();
 }
 
 void LocalFrameClientImpl::DispatchDidFailProvisionalLoad(
     const ResourceError& error,
     HistoryCommitType commit_type) {
   web_frame_->DidFail(error, true, commit_type);
-  virtual_time_pauser_.PauseVirtualTime(false);
+  virtual_time_pauser_.UnpauseVirtualTime();
 }
 
 void LocalFrameClientImpl::DispatchDidFailLoad(const ResourceError& error,
diff --git a/third_party/WebKit/Source/core/exported/WebFrame.cpp b/third_party/WebKit/Source/core/exported/WebFrame.cpp
index a641f8d0..58487bb2 100644
--- a/third_party/WebKit/Source/core/exported/WebFrame.cpp
+++ b/third_party/WebKit/Source/core/exported/WebFrame.cpp
@@ -112,7 +112,10 @@
             local_frame.View());
       }
     } else {
-      local_frame.GetPage()->SetMainFrame(&local_frame);
+      Page* other_page = local_frame.GetPage();
+      other_page->SetMainFrame(&local_frame);
+      if (PageScheduler* page_scheduler = other_page->GetPageScheduler())
+        page_scheduler->SetIsMainFrameLocal(true);
       // This trace event is needed to detect the main frame of the
       // renderer in telemetry metrics. See crbug.com/692112#c11.
       TRACE_EVENT_INSTANT1("loading", "markAsMainFrame",
diff --git a/third_party/WebKit/Source/core/fileapi/PublicURLManager.cpp b/third_party/WebKit/Source/core/fileapi/PublicURLManager.cpp
index 8a65267..129a3e00 100644
--- a/third_party/WebKit/Source/core/fileapi/PublicURLManager.cpp
+++ b/third_party/WebKit/Source/core/fileapi/PublicURLManager.cpp
@@ -72,6 +72,7 @@
   // that the origin can be retrieved when doing security origin check.
   //
   // See the definition of the origin of a Blob URL in the File API spec.
+  DCHECK(!url.HasFragmentIdentifier());
   if (origin && BlobURL::GetOrigin(url) == "null")
     OriginMap()->insert(url.GetString(), origin);
 }
@@ -86,8 +87,11 @@
 }
 
 SecurityOrigin* BlobOriginMap::GetOrigin(const KURL& url) {
-  if (url.ProtocolIs("blob"))
-    return OriginMap()->at(url.GetString());
+  if (url.ProtocolIs("blob")) {
+    KURL url_without_fragment = url;
+    url_without_fragment.RemoveFragmentIdentifier();
+    return OriginMap()->at(url_without_fragment.GetString());
+  }
   return nullptr;
 }
 
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index af0e28f..7820504 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -168,6 +168,7 @@
   }
   PageScheduler* GetPageScheduler() const override { return nullptr; }
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const String&,
       WebScopedVirtualTimePauser::VirtualTaskDuration) {
     return WebScopedVirtualTimePauser();
   }
@@ -195,6 +196,9 @@
       client, page, owner,
       interface_registry ? interface_registry
                          : InterfaceRegistry::GetEmptyInterfaceRegistry());
+  PageScheduler* page_scheduler = page.GetPageScheduler();
+  if (frame->IsMainFrame() && page_scheduler)
+    page_scheduler->SetIsMainFrameLocal(true);
   probe::frameAttachedToParent(frame);
   return frame;
 }
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
index bac97e7..435099a 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
@@ -14,6 +14,7 @@
 #include "core/layout/LayoutEmbeddedContent.h"
 #include "core/loader/FrameLoadRequest.h"
 #include "core/loader/FrameLoader.h"
+#include "core/page/Page.h"
 #include "core/paint/PaintLayer.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/loader/fetch/ResourceRequest.h"
@@ -36,7 +37,11 @@
 RemoteFrame* RemoteFrame::Create(RemoteFrameClient* client,
                                  Page& page,
                                  FrameOwner* owner) {
-  return new RemoteFrame(client, page, owner);
+  RemoteFrame* frame = new RemoteFrame(client, page, owner);
+  PageScheduler* page_scheduler = page.GetPageScheduler();
+  if (frame->IsMainFrame() && page_scheduler)
+    page_scheduler->SetIsMainFrameLocal(false);
+  return frame;
 }
 
 RemoteFrame::~RemoteFrame() {
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
index d550e49..616482d 100644
--- a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
@@ -1791,6 +1791,7 @@
 
   local_frame_client_->SetVirtualTimePauser(
       frame_ ? frame_->GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
+                   "WebLocalFrameImpl",
                    WebScopedVirtualTimePauser::VirtualTaskDuration::kInstant)
              : WebScopedVirtualTimePauser());
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
index 8b91b84..30d677f00 100644
--- a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
@@ -367,8 +367,16 @@
               : WebFeature::
                     kHTMLAnchorElementDownloadInSandboxWithoutUserGesture);
     }
+    // TODO(jochen): Only set the suggested filename for URLs we can request.
     request.SetSuggestedFilename(
         static_cast<String>(FastGetAttribute(downloadAttr)));
+    if (GetDocument().GetSecurityOrigin()->CanReadContent(completed_url)) {
+      // TODO(jochen): Handle cross origin server redirects.
+      request.SetRequestContext(WebURLRequest::kRequestContextDownload);
+      request.SetRequestorOrigin(SecurityOrigin::Create(GetDocument().Url()));
+      frame->Client()->DownloadURL(request);
+      return;
+    }
   }
   request.SetRequestContext(WebURLRequest::kRequestContextHyperlink);
   FrameLoadRequest frame_request(&GetDocument(), request,
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
index c23facc..05ceddc0 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLTreeBuilder.cpp
@@ -2678,6 +2678,8 @@
         tree_.OpenElements()->PopUntilForeignContentScopeMarker();
         ProcessStartTag(token);
         return;
+      } else if (token->GetName() == scriptTag) {
+        script_to_process_start_position_ = parser_->GetTextPosition();
       }
       const AtomicString& current_namespace =
           adjusted_current_node->NamespaceURI();
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
index 2ad1ac9..94c800a 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
@@ -1256,9 +1256,11 @@
 
 std::unique_ptr<TracedValue> InspectorTracingSessionIdForWorkerEvent::Data(
     LocalFrame* frame,
+    const String& url,
     WorkerThread* worker_thread) {
   std::unique_ptr<TracedValue> value = TracedValue::Create();
   value->SetString("frame", IdentifiersFactory::FrameId(frame));
+  value->SetString("url", url);
   value->SetString("workerId", IdentifiersFactory::IdFromToken(
                                    worker_thread->GetDevToolsWorkerToken()));
   value->SetDouble("workerThreadId", worker_thread->GetPlatformThreadId());
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
index 57cdb17..5e7a342 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
@@ -449,7 +449,9 @@
 }
 
 namespace InspectorTracingSessionIdForWorkerEvent {
-std::unique_ptr<TracedValue> Data(LocalFrame*, WorkerThread*);
+std::unique_ptr<TracedValue> Data(LocalFrame*,
+                                  const String& url,
+                                  WorkerThread*);
 }
 
 namespace InspectorTracingStartedInFrame {
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTracingAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorTracingAgent.cpp
index 2cbaef1..08361c4 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTracingAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorTracingAgent.cpp
@@ -69,7 +69,7 @@
                          "TracingSessionIdForWorker", TRACE_EVENT_SCOPE_THREAD,
                          "data",
                          InspectorTracingSessionIdForWorkerEvent::Data(
-                             frame, proxy->GetWorkerThread()));
+                             frame, proxy->Url(), proxy->GetWorkerThread()));
   }
 }
 
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 1277ba1..02c7497 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -282,7 +282,9 @@
   // remote frames.
   // FIXME: Unfortunately we can't assert on this at the moment, because this
   // is called in the base constructor for both LocalFrame and RemoteFrame,
-  // when the vtables for the derived classes have not yet been setup.
+  // when the vtables for the derived classes have not yet been setup. Once this
+  // is fixed, also call  page_scheduler_->SetIsMainFrameLocal() from here
+  // instead of from the callers of this method.
   main_frame_ = main_frame;
 }
 
@@ -787,6 +789,8 @@
 
 void Page::SetPageScheduler(std::unique_ptr<PageScheduler> page_scheduler) {
   page_scheduler_ = std::move(page_scheduler);
+  // The scheduler should be set before the main frame.
+  DCHECK(!main_frame_);
 }
 
 void Page::ReportIntervention(const String& text) {
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index 7e554497..39134a1 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -33,7 +33,6 @@
 #include "core/frame/LocalFrame.h"
 #include "core/frame/SettingsDelegate.h"
 #include "core/frame/UseCounter.h"
-#include "core/page/Page.h"
 #include "core/page/PageAnimator.h"
 #include "core/page/PageLifecycleState.h"
 #include "core/page/PageVisibilityNotifier.h"
@@ -148,6 +147,9 @@
   // PluginsChangedObservers.
   static void ResetPluginData();
 
+  // When this method is called, page_scheduler_->SetIsMainFrameLocal should
+  // also be called to update accordingly.
+  // TODO(npm): update the |page_scheduler_| directly in this method.
   void SetMainFrame(Frame*);
   Frame* MainFrame() const { return main_frame_; }
   // Escape hatch for existing code that assumes that the root frame is
diff --git a/third_party/WebKit/Source/core/paint/BackgroundImageGeometry.cpp b/third_party/WebKit/Source/core/paint/BackgroundImageGeometry.cpp
index 6b525a9..6a101f8 100644
--- a/third_party/WebKit/Source/core/paint/BackgroundImageGeometry.cpp
+++ b/third_party/WebKit/Source/core/paint/BackgroundImageGeometry.cpp
@@ -529,28 +529,33 @@
   //     respective box, but for padding-box we also try to force alignment
   //     with the inner border.
   //
-  //   * for border-box, we can modufy individual edges iff the border fully
+  //   * for border-box, we can modify individual edges iff the border fully
   //     obscures the background.
   //
-  // It it unsafe to derive dest from border information if the layer is not
-  // painted as part of a regular background phase (e.g. paint_phase == kMask)
-  // or in the presence of non-SrcOver compositing.
-  // Also, if coordinate_offset_by_paint_rect_ is set, we're dealing with a
-  // LayoutView - for which dest_rect is overflowing (expanded to cover the
-  // whole canvas).
-  // Finally, table cells using the table background should not adjust
-  // based on borders.
-  bool allow_border_derived_adjustment =
-      ShouldPaintSelfBlockBackground(paint_phase) &&
-      fill_layer.Composite() == CompositeOperator::kCompositeSourceOver &&
-      !coordinate_offset_by_paint_rect_ && !painting_table_cell_;
+  // It it unsafe to derive dest from border information when any of the
+  // following is true:
+  // * the layer is not painted as part of a regular background phase
+  //  (e.g.paint_phase == kMask)
+  // * non-SrcOver compositing is active
+  // * coordinate_offset_by_paint_rect_ is set, meaning we're dealing with a
+  //   LayoutView - for which dest_rect is overflowing (expanded to cover
+  //   the whole canvas).
+  // * We are painting table cells using the table background
+  // * There is a border image, because it may not be opaque or may be outset.
+  bool disallow_border_derived_adjustment =
+      !ShouldPaintSelfBlockBackground(paint_phase) ||
+      fill_layer.Composite() != CompositeOperator::kCompositeSourceOver ||
+      coordinate_offset_by_paint_rect_ || painting_table_cell_ ||
+      positioning_box_.StyleRef().BorderImage().GetImage();
   switch (fill_layer.Clip()) {
     case EFillBox::kContent:
       dest_adjust += positioning_box_.PaddingOutsets();
       dest_adjust += positioning_box_.BorderBoxOutsets();
       break;
     case EFillBox::kPadding:
-      if (allow_border_derived_adjustment) {
+      if (disallow_border_derived_adjustment) {
+        dest_adjust = positioning_box_.BorderBoxOutsets();
+      } else {
         FloatRect inner_border_rect =
             positioning_box_.StyleRef()
                 .GetRoundedInnerBorderFor(positioning_rect)
@@ -561,12 +566,10 @@
                              LayoutUnit(inner_border_rect.MaxX()));
         dest_adjust.SetBottom(dest_rect.MaxY() -
                               LayoutUnit(inner_border_rect.MaxY()));
-      } else {
-        dest_adjust = positioning_box_.BorderBoxOutsets();
       }
       break;
     case EFillBox::kBorder: {
-      if (!allow_border_derived_adjustment)
+      if (disallow_border_derived_adjustment)
         break;
 
       BorderEdge edges[4];
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
index 531a99c..6a75c00 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
@@ -1311,7 +1311,7 @@
 
   HTMLFrameOwnerElement* owner = ToHTMLFrameOwnerElement(frame->Owner());
   LayoutObject* ownerLayoutObject = owner->GetLayoutObject();
-  if (!ownerLayoutObject->HasLayer())
+  if (!ownerLayoutObject || !ownerLayoutObject->HasLayer())
     return LayoutPoint();
 
   PaintLayer* ownerLayer = ToLayoutBoxModelObject(ownerLayoutObject)->Layer();
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositingLayerPropertyUpdater.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositingLayerPropertyUpdater.cpp
index 96fe816c..14ff78f 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositingLayerPropertyUpdater.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositingLayerPropertyUpdater.cpp
@@ -31,7 +31,8 @@
   LayoutPoint layout_snapped_paint_offset =
       fragment_data.PaintOffset() - mapping->SubpixelAccumulation();
   IntPoint snapped_paint_offset = RoundedIntPoint(layout_snapped_paint_offset);
-  DCHECK(layout_snapped_paint_offset == snapped_paint_offset);
+  // TODO(crbug.com/816490): Re-enable this check once it is fixed.
+  // DCHECK(layout_snapped_paint_offset == snapped_paint_offset);
 
   Optional<PropertyTreeState> container_layer_state;
   auto SetContainerLayerState =
diff --git a/third_party/WebKit/Source/core/script/PendingScript.cpp b/third_party/WebKit/Source/core/script/PendingScript.cpp
index 7da7daf..447bf30 100644
--- a/third_party/WebKit/Source/core/script/PendingScript.cpp
+++ b/third_party/WebKit/Source/core/script/PendingScript.cpp
@@ -42,6 +42,7 @@
       .GetFrame()
       ->GetFrameScheduler()
       ->CreateWebScopedVirtualTimePauser(
+          "PendingScript",
           WebScopedVirtualTimePauser::VirtualTaskDuration::kInstant);
 }
 }  // namespace
@@ -82,7 +83,7 @@
   if (IsReady()) {
     client_->PendingScriptFinished(this);
   } else {
-    virtual_time_pauser_.PauseVirtualTime(true);
+    virtual_time_pauser_.PauseVirtualTime();
   }
 }
 
@@ -92,7 +93,7 @@
   CheckState();
   DCHECK(IsExternalOrModule());
   client_ = nullptr;
-  virtual_time_pauser_.PauseVirtualTime(false);
+  virtual_time_pauser_.UnpauseVirtualTime();
 }
 
 ScriptElementBase* PendingScript::GetElement() const {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceModel.js b/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceModel.js
index 2a9510e..d8d80283 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/PerformanceModel.js
@@ -63,7 +63,14 @@
     if (inputEvents || animationEvents)
       this._irModel.populate(inputEvents || [], animationEvents || []);
 
-    this._frameModel.addTraceEvents(this._mainTarget, this._timelineModel.inspectedTargetEvents());
+    const mainTracks = this._timelineModel.tracks().filter(
+        track => track.type === TimelineModel.TimelineModel.TrackType.MainThread && track.forMainFrame &&
+            track.events.length);
+    const threadData = mainTracks.map(track => {
+      const event = track.events[0];
+      return {thread: event.thread, time: event.startTime};
+    });
+    this._frameModel.addTraceEvents(this._mainTarget, this._timelineModel.inspectedTargetEvents(), threadData);
 
     for (const entry of this._extensionTracingModels) {
       entry.model.adjustTime(
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js
index 991ec2e..384c83d 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js
@@ -240,7 +240,7 @@
     };
 
     const tracks = this._model.tracks().slice();
-    tracks.sort((a, b) => weight(a) - weight(b));
+    tracks.stableSort((a, b) => weight(a) - weight(b));
     let rasterCount = 0;
     for (const track of tracks) {
       switch (track.type) {
@@ -265,19 +265,19 @@
         case TimelineModel.TimelineModel.TrackType.MainThread:
           if (track.forMainFrame) {
             const group = this._appendSyncEvents(
-                track, track.events, ls`Main`, this._headerLevel1, eventEntryType, true /* selectable */);
-            if (group) {
-              group.expanded = true;
+                track, track.events, track.url ? ls`Main \u2014 ${track.url}` : ls`Main`, this._headerLevel1,
+                eventEntryType, true /* selectable */);
+            if (group)
               this._timelineData.selectedGroup = group;
-            }
           } else {
             this._appendSyncEvents(
-                track, track.events, track.name, this._headerLevel2, eventEntryType, true /* selectable */);
+                track, track.events, track.url ? ls`Frame \u2014 ${track.url}` : ls`Subframe`, this._headerLevel1,
+                eventEntryType, true /* selectable */);
           }
           break;
         case TimelineModel.TimelineModel.TrackType.Worker:
           this._appendSyncEvents(
-              track, track.events, track.workerUrl ? ls`Worker ${track.workerUrl}` : ls`Dedicated worker`,
+              track, track.events, track.url ? ls`Worker \u2014 ${track.url}` : ls`Dedicated Worker`,
               this._headerLevel1, eventEntryType, true /* selectable */);
           break;
         case TimelineModel.TimelineModel.TrackType.Raster:
@@ -300,6 +300,8 @@
           break;
       }
     }
+    if (this._timelineData.selectedGroup)
+      this._timelineData.selectedGroup.expanded = true;
 
     for (let extensionIndex = 0; extensionIndex < this._extensionInfo.length; extensionIndex++)
       this._innerAppendExtensionEvents(extensionIndex);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
index 2469634..577ea47a 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
@@ -239,11 +239,18 @@
   /**
    * @param {?SDK.Target} target
    * @param {!Array.<!SDK.TracingModel.Event>} events
+   * @param {!Array<!{thread: !SDK.TracingModel.Thread, time: number}>} threadData
    */
-  addTraceEvents(target, events) {
+  addTraceEvents(target, events, threadData) {
     this._target = target;
-    for (let i = 0; i < events.length; ++i)
+    let j = 0;
+    this._currentProcessMainThread = threadData.length && threadData[0].thread || null;
+    for (let i = 0; i < events.length; ++i) {
+      while (j + 1 < threadData.length && threadData[j + 1].time <= events[i].startTime)
+        this._currentProcessMainThread = threadData[++j].thread;
       this._addTraceEvent(events[i]);
+    }
+    this._currentProcessMainThread = null;
   }
 
   /**
@@ -256,8 +263,6 @@
 
     if (event.name === eventNames.SetLayerTreeId) {
       this._layerTreeId = event.args['layerTreeId'] || event.args['data']['layerTreeId'];
-    } else if (event.name === eventNames.TracingStartedInPage) {
-      this._currentProcessMainThread = event.thread;
     } else if (
         event.phase === SDK.TracingModel.Phase.SnapshotObject && event.name === eventNames.LayerTreeHostImplSnapshot &&
         parseInt(event.id, 0) === this._layerTreeId) {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
index d72d11b..675727a2 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
@@ -136,7 +136,8 @@
    * @return {string}
    */
   static eventFrameId(event) {
-    return TimelineModel.TimelineModel.globalEventId(event, 'frame');
+    const data = event.args['data'] || event.args['beginData'];
+    return (data && data['frame']) || '';
   }
 
   /**
@@ -168,53 +169,149 @@
     this._minimumRecordTime = tracingModel.minimumRecordTime();
     this._maximumRecordTime = tracingModel.maximumRecordTime();
 
-    const metadataEvents = this._processMetadataEvents(tracingModel, !!produceTraceStartedInPage);
-    if (Runtime.experiments.isEnabled('timelineShowAllProcesses')) {
-      const lastPageMetaEvent = metadataEvents.page.peekLast();
-      for (const process of tracingModel.sortedProcesses()) {
-        for (const thread of process.sortedThreads())
-          this._processThreadEvents(tracingModel, 0, Infinity, thread, thread === lastPageMetaEvent.thread, null);
-      }
+    this._processSyncBrowserEvents(tracingModel);
+    if (this._browserFrameTracking) {
+      this._processThreadsForBrowserFrames(tracingModel);
     } else {
-      let startTime = 0;
-      for (let i = 0, length = metadataEvents.page.length; i < length; i++) {
-        const metaEvent = metadataEvents.page[i];
-        const process = metaEvent.thread.process();
-        const endTime = i + 1 < length ? metadataEvents.page[i + 1].startTime : Infinity;
-        this._legacyCurrentPage = metaEvent.args['data'] && metaEvent.args['data']['page'];
-        for (const thread of process.sortedThreads()) {
-          let workerUrl = null;
-          if (thread.name() === TimelineModel.TimelineModel.WorkerThreadName ||
-              thread.name() === TimelineModel.TimelineModel.WorkerThreadNameLegacy) {
-            const workerMetaEvent = metadataEvents.workers.find(e => {
-              if (e.args['data']['workerThreadId'] !== thread.id())
-                return false;
-              // This is to support old traces.
-              if (e.args['data']['sessionId'] === this._sessionId)
-                return true;
-              return !!this._pageFrames.get(TimelineModel.TimelineModel.eventFrameId(e));
-            });
-            if (!workerMetaEvent)
-              continue;
-            const workerId = workerMetaEvent.args['data']['workerId'];
-            if (workerId)
-              this._workerIdByThread.set(thread, workerId);
-            workerUrl = workerMetaEvent.args['data']['url'] || '';
-          }
-          this._processThreadEvents(tracingModel, startTime, endTime, thread, thread === metaEvent.thread, workerUrl);
-        }
-        startTime = endTime;
-      }
+      const metadataEvents = this._processMetadataEvents(tracingModel, !!produceTraceStartedInPage);
+      if (Runtime.experiments.isEnabled('timelineShowAllProcesses'))
+        this._processAllThreads(tracingModel, /** @type {!SDK.TracingModel.Event} */ (metadataEvents.page.peekLast()));
+      else
+        this._processMetadataAndThreads(tracingModel, metadataEvents);
     }
     this._inspectedTargetEvents.sort(SDK.TracingModel.Event.compareStartTime);
-
-    this._processBrowserEvents(tracingModel);
+    this._processAsyncBrowserEvents(tracingModel);
     this._buildGPUEvents(tracingModel);
     this._resetProcessingState();
   }
 
   /**
    * @param {!SDK.TracingModel} tracingModel
+   * @param {!SDK.TracingModel.Event} lastPageMetaEvent
+   */
+  _processAllThreads(tracingModel, lastPageMetaEvent) {
+    for (const process of tracingModel.sortedProcesses()) {
+      for (const thread of process.sortedThreads()) {
+        this._processThreadEvents(
+            tracingModel, [{from: 0, to: Infinity}], thread, thread === lastPageMetaEvent.thread, false, true, null);
+      }
+    }
+  }
+
+  /**
+   * @param {!SDK.TracingModel} tracingModel
+   * @param {!TimelineModel.TimelineModel.MetadataEvents} metadataEvents
+   */
+  _processMetadataAndThreads(tracingModel, metadataEvents) {
+    let startTime = 0;
+    for (let i = 0, length = metadataEvents.page.length; i < length; i++) {
+      const metaEvent = metadataEvents.page[i];
+      const process = metaEvent.thread.process();
+      const endTime = i + 1 < length ? metadataEvents.page[i + 1].startTime : Infinity;
+      this._legacyCurrentPage = metaEvent.args['data'] && metaEvent.args['data']['page'];
+      for (const thread of process.sortedThreads()) {
+        let workerUrl = null;
+        if (thread.name() === TimelineModel.TimelineModel.WorkerThreadName ||
+            thread.name() === TimelineModel.TimelineModel.WorkerThreadNameLegacy) {
+          const workerMetaEvent = metadataEvents.workers.find(e => {
+            if (e.args['data']['workerThreadId'] !== thread.id())
+              return false;
+            // This is to support old traces.
+            if (e.args['data']['sessionId'] === this._sessionId)
+              return true;
+            return !!this._pageFrames.get(TimelineModel.TimelineModel.eventFrameId(e));
+          });
+          if (!workerMetaEvent)
+            continue;
+          const workerId = workerMetaEvent.args['data']['workerId'];
+          if (workerId)
+            this._workerIdByThread.set(thread, workerId);
+          workerUrl = workerMetaEvent.args['data']['url'] || '';
+        }
+        this._processThreadEvents(
+            tracingModel, [{from: startTime, to: endTime}], thread, thread === metaEvent.thread, !!workerUrl, true,
+            workerUrl);
+      }
+      startTime = endTime;
+    }
+  }
+
+  /**
+   * @param {!SDK.TracingModel} tracingModel
+   */
+  _processThreadsForBrowserFrames(tracingModel) {
+    const processData = new Map();
+    for (const frame of this._pageFrames.values()) {
+      for (let i = 0; i < frame.processes.length; i++) {
+        const pid = frame.processes[i].processId;
+        let data = processData.get(pid);
+        if (!data) {
+          data = [];
+          processData.set(pid, data);
+        }
+        const to = i === frame.processes.length - 1 ? (frame.deletedTime || this._maximumRecordTime) :
+                                                      frame.processes[i + 1].time;
+        data.push({from: frame.processes[i].time, to: to, main: !frame.parent, url: frame.processes[i].url});
+      }
+    }
+    const allMetadataEvents = tracingModel.devToolsMetadataEvents();
+    for (const process of tracingModel.sortedProcesses()) {
+      const data = processData.get(process.id());
+      if (!data)
+        continue;
+      data.sort((a, b) => a.from - b.from || a.to - b.to);
+      const ranges = [];
+      let lastUrl = null;
+      let lastMainUrl = null;
+      let hasMain = false;
+      for (const item of data) {
+        if (!ranges.length || item.from > ranges.peekLast().to)
+          ranges.push({from: item.from, to: item.to});
+        else
+          ranges.peekLast().to = item.to;
+        if (item.main)
+          hasMain = true;
+        if (item.url) {
+          if (item.main)
+            lastMainUrl = item.url;
+          lastUrl = item.url;
+        }
+      }
+
+      for (const thread of process.sortedThreads()) {
+        if (thread.name() === TimelineModel.TimelineModel.RendererMainThreadName) {
+          this._processThreadEvents(
+              tracingModel, ranges, thread, true /* isMainThread */, false /* isWorker */, hasMain,
+              hasMain ? lastMainUrl : lastUrl);
+        } else if (
+            thread.name() === TimelineModel.TimelineModel.WorkerThreadName ||
+            thread.name() === TimelineModel.TimelineModel.WorkerThreadNameLegacy) {
+          const workerMetaEvent = allMetadataEvents.find(e => {
+            if (e.name !== TimelineModel.TimelineModel.DevToolsMetadataEvent.TracingSessionIdForWorker)
+              return false;
+            if (e.thread.process() !== process)
+              return false;
+            if (e.args['data']['workerThreadId'] !== thread.id())
+              return false;
+            return !!this._pageFrames.get(TimelineModel.TimelineModel.eventFrameId(e));
+          });
+          if (!workerMetaEvent)
+            continue;
+          this._workerIdByThread.set(thread, workerMetaEvent.args['data']['workerId'] || '');
+          this._processThreadEvents(
+              tracingModel, ranges, thread, false /* isMainThread */, true /* isWorker */, false /* forMainFrame */,
+              workerMetaEvent.args['data']['url'] || '');
+        } else {
+          this._processThreadEvents(
+              tracingModel, ranges, thread, false /* isMainThread */, false /* isWorker */, false /* forMainFrame */,
+              null);
+        }
+      }
+    }
+  }
+
+  /**
+   * @param {!SDK.TracingModel} tracingModel
    * @param {boolean} produceTraceStartedInPage
    * @return {!TimelineModel.TimelineModel.MetadataEvents}
    */
@@ -230,9 +327,7 @@
           this._persistentIds = true;
         const frames = ((event.args['data'] && event.args['data']['frames']) || []);
         frames.forEach(payload => this._addPageFrame(event, payload));
-        const rootFrame = this.rootFrames()[0];
-        if (rootFrame && rootFrame.url)
-          this._pageURL = rootFrame.url;
+        this._mainFrame = this.rootFrames()[0];
       } else if (event.name === TimelineModel.TimelineModel.DevToolsMetadataEvent.TracingSessionIdForWorker) {
         workersDevToolsMetadataEvents.push(event);
       } else if (event.name === TimelineModel.TimelineModel.DevToolsMetadataEvent.TracingStartedInBrowser) {
@@ -306,14 +401,19 @@
   /**
    * @param {!SDK.TracingModel} tracingModel
    */
-  _processBrowserEvents(tracingModel) {
+  _processSyncBrowserEvents(tracingModel) {
     const browserMain = SDK.TracingModel.browserMainThread(tracingModel);
-    if (!browserMain)
-      return;
+    if (browserMain)
+      browserMain.events().forEach(this._processBrowserEvent, this);
+  }
 
-    // Disregard regular events, we don't need them yet, but still process to get proper metadata.
-    browserMain.events().forEach(this._processBrowserEvent, this);
-    this._processAsyncEvents(browserMain.asyncEvents());
+  /**
+   * @param {!SDK.TracingModel} tracingModel
+   */
+  _processAsyncBrowserEvents(tracingModel) {
+    const browserMain = SDK.TracingModel.browserMainThread(tracingModel);
+    if (browserMain)
+      this._processAsyncEvents(browserMain.asyncEvents(), [{from: 0, to: Infinity}]);
   }
 
   /**
@@ -340,6 +440,7 @@
     this._eventStack = [];
     /** @type {!Set<string>} */
     this._knownInputEvents = new Set();
+    this._browserFrameTracking = false;
     this._persistentIds = false;
     this._legacyCurrentPage = null;
   }
@@ -423,56 +524,63 @@
 
   /**
    * @param {!SDK.TracingModel} tracingModel
-   * @param {number} startTime
-   * @param {number} endTime
+   * @param {!Array<!{from: number, to: number}>} ranges
    * @param {!SDK.TracingModel.Thread} thread
    * @param {boolean} isMainThread
-   * @param {?string} workerUrl
+   * @param {boolean} isWorker
+   * @param {boolean} forMainFrame
+   * @param {?string} url
    */
-  _processThreadEvents(tracingModel, startTime, endTime, thread, isMainThread, workerUrl) {
-    const events = this._injectJSFrameEvents(tracingModel, thread);
-
-    let type = TimelineModel.TimelineModel.TrackType.Other;
-    if (workerUrl !== null)
-      type = TimelineModel.TimelineModel.TrackType.Worker;
-    else if (isMainThread)
-      type = TimelineModel.TimelineModel.TrackType.MainThread;
-    else if (thread.name().startsWith('CompositorTileWorker'))
-      type = TimelineModel.TimelineModel.TrackType.Raster;
-    const track = new TimelineModel.TimelineModel.Track(
-        thread.name(), type, type === TimelineModel.TimelineModel.TrackType.MainThread /* forMainFrame */, workerUrl);
+  _processThreadEvents(tracingModel, ranges, thread, isMainThread, isWorker, forMainFrame, url) {
+    const track = new TimelineModel.TimelineModel.Track();
+    track.name = thread.name();
+    track.type = TimelineModel.TimelineModel.TrackType.Other;
+    if (isMainThread) {
+      track.type = TimelineModel.TimelineModel.TrackType.MainThread;
+      track.url = url || null;
+      track.forMainFrame = forMainFrame;
+    } else if (isWorker) {
+      track.type = TimelineModel.TimelineModel.TrackType.Worker;
+      track.url = url;
+    } else if (thread.name().startsWith('CompositorTileWorker')) {
+      track.type = TimelineModel.TimelineModel.TrackType.Raster;
+    }
     this._tracks.push(track);
 
+    const events = this._injectJSFrameEvents(tracingModel, thread);
     this._eventStack = [];
     const eventStack = this._eventStack;
-    let i = events.lowerBound(startTime, (time, event) => time - event.startTime);
-    for (; i < events.length; i++) {
-      const event = events[i];
-      if (endTime && event.startTime >= endTime)
-        break;
-      while (eventStack.length && eventStack.peekLast().endTime <= event.startTime)
-        eventStack.pop();
-      if (!this._processEvent(event))
-        continue;
-      if (!SDK.TracingModel.isAsyncPhase(event.phase) && event.duration) {
-        if (eventStack.length) {
-          const parent = eventStack.peekLast();
-          parent.selfTime -= event.duration;
-          if (parent.selfTime < 0)
-            this._fixNegativeDuration(parent, event);
-        }
-        event.selfTime = event.duration;
-        if (!eventStack.length)
-          track.tasks.push(event);
-        eventStack.push(event);
-      }
-      if (TimelineModel.TimelineModel.isMarkerEvent(event))
-        this._eventDividers.push(event);
 
-      track.events.push(event);
-      this._inspectedTargetEvents.push(event);
+    for (const range of ranges) {
+      let i = events.lowerBound(range.from, (time, event) => time - event.startTime);
+      for (; i < events.length; i++) {
+        const event = events[i];
+        if (event.startTime >= range.to)
+          break;
+        while (eventStack.length && eventStack.peekLast().endTime <= event.startTime)
+          eventStack.pop();
+        if (!this._processEvent(event))
+          continue;
+        if (!SDK.TracingModel.isAsyncPhase(event.phase) && event.duration) {
+          if (eventStack.length) {
+            const parent = eventStack.peekLast();
+            parent.selfTime -= event.duration;
+            if (parent.selfTime < 0)
+              this._fixNegativeDuration(parent, event);
+          }
+          event.selfTime = event.duration;
+          if (!eventStack.length)
+            track.tasks.push(event);
+          eventStack.push(event);
+        }
+        if (TimelineModel.TimelineModel.isMarkerEvent(event))
+          this._eventDividers.push(event);
+
+        track.events.push(event);
+        this._inspectedTargetEvents.push(event);
+      }
     }
-    this._processAsyncEvents(thread.asyncEvents(), startTime, endTime);
+    this._processAsyncEvents(thread.asyncEvents(), ranges);
   }
 
   /**
@@ -491,10 +599,9 @@
 
   /**
    * @param {!Array<!SDK.TracingModel.AsyncEvent>} asyncEvents
-   * @param {number=} startTime
-   * @param {number=} endTime
+   * @param {!Array<!{from: number, to: number}>} ranges
    */
-  _processAsyncEvents(asyncEvents, startTime, endTime) {
+  _processAsyncEvents(asyncEvents, ranges) {
     const groups = new Map();
 
     /**
@@ -507,53 +614,55 @@
       return groups.get(type);
     }
 
-    let i = startTime ? asyncEvents.lowerBound(startTime, function(time, asyncEvent) {
-      return time - asyncEvent.startTime;
-    }) : 0;
+    for (const range of ranges) {
+      let i = asyncEvents.lowerBound(range.from, function(time, asyncEvent) {
+        return time - asyncEvent.startTime;
+      });
 
-    for (; i < asyncEvents.length; ++i) {
-      const asyncEvent = asyncEvents[i];
-      if (endTime && asyncEvent.startTime >= endTime)
-        break;
+      for (; i < asyncEvents.length; ++i) {
+        const asyncEvent = asyncEvents[i];
+        if (asyncEvent.startTime >= range.to)
+          break;
 
-      if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.Console)) {
-        group(TimelineModel.TimelineModel.TrackType.Console).push(asyncEvent);
-        continue;
-      }
-
-      if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.UserTiming)) {
-        group(TimelineModel.TimelineModel.TrackType.UserTiming).push(asyncEvent);
-        continue;
-      }
-
-      if (asyncEvent.name === TimelineModel.TimelineModel.RecordType.Animation) {
-        group(TimelineModel.TimelineModel.TrackType.Animation).push(asyncEvent);
-        continue;
-      }
-
-      if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo) ||
-          asyncEvent.name === TimelineModel.TimelineModel.RecordType.ImplSideFling) {
-        const lastStep = asyncEvent.steps.peekLast();
-        // FIXME: fix event termination on the back-end instead.
-        if (lastStep.phase !== SDK.TracingModel.Phase.AsyncEnd)
+        if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.Console)) {
+          group(TimelineModel.TimelineModel.TrackType.Console).push(asyncEvent);
           continue;
-        const data = lastStep.args['data'];
-        asyncEvent.causedFrame = !!(data && data['INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT']);
-        if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo)) {
-          if (!this._knownInputEvents.has(lastStep.id))
-            continue;
-          if (asyncEvent.name === TimelineModel.TimelineModel.RecordType.InputLatencyMouseMove &&
-              !asyncEvent.causedFrame)
-            continue;
-          const rendererMain = data['INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT'];
-          if (rendererMain) {
-            const time = rendererMain['time'] / 1000;
-            TimelineModel.TimelineData.forEvent(asyncEvent.steps[0]).timeWaitingForMainThread =
-                time - asyncEvent.steps[0].startTime;
-          }
         }
-        group(TimelineModel.TimelineModel.TrackType.Input).push(asyncEvent);
-        continue;
+
+        if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.UserTiming)) {
+          group(TimelineModel.TimelineModel.TrackType.UserTiming).push(asyncEvent);
+          continue;
+        }
+
+        if (asyncEvent.name === TimelineModel.TimelineModel.RecordType.Animation) {
+          group(TimelineModel.TimelineModel.TrackType.Animation).push(asyncEvent);
+          continue;
+        }
+
+        if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo) ||
+            asyncEvent.name === TimelineModel.TimelineModel.RecordType.ImplSideFling) {
+          const lastStep = asyncEvent.steps.peekLast();
+          // FIXME: fix event termination on the back-end instead.
+          if (lastStep.phase !== SDK.TracingModel.Phase.AsyncEnd)
+            continue;
+          const data = lastStep.args['data'];
+          asyncEvent.causedFrame = !!(data && data['INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT']);
+          if (asyncEvent.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo)) {
+            if (!this._knownInputEvents.has(lastStep.id))
+              continue;
+            if (asyncEvent.name === TimelineModel.TimelineModel.RecordType.InputLatencyMouseMove &&
+                !asyncEvent.causedFrame)
+              continue;
+            const rendererMain = data['INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT'];
+            if (rendererMain) {
+              const time = rendererMain['time'] / 1000;
+              TimelineModel.TimelineData.forEvent(asyncEvent.steps[0]).timeWaitingForMainThread =
+                  time - asyncEvent.steps[0].startTime;
+            }
+          }
+          group(TimelineModel.TimelineModel.TrackType.Input).push(asyncEvent);
+          continue;
+        }
       }
     }
 
@@ -604,7 +713,7 @@
     let pageFrameId = TimelineModel.TimelineModel.eventFrameId(event);
     if (!pageFrameId && eventStack.length)
       pageFrameId = TimelineModel.TimelineData.forEvent(eventStack.peekLast()).frameId;
-    timelineData.frameId = pageFrameId || TimelineModel.TimelineModel.PageFrame.mainFrameId;
+    timelineData.frameId = pageFrameId || (this._mainFrame && this._mainFrame.frameId) || '';
     this._asyncEventTracker.processEvent(event);
     switch (event.name) {
       case recordTypes.ResourceSendRequest:
@@ -782,11 +891,13 @@
       }
 
       case recordTypes.CommitLoad: {
+        if (this._browserFrameTracking)
+          break;
         const frameId = TimelineModel.TimelineModel.eventFrameId(event);
         const isMainFrame = !!eventData['isMainFrame'];
         const pageFrame = this._pageFrames.get(frameId);
         if (pageFrame) {
-          pageFrame.update(eventData.name || '', eventData.url || '');
+          pageFrame.update(event.startTime, eventData);
         } else {
           // We should only have one main frame which has persistent id,
           // unless it's an old trace without 'persistentIds' flag.
@@ -799,8 +910,8 @@
             return false;
           }
         }
-        if (isMainFrame && eventData.url)
-          this._pageURL = eventData.url;
+        if (isMainFrame)
+          this._mainFrame = this._pageFrames.get(frameId);
         break;
       }
 
@@ -817,11 +928,69 @@
    * @param {!SDK.TracingModel.Event} event
    */
   _processBrowserEvent(event) {
-    if (event.name !== TimelineModel.TimelineModel.RecordType.LatencyInfoFlow)
+    if (event.name === TimelineModel.TimelineModel.RecordType.LatencyInfoFlow) {
+      const frameId = event.args['frameTreeNodeId'];
+      if (typeof frameId === 'number' && frameId === this._mainFrameNodeId)
+        this._knownInputEvents.add(event.bind_id);
       return;
-    const frameId = event.args['frameTreeNodeId'];
-    if (typeof frameId === 'number' && frameId === this._mainFrameNodeId)
-      this._knownInputEvents.add(event.bind_id);
+    }
+
+    if (event.hasCategory(SDK.TracingModel.DevToolsMetadataEventCategory) && event.args['data']) {
+      const data = event.args['data'];
+      if (event.name === TimelineModel.TimelineModel.DevToolsMetadataEvent.TracingStartedInBrowser) {
+        if (!data['persistentIds'])
+          return;
+        this._browserFrameTracking = true;
+        this._mainFrameNodeId = data['frameTreeNodeId'];
+        const frames = data['frames'] || [];
+        frames.forEach(payload => {
+          const parent = payload['parent'] && this._pageFrames.get(payload['parent']);
+          if (payload['parent'] && !parent)
+            return;
+          let frame = this._pageFrames.get(payload['frame']);
+          if (!frame) {
+            frame = new TimelineModel.TimelineModel.PageFrame(payload);
+            this._pageFrames.set(frame.frameId, frame);
+            if (parent)
+              parent.addChild(frame);
+            else
+              this._mainFrame = frame;
+          }
+          // TODO(dgozman): this should use event.startTime, but due to races between tracing start
+          // in different processes we cannot do this yet.
+          frame.update(this._minimumRecordTime, payload);
+        });
+        return;
+      }
+      if (event.name === TimelineModel.TimelineModel.DevToolsMetadataEvent.FrameCommittedInBrowser &&
+          this._browserFrameTracking) {
+        let frame = this._pageFrames.get(data['frame']);
+        if (!frame) {
+          const parent = data['parent'] && this._pageFrames.get(data['parent']);
+          if (!parent)
+            return;
+          frame = new TimelineModel.TimelineModel.PageFrame(data);
+          this._pageFrames.set(frame.frameId, frame);
+          parent.addChild(frame);
+        }
+        frame.update(event.startTime, data);
+        return;
+      }
+      if (event.name === TimelineModel.TimelineModel.DevToolsMetadataEvent.ProcessReadyInBrowser &&
+          this._browserFrameTracking) {
+        const frame = this._pageFrames.get(data['frame']);
+        if (frame)
+          frame.processReady(data['processPseudoId'], data['processId']);
+        return;
+      }
+      if (event.name === TimelineModel.TimelineModel.DevToolsMetadataEvent.FrameDeletedInBrowser &&
+          this._browserFrameTracking) {
+        const frame = this._pageFrames.get(data['frame']);
+        if (frame)
+          frame.deletedTime = event.startTime;
+        return;
+      }
+    }
   }
 
   /**
@@ -830,7 +999,8 @@
    */
   _ensureNamedTrack(type) {
     if (!this._namedTracks.has(type)) {
-      const track = new TimelineModel.TimelineModel.Track('', type, true /* forMainFrame */, null);
+      const track = new TimelineModel.TimelineModel.Track();
+      track.type = type;
       this._tracks.push(track);
       this._namedTracks.set(type, track);
     }
@@ -856,12 +1026,12 @@
    * @return {boolean}
    */
   _addPageFrame(event, payload) {
-    const processId = event.thread.process().id();
-    const parent = payload['parent'] && this._pageFrames.get(`${processId}.${payload['parent']}`);
+    const parent = payload['parent'] && this._pageFrames.get(payload['parent']);
     if (payload['parent'] && !parent)
       return false;
-    const pageFrame = new TimelineModel.TimelineModel.PageFrame(this.targetByEvent(event), processId, payload);
-    this._pageFrames.set(pageFrame.id, pageFrame);
+    const pageFrame = new TimelineModel.TimelineModel.PageFrame(payload);
+    this._pageFrames.set(pageFrame.frameId, pageFrame);
+    pageFrame.update(event.startTime, payload);
     if (parent)
       parent.addChild(pageFrame);
     return true;
@@ -886,7 +1056,7 @@
     this._workerIdByThread = new WeakMap();
     /** @type {!Map<string, !TimelineModel.TimelineModel.PageFrame>} */
     this._pageFrames = new Map();
-    this._pageURL = '';
+    this._mainFrame = null;
 
     this._minimumRecordTime = 0;
     this._maximumRecordTime = 0;
@@ -945,7 +1115,7 @@
    * @return {string}
    */
   pageURL() {
-    return this._pageURL;
+    return this._mainFrame && this._mainFrame.url || '';
   }
 
   /**
@@ -1155,7 +1325,6 @@
   V8Deopt: 'V8Deopt'
 };
 
-TimelineModel.TimelineModel.MainThreadName = 'main';
 TimelineModel.TimelineModel.WorkerThreadName = 'DedicatedWorker thread';
 TimelineModel.TimelineModel.WorkerThreadNameLegacy = 'DedicatedWorker Thread';
 TimelineModel.TimelineModel.RendererMainThreadName = 'CrRendererMain';
@@ -1164,6 +1333,9 @@
   TracingStartedInBrowser: 'TracingStartedInBrowser',
   TracingStartedInPage: 'TracingStartedInPage',
   TracingSessionIdForWorker: 'TracingSessionIdForWorker',
+  FrameCommittedInBrowser: 'FrameCommittedInBrowser',
+  ProcessReadyInBrowser: 'ProcessReadyInBrowser',
+  FrameDeletedInBrowser: 'FrameDeletedInBrowser',
 };
 
 TimelineModel.TimelineModel.Thresholds = {
@@ -1174,20 +1346,12 @@
 };
 
 TimelineModel.TimelineModel.Track = class {
-  /**
-   * @param {string} name
-   * @param {!TimelineModel.TimelineModel.TrackType} type
-   * @param {boolean} forMainFrame
-   * @param {?string} workerUrl
-   */
-  constructor(name, type, forMainFrame, workerUrl) {
-    // Note that each main thread track only contains events from the
-    // interesting time intervals, while at least one frame from the
-    // page operated on the thread.
-    this.name = name;
-    this.type = type;
-    this.forMainFrame = forMainFrame;
-    this.workerUrl = workerUrl;
+  constructor() {
+    this.name = '';
+    this.type = TimelineModel.TimelineModel.TrackType.Other;
+    // TODO(dgozman): replace forMainFrame with a list of frames, urls and time ranges.
+    this.forMainFrame = false;
+    this.url = '';
     // TODO(dgozman): do not distinguish between sync and async events.
     /** @type {!Array<!SDK.TracingModel.Event>} */
     this.events = [];
@@ -1245,35 +1409,54 @@
   Other: Symbol('Other'),
 };
 
-/** @typedef {!{page: !Array<!SDK.TracingModel.Event>, workers: !Array<!SDK.TracingModel.Event>}} */
-TimelineModel.TimelineModel.MetadataEvents;
-
-
 TimelineModel.TimelineModel.PageFrame = class {
   /**
-   * @param {?SDK.Target} target
-   * @param {number} pid
    * @param {!Object} payload
    */
-  constructor(target, pid, payload) {
+  constructor(payload) {
     this.frameId = payload['frame'];
     this.url = payload['url'] || '';
     this.name = payload['name'];
-    this.processId = pid;
+    /** @type {!Array<!TimelineModel.TimelineModel.PageFrame>} */
     this.children = [];
     /** @type {?TimelineModel.TimelineModel.PageFrame} */
     this.parent = null;
-    this.id = `${this.processId}.${this.frameId}`;
-    this.ownerNode = target && payload['nodeId'] ? new SDK.DeferredDOMNode(target, payload['nodeId']) : null;
+    /** @type {!Array<!{time: number, processId: number, processPseudoId: ?string, url: string}>} */
+    this.processes = [];
+    /** @type {?number} */
+    this.deletedTime = null;
+    // TODO(dgozman): figure this out.
+    // this.ownerNode = target && payload['nodeId'] ? new SDK.DeferredDOMNode(target, payload['nodeId']) : null;
+    this.ownerNode = null;
   }
 
   /**
-   * @param {string} name
-   * @param {string} url
+   * @param {number} time
+   * @param {!Object} payload
    */
-  update(name, url) {
-    this.name = name;
-    this.url = url;
+  update(time, payload) {
+    this.url = payload['url'] || '';
+    this.name = payload['name'];
+    if (payload['processId']) {
+      this.processes.push(
+          {time: time, processId: payload['processId'], processPseudoId: '', url: payload['url'] || ''});
+    } else {
+      this.processes.push(
+          {time: time, processId: -1, processPseudoId: payload['processPseudoId'], url: payload['url'] || ''});
+    }
+  }
+
+  /**
+   * @param {string} processPseudoId
+   * @param {number} processId
+   */
+  processReady(processPseudoId, processId) {
+    for (const process of this.processes) {
+      if (process.processPseudoId === processPseudoId) {
+        process.processPseudoId = '';
+        process.processId = processId;
+      }
+    }
   }
 
   /**
@@ -1285,8 +1468,8 @@
   }
 };
 
-TimelineModel.TimelineModel.PageFrame.mainFrameId = '';
-
+/** @typedef {!{page: !Array<!SDK.TracingModel.Event>, workers: !Array<!SDK.TracingModel.Event>}} */
+TimelineModel.TimelineModel.MetadataEvents;
 
 /**
  * @unrestricted
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
index 231f62a..fe1208a3 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
@@ -566,6 +566,9 @@
   if (layout_object_->IsBR())
     return false;
 
+  if (CanSetFocusAttribute())
+    return false;
+
   if (IsLink())
     return false;
 
@@ -660,28 +663,13 @@
   if (RoleValue() == kTimeRole)
     return false;
 
-  if (RoleValue() == kProgressIndicatorRole) {
+  if (RoleValue() == kProgressIndicatorRole)
     return false;
-  }
 
   // if this element has aria attributes on it, it should not be ignored.
   if (SupportsARIAAttributes())
     return false;
 
-  // <span> tags are inline tags and not meant to convey information if they
-  // have no other aria information on them. If we don't ignore them, they may
-  // emit signals expected to come from their parent. In addition, because
-  // included spans are GroupRole objects, and GroupRole objects are often
-  // containers with meaningful information, the inclusion of a span can have
-  // the side effect of causing the immediate parent accessible to be ignored.
-  // This is especially problematic for platforms which have distinct roles for
-  // textual block elements.
-  if (IsHTMLSpanElement(node)) {
-    if (ignored_reasons)
-      ignored_reasons->push_back(IgnoredReason(kAXUninteresting));
-    return true;
-  }
-
   if (IsImage())
     return false;
 
@@ -714,10 +702,14 @@
       !GetAttribute(altAttr).IsEmpty() || !GetAttribute(titleAttr).IsEmpty())
     return false;
 
-  // Don't ignore generic focusable elements like <div tabindex=0>
-  // unless they're completely empty, with no children.
-  if (IsGenericFocusableElement() && node->hasChildren())
-    return false;
+  // <span> tags are inline tags and not meant to convey information if they
+  // have no other ARIA information on them. If we don't ignore them, they may
+  // emit signals expected to come from their parent.
+  if (IsHTMLSpanElement(node)) {
+    if (ignored_reasons)
+      ignored_reasons->push_back(IgnoredReason(kAXUninteresting));
+    return true;
+  }
 
   // Positioned elements and scrollable containers are important for
   // determining bounding boxes.
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
index 2b775d9b..f5bca07 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
@@ -606,41 +606,6 @@
   }
 }
 
-bool AXNodeObject::IsGenericFocusableElement() const {
-  if (!CanSetFocusAttribute())
-    return false;
-
-  // If it's a control, it's not generic.
-  if (IsControl())
-    return false;
-
-  // If it has an aria role, it's not generic.
-  if (aria_role_ != kUnknownRole)
-    return false;
-
-  // If the content editable attribute is set on this element, that's the reason
-  // it's focusable, and existing logic should handle this case already - so
-  // it's not a generic focusable element.
-
-  if (HasContentEditableAttributeSet())
-    return false;
-
-  // The web area and body element are both focusable, but existing logic
-  // handles these cases already, so we don't need to include them here.
-  if (RoleValue() == kWebAreaRole)
-    return false;
-  if (IsHTMLBodyElement(GetNode()))
-    return false;
-
-  // An SVG root is focusable by default, but it's probably not interactive, so
-  // don't include it. It can still be made accessible by giving it an ARIA
-  // role.
-  if (RoleValue() == kSVGRootRole)
-    return false;
-
-  return true;
-}
-
 AXObject* AXNodeObject::MenuButtonForMenu() const {
   Element* menu_item = MenuItemElementForMenu();
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
index 189805f1..5656ee8 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.h
@@ -70,9 +70,6 @@
 
   bool HasContentEditableAttributeSet() const;
   bool IsTextControl() const override;
-  // This returns true if it's focusable but it's not content editable and it's
-  // not a control or ARIA control.
-  bool IsGenericFocusableElement() const;
   AXObject* MenuButtonForMenu() const;
   AXObject* MenuButtonForMenuIfExists() const;
   Element* MenuItemElementForMenu() const;
diff --git a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
index 4184e3b7..603aba6a 100644
--- a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
@@ -164,13 +164,7 @@
       "implementation-status.md"));
 #endif
 
-  // If the Relevant settings object is not a secure context, reject promise
-  // with a SecurityError and abort these steps.
-  String error_message;
-  if (!context->IsSecureContext(error_message)) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state, DOMException::Create(kSecurityError, error_message));
-  }
+  CHECK(context->IsSecureContext());
 
   // If the algorithm is not allowed to show a popup, reject promise with a
   // SecurityError and abort these steps.
diff --git a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.idl b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.idl
index 302b496..5ceef89 100644
--- a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.idl
+++ b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.idl
@@ -7,5 +7,5 @@
 [
     RuntimeEnabled=WebBluetooth
 ] interface Bluetooth {
-    [CallWith=ScriptState, RaisesException, MeasureAs=WebBluetoothRequestDevice] Promise<BluetoothDevice> requestDevice (optional RequestDeviceOptions options);
+    [CallWith=ScriptState, RaisesException, MeasureAs=WebBluetoothRequestDevice, SecureContext] Promise<BluetoothDevice> requestDevice (optional RequestDeviceOptions options);
 };
diff --git a/third_party/WebKit/Source/modules/media_controls/resources/modernMediaControls.css b/third_party/WebKit/Source/modules/media_controls/resources/modernMediaControls.css
index 6a2ac3e..edb754a 100644
--- a/third_party/WebKit/Source/modules/media_controls/resources/modernMediaControls.css
+++ b/third_party/WebKit/Source/modules/media_controls/resources/modernMediaControls.css
@@ -35,6 +35,7 @@
   justify-content: flex-end;
   align-items: center;
   font-size: 14px;
+  will-change: transform;
 }
 
 audio::-webkit-media-controls-enclosure,
diff --git a/third_party/WebKit/Source/modules/payments/PaymentAddress.idl b/third_party/WebKit/Source/modules/payments/PaymentAddress.idl
index c408e26..74bda420 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentAddress.idl
+++ b/third_party/WebKit/Source/modules/payments/PaymentAddress.idl
@@ -2,22 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/browser-payment-api/#paymentaddress-interface
+// https://w3c.github.io/payment-request/#paymentaddress-interface
 
 [
     RuntimeEnabled=PaymentRequest,
+    SecureContext,
     Exposed=Window
 ] interface PaymentAddress {
     serializer = {attribute};
-    readonly attribute DOMString country;
-    readonly attribute FrozenArray<DOMString> addressLine;
-    readonly attribute DOMString region;
     readonly attribute DOMString city;
+    readonly attribute DOMString country;
     readonly attribute DOMString dependentLocality;
-    readonly attribute DOMString postalCode;
-    readonly attribute DOMString sortingCode;
     readonly attribute DOMString languageCode;
     readonly attribute DOMString organization;
-    readonly attribute DOMString recipient;
     readonly attribute DOMString phone;
+    readonly attribute DOMString postalCode;
+    readonly attribute DOMString recipient;
+    readonly attribute DOMString region;
+    readonly attribute DOMString sortingCode;
+    readonly attribute FrozenArray<DOMString> addressLine;
 };
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
index fc2ef5f..96c4c5e 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -1005,10 +1005,7 @@
           execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI),
           this,
           &PaymentRequest::OnCompleteTimeout) {
-  if (!GetExecutionContext()->IsSecureContext()) {
-    exception_state.ThrowSecurityError("Must be in a secure context");
-    return;
-  }
+  DCHECK(GetExecutionContext()->IsSecureContext());
 
   if (!AllowedToUsePaymentRequest(GetFrame())) {
     exception_state.ThrowSecurityError(
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.idl b/third_party/WebKit/Source/modules/payments/PaymentRequest.idl
index 12518140..cfc59a9 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.idl
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/browser-payment-api/#paymentrequest-interface
+// https://w3c.github.io/payment-request/#paymentrequest-interface
 // http://crbug.com/587995
 
 [
@@ -10,6 +10,7 @@
     Constructor(sequence<PaymentMethodData> methodData, PaymentDetailsInit details, optional PaymentOptions options),
     ConstructorCallWith=ExecutionContext,
     RaisesException=Constructor,
+    SecureContext,
     Exposed=Window,
     ActiveScriptWrappable
 ] interface PaymentRequest : EventTarget {
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequestTest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequestTest.cpp
index 974aab7..de649fad 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequestTest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequestTest.cpp
@@ -14,19 +14,6 @@
 namespace blink {
 namespace {
 
-TEST(PaymentRequestTest, SecureContextRequired) {
-  V8TestingScope scope;
-  scope.GetDocument().SetSecurityOrigin(
-      SecurityOrigin::Create(KURL("http://www.example.com/")));
-
-  PaymentRequest::Create(
-      scope.GetExecutionContext(), BuildPaymentMethodDataForTest(),
-      BuildPaymentDetailsInitForTest(), scope.GetExceptionState());
-
-  EXPECT_TRUE(scope.GetExceptionState().HadException());
-  EXPECT_EQ(kSecurityError, scope.GetExceptionState().Code());
-}
-
 TEST(PaymentRequestTest, NoExceptionWithValidData) {
   V8TestingScope scope;
   MakePaymentRequestOriginSecure(scope.GetDocument());
diff --git a/third_party/WebKit/Source/modules/payments/PaymentResponse.idl b/third_party/WebKit/Source/modules/payments/PaymentResponse.idl
index 863b2a0..53dcc0a9 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentResponse.idl
+++ b/third_party/WebKit/Source/modules/payments/PaymentResponse.idl
@@ -10,22 +10,23 @@
     "unknown"
 };
 
-// https://w3c.github.io/browser-payment-api/#paymentresponse-interface
+// https://w3c.github.io/payment-request/#dom-paymentresponse
 
 [
     RuntimeEnabled=PaymentRequest,
+    SecureContext,
     Exposed=Window
 ] interface PaymentResponse {
     serializer = {attribute};
 
     readonly attribute DOMString requestId;
     readonly attribute DOMString methodName;
-    readonly attribute DOMString? payerName;
-    readonly attribute DOMString? payerEmail;
-    readonly attribute DOMString? payerPhone;
     [CallWith=ScriptState, RaisesException] readonly attribute object details;
     readonly attribute PaymentAddress? shippingAddress;
     readonly attribute DOMString? shippingOption;
+    readonly attribute DOMString? payerName;
+    readonly attribute DOMString? payerEmail;
+    readonly attribute DOMString? payerPhone;
 
     [CallWith=ScriptState] Promise<void> complete(optional PaymentComplete paymentResult = "unknown");
 };
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
index 4ad03cb..e1fdb19 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
@@ -535,7 +535,8 @@
       negotiation_needed_(false),
       stopped_(false),
       closed_(false),
-      has_data_channels_(false) {
+      has_data_channels_(false),
+      sdp_semantics_(configuration.sdp_semantics) {
   Document* document = ToDocument(GetExecutionContext());
 
   // If we fail, set |m_closed| and |m_stopped| to true, to avoid hitting the
@@ -1424,9 +1425,13 @@
   DCHECK(track->Component());
   if (ThrowExceptionIfSignalingStateClosed(signaling_state_, exception_state))
     return nullptr;
-  if (streams.size() >= 2) {
-    // TODO(hbos): Don't throw an exception when this is supported by the lower
-    // layers. https://crbug.com/webrtc/7932
+  // TODO(bugs.webrtc.org/8530): Take out WebRTCSdpSemantics::kDefault check
+  // once default is no longer interpreted as Plan B lower down.
+  if ((sdp_semantics_ == WebRTCSdpSemantics::kPlanB ||
+       sdp_semantics_ == WebRTCSdpSemantics::kDefault) &&
+      streams.size() >= 2) {
+    // TODO(hbos): Update peer_handler_ to call the AddTrack() that returns the
+    // appropriate errors, and let the lower layers handle it.
     exception_state.ThrowDOMException(
         kNotSupportedError,
         "Adding a track to multiple streams is not supported.");
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h
index 496f2c2..a117adf 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.h
@@ -44,6 +44,7 @@
 #include "platform/heap/HeapAllocator.h"
 #include "platform/scheduler/public/frame_scheduler.h"
 #include "public/platform/WebMediaConstraints.h"
+#include "public/platform/WebRTCConfiguration.h"
 #include "public/platform/WebRTCPeerConnectionHandler.h"
 #include "public/platform/WebRTCPeerConnectionHandlerClient.h"
 
@@ -351,6 +352,7 @@
   String last_answer_;
 
   bool has_data_channels_;  // For RAPPOR metrics
+  WebRTCSdpSemantics sdp_semantics_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/FontDescription.cpp b/third_party/WebKit/Source/platform/fonts/FontDescription.cpp
index 8250db0..d760d78 100644
--- a/third_party/WebKit/Source/platform/fonts/FontDescription.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontDescription.cpp
@@ -114,22 +114,26 @@
 }
 
 FontSelectionValue FontDescription::LighterWeight(FontSelectionValue weight) {
-  if (weight >= FontSelectionValue(100) && weight <= FontSelectionValue(500))
+  // TODO(fs): Adjust to match table in
+  // https://drafts.csswg.org/css-fonts-4/#font-weight-prop
+  if (weight < FontSelectionValue(501))
     return FontSelectionValue(100);
-  if (weight >= FontSelectionValue(600) && weight <= FontSelectionValue(700))
+  if (weight < FontSelectionValue(701))
     return FontSelectionValue(400);
-  if (weight >= FontSelectionValue(800) && weight <= FontSelectionValue(900))
+  if (weight <= FontSelectionValue(1000))
     return FontSelectionValue(700);
   NOTREACHED();
   return NormalWeightValue();
 }
 
 FontSelectionValue FontDescription::BolderWeight(FontSelectionValue weight) {
-  if (weight >= FontSelectionValue(100) && weight <= FontSelectionValue(300))
+  // TODO(fs): Adjust to match table in
+  // https://drafts.csswg.org/css-fonts-4/#font-weight-prop
+  if (weight < FontSelectionValue(301))
     return FontSelectionValue(400);
-  if (weight >= FontSelectionValue(400) && weight <= FontSelectionValue(500))
+  if (weight < FontSelectionValue(501))
     return FontSelectionValue(700);
-  if (weight >= FontSelectionValue(600) && weight <= FontSelectionValue(900))
+  if (weight <= FontSelectionValue(1000))
     return FontSelectionValue(900);
   NOTREACHED();
   return NormalWeightValue();
diff --git a/third_party/WebKit/Source/platform/heap/HeapPage.cpp b/third_party/WebKit/Source/platform/heap/HeapPage.cpp
index 02daa74..6519f58d 100644
--- a/third_party/WebKit/Source/platform/heap/HeapPage.cpp
+++ b/third_party/WebKit/Source/platform/heap/HeapPage.cpp
@@ -210,7 +210,7 @@
   }
   DCHECK(SweepingCompleted());
 
-  Verify();
+  VerifyObjectStartBitmap();
 }
 
 size_t BaseArena::ObjectPayloadSizeForTesting() {
@@ -232,7 +232,7 @@
   ClearFreeLists();
 
   // Verification depends on the allocation point being cleared.
-  Verify();
+  VerifyObjectStartBitmap();
 
   for (BasePage* page = first_page_; page; page = page->Next()) {
     page->MarkAsUnswept();
@@ -584,11 +584,14 @@
   heap.Compaction()->FinishedArenaCompaction(this, freed_page_count,
                                              freed_size);
 
-  Verify();
+  VerifyObjectStartBitmap();
 }
 
-void NormalPageArena::Verify() {
+void NormalPageArena::VerifyObjectStartBitmap() {
 #if DCHECK_IS_ON()
+  // Verifying object start bitmap requires iterability of pages. As compaction
+  // may set up a new we have to reset here.
+  SetAllocationPoint(nullptr, 0);
   for (NormalPage* page = static_cast<NormalPage*>(first_page_); page;
        page = static_cast<NormalPage*>(page->Next()))
     page->VerifyObjectStartBitmapIsConsistentWithPayload();
diff --git a/third_party/WebKit/Source/platform/heap/HeapPage.h b/third_party/WebKit/Source/platform/heap/HeapPage.h
index 22d72f2..6d344bc 100644
--- a/third_party/WebKit/Source/platform/heap/HeapPage.h
+++ b/third_party/WebKit/Source/platform/heap/HeapPage.h
@@ -774,7 +774,7 @@
 
   bool WillObjectBeLazilySwept(BasePage*, void*) const;
 
-  virtual void Verify(){};
+  virtual void VerifyObjectStartBitmap(){};
   virtual void VerifyMarking(){};
 
  protected:
@@ -836,7 +836,7 @@
 
   void SweepAndCompact();
 
-  void Verify() override;
+  void VerifyObjectStartBitmap() override;
   void VerifyMarking() override;
 
   Address CurrentAllocationPoint() const { return current_allocation_point_; }
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
index 92935f86..cedf80ad 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
@@ -1444,7 +1444,7 @@
     }
   }
 
-  resource->VirtualTimePauser().PauseVirtualTime(false);
+  resource->VirtualTimePauser().UnpauseVirtualTime();
   Context().DispatchDidFinishLoading(
       resource->Identifier(), finish_time, encoded_data_length,
       resource->GetResponse().DecodedBodyLength(), blocked_cross_site_document);
@@ -1470,7 +1470,7 @@
   bool is_internal_request = resource->Options().initiator_info.name ==
                              FetchInitiatorTypeNames::internal;
 
-  resource->VirtualTimePauser().PauseVirtualTime(false);
+  resource->VirtualTimePauser().UnpauseVirtualTime();
   Context().DispatchDidFail(
       resource->LastResourceRequest().Url(), resource->Identifier(), error,
       resource->GetResponse().EncodedDataLength(), is_internal_request);
@@ -1519,8 +1519,9 @@
     if (Context().GetFrameScheduler()) {
       WebScopedVirtualTimePauser virtual_time_pauser =
           Context().GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
+              resource->Url().GetString(),
               WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
-      virtual_time_pauser.PauseVirtualTime(true);
+      virtual_time_pauser.PauseVirtualTime();
       resource->VirtualTimePauser() = std::move(virtual_time_pauser);
     }
     Context().DispatchWillSendRequest(resource->Identifier(), request, response,
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
index 341d0b7..b2f6a01f 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
@@ -167,7 +167,11 @@
 void ResourceLoader::SetDefersLoading(bool defers) {
   DCHECK(loader_);
   loader_->SetDefersLoading(defers);
-  resource_->VirtualTimePauser().PauseVirtualTime(!defers);
+  if (defers) {
+    resource_->VirtualTimePauser().UnpauseVirtualTime();
+  } else {
+    resource_->VirtualTimePauser().PauseVirtualTime();
+  }
 }
 
 void ResourceLoader::DidChangePriority(ResourceLoadPriority load_priority,
@@ -361,8 +365,9 @@
   if (Context().GetFrameScheduler()) {
     WebScopedVirtualTimePauser virtual_time_pauser =
         Context().GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
+            resource_->Url().GetString(),
             WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
-    virtual_time_pauser.PauseVirtualTime(true);
+    virtual_time_pauser.PauseVirtualTime();
     resource_->VirtualTimePauser() = std::move(virtual_time_pauser);
   }
   Context().DispatchWillSendRequest(resource_->Identifier(), *new_request,
diff --git a/third_party/WebKit/Source/platform/mhtml/MHTMLParser.cpp b/third_party/WebKit/Source/platform/mhtml/MHTMLParser.cpp
index 3cd2b12..a41a6d0 100644
--- a/third_party/WebKit/Source/platform/mhtml/MHTMLParser.cpp
+++ b/third_party/WebKit/Source/platform/mhtml/MHTMLParser.cpp
@@ -221,9 +221,11 @@
 HeapVector<Member<ArchiveResource>> MHTMLParser::ParseArchive() {
   MIMEHeader* header = MIMEHeader::ParseHeader(&line_reader_);
   HeapVector<Member<ArchiveResource>> resources;
-  if (!ParseArchiveWithHeader(header, resources))
+  if (ParseArchiveWithHeader(header, resources)) {
+    creation_date_ = header->Date();
+  } else {
     resources.clear();
-  creation_date_ = header->Date();
+  }
   return resources;
 }
 
diff --git a/third_party/WebKit/Source/platform/mhtml/MHTMLParserTest.cpp b/third_party/WebKit/Source/platform/mhtml/MHTMLParserTest.cpp
index 37f2b1f..14eb955 100644
--- a/third_party/WebKit/Source/platform/mhtml/MHTMLParserTest.cpp
+++ b/third_party/WebKit/Source/platform/mhtml/MHTMLParserTest.cpp
@@ -390,7 +390,7 @@
 }
 
 TEST_F(MHTMLParserTest, DateParsing_ValidDate) {
-  // Missing encoding is treated as binary.
+  // Valid date is used.
   const char mhtml_data[] =
       "From: <Saved by Blink>\r\n"
       "Subject: Test Subject\r\n"
@@ -415,4 +415,14 @@
   EXPECT_EQ(expected_time, creation_time);
 }
 
+TEST_F(MHTMLParserTest, MissingBoundary) {
+  // No "boundary" parameter in the content type header means that parsing will
+  // be a failure and the header will be |nullptr|.
+  const char mhtml_data[] = "Content-Type: multipart/false\r\n";
+
+  HeapVector<Member<ArchiveResource>> resources =
+      ParseArchive(mhtml_data, sizeof(mhtml_data));
+  EXPECT_EQ(0U, resources.size());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 010a204..4411b92e 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -443,8 +443,9 @@
 }
 
 WebScopedVirtualTimePauser FrameSchedulerImpl::CreateWebScopedVirtualTimePauser(
+    const WTF::String& name,
     WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
-  return WebScopedVirtualTimePauser(renderer_scheduler_, duration);
+  return WebScopedVirtualTimePauser(renderer_scheduler_, duration, name);
 }
 
 void FrameSchedulerImpl::DidOpenActiveConnection() {
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.h
index 017b322..3d97273 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -77,6 +77,7 @@
                                 bool is_reload,
                                 bool is_main_frame) override;
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const WTF::String& name,
       WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;
   void OnFirstMeaningfulPaint() override;
   std::unique_ptr<ActiveConnectionHandle> OnActiveConnectionCreated() override;
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.cc
index 3aa1abf..5119f6e 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.cc
@@ -2417,8 +2417,10 @@
 
 WebScopedVirtualTimePauser
 RendererSchedulerImpl::CreateWebScopedVirtualTimePauser(
+    const char* name,
     WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
-  return WebScopedVirtualTimePauser(this, duration);
+  return WebScopedVirtualTimePauser(this, duration,
+                                    WebString(WTF::String(name)));
 }
 
 void RendererSchedulerImpl::RegisterTimeDomain(TimeDomain* time_domain) {
@@ -2509,6 +2511,14 @@
   helper_.RemoveTaskTimeObserver(task_time_observer);
 }
 
+bool RendererSchedulerImpl::ContainsLocalMainFrame() {
+  for (auto* page_scheduler : main_thread_only().page_schedulers) {
+    if (page_scheduler->IsMainFrameLocal())
+      return true;
+  }
+  return false;
+}
+
 void RendererSchedulerImpl::OnQueueingTimeForWindowEstimated(
     base::TimeDelta queueing_time,
     bool is_disjoint_window) {
@@ -2526,7 +2536,7 @@
     }
   }
 
-  if (!is_disjoint_window)
+  if (!is_disjoint_window || !ContainsLocalMainFrame())
     return;
 
   UMA_HISTOGRAM_TIMES("RendererScheduler.ExpectedTaskQueueingDuration",
@@ -2548,6 +2558,9 @@
 void RendererSchedulerImpl::OnReportFineGrainedExpectedQueueingTime(
     const char* split_description,
     base::TimeDelta queueing_time) {
+  if (!ContainsLocalMainFrame())
+    return;
+
   base::UmaHistogramCustomCounts(
       split_description, queueing_time.InMicroseconds(),
       kMinExpectedQueueingTimeBucket, kMaxExpectedQueueingTimeBucket,
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.h b/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.h
index 5e67c59..b20cf4de 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.h
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler.h
@@ -138,6 +138,7 @@
       base::TimeDelta main_thread_responsiveness_threshold) override;
   void SetRendererProcessType(RendererProcessType type) override;
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const char* name,
       WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;
 
   // AutoAdvancingVirtualTimeDomain::Observer implementation:
@@ -318,6 +319,8 @@
 
   void SetStoppedInBackground(bool) const;
 
+  bool ContainsLocalMainFrame();
+
   struct TaskQueuePolicy {
     // Default constructor of TaskQueuePolicy should match behaviour of a
     // newly-created task queue.
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler_unittest.cc
index fca09c50..5fc2bfb3 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/main_thread_scheduler_unittest.cc
@@ -3791,14 +3791,14 @@
 
   WebScopedVirtualTimePauser pauser =
       scheduler_->CreateWebScopedVirtualTimePauser(
-          WebScopedVirtualTimePauser::VirtualTaskDuration::kInstant);
+          "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kInstant);
 
   base::TimeTicks before = scheduler_->GetVirtualTimeDomain()->Now();
   EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
-  pauser.PauseVirtualTime(true);
+  pauser.PauseVirtualTime();
   EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
 
-  pauser.PauseVirtualTime(false);
+  pauser.UnpauseVirtualTime();
   EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
   base::TimeTicks after = scheduler_->GetVirtualTimeDomain()->Now();
   EXPECT_EQ(after, before);
@@ -3812,11 +3812,11 @@
 
   WebScopedVirtualTimePauser pauser =
       scheduler_->CreateWebScopedVirtualTimePauser(
-          WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+          "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
 
   base::TimeTicks before = scheduler_->GetVirtualTimeDomain()->Now();
-  pauser.PauseVirtualTime(true);
-  pauser.PauseVirtualTime(false);
+  pauser.PauseVirtualTime();
+  pauser.UnpauseVirtualTime();
   base::TimeTicks after = scheduler_->GetVirtualTimeDomain()->Now();
   EXPECT_GT(after, before);
 }
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.cc
index 4d79dbd..8fc795f 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -103,6 +103,7 @@
       reported_background_throttling_since_navigation_(false),
       has_active_connection_(false),
       nested_runloop_(false),
+      is_main_frame_local_(false),
       background_time_budget_pool_(nullptr),
       delegate_(delegate),
       weak_factory_(this) {
@@ -147,6 +148,14 @@
     frame_scheduler->SetKeepActive(keep_active);
 }
 
+bool PageSchedulerImpl::IsMainFrameLocal() const {
+  return is_main_frame_local_;
+}
+
+void PageSchedulerImpl::SetIsMainFrameLocal(bool is_local) {
+  is_main_frame_local_ = is_local;
+}
+
 std::unique_ptr<FrameSchedulerImpl> PageSchedulerImpl::CreateFrameSchedulerImpl(
     base::trace_event::BlameContext* blame_context,
     FrameScheduler::FrameType frame_type) {
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.h
index 951f14540..50cdf63 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -46,6 +46,8 @@
   void SetPageVisible(bool page_visible) override;
   void SetPageFrozen(bool) override;
   void SetKeepActive(bool) override;
+  bool IsMainFrameLocal() const override;
+  void SetIsMainFrameLocal(bool is_local) override;
 
   std::unique_ptr<FrameScheduler> CreateFrameScheduler(
       BlameContext*,
@@ -118,6 +120,7 @@
   bool reported_background_throttling_since_navigation_;
   bool has_active_connection_;
   bool nested_runloop_;
+  bool is_main_frame_local_;
   CPUTimeBudgetPool* background_time_budget_pool_;  // Not owned.
   PageScheduler::Delegate* delegate_;               // Not owned.
   base::WeakPtrFactory<PageSchedulerImpl> weak_factory_;
diff --git a/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
index 1d66fc7..09dd147 100644
--- a/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -532,16 +532,17 @@
   {
     WebScopedVirtualTimePauser virtual_time_pauser =
         frame_scheduler->CreateWebScopedVirtualTimePauser(
+            "test",
             WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
     EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
 
-    virtual_time_pauser.PauseVirtualTime(true);
+    virtual_time_pauser.PauseVirtualTime();
     EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
 
-    virtual_time_pauser.PauseVirtualTime(false);
+    virtual_time_pauser.UnpauseVirtualTime();
     EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
 
-    virtual_time_pauser.PauseVirtualTime(true);
+    virtual_time_pauser.PauseVirtualTime();
     EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
   }
 
@@ -563,8 +564,9 @@
   {
     WebScopedVirtualTimePauser virtual_time_pauser =
         frame_scheduler->CreateWebScopedVirtualTimePauser(
+            "test",
             WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
-    virtual_time_pauser.PauseVirtualTime(true);
+    virtual_time_pauser.PauseVirtualTime();
   }
 
   *unpaused = scheduler->GetVirtualTimeDomain()->Now();
@@ -630,21 +632,21 @@
 
   WebScopedVirtualTimePauser virtual_time_pauser1 =
       frame_scheduler->CreateWebScopedVirtualTimePauser(
-          WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+          "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
   WebScopedVirtualTimePauser virtual_time_pauser2 =
       frame_scheduler->CreateWebScopedVirtualTimePauser(
-          WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+          "test", WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
 
   EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
 
-  virtual_time_pauser1.PauseVirtualTime(true);
-  virtual_time_pauser2.PauseVirtualTime(true);
+  virtual_time_pauser1.PauseVirtualTime();
+  virtual_time_pauser2.PauseVirtualTime();
   EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
 
-  virtual_time_pauser2.PauseVirtualTime(false);
+  virtual_time_pauser2.UnpauseVirtualTime();
   EXPECT_FALSE(scheduler_->VirtualTimeAllowedToAdvance());
 
-  virtual_time_pauser1.PauseVirtualTime(false);
+  virtual_time_pauser1.UnpauseVirtualTime();
   EXPECT_TRUE(scheduler_->VirtualTimeAllowedToAdvance());
 }
 
diff --git a/third_party/WebKit/Source/platform/scheduler/public/frame_scheduler.h b/third_party/WebKit/Source/platform/scheduler/public/frame_scheduler.h
index 7aa9d2be..897537e 100644
--- a/third_party/WebKit/Source/platform/scheduler/public/frame_scheduler.h
+++ b/third_party/WebKit/Source/platform/scheduler/public/frame_scheduler.h
@@ -148,6 +148,7 @@
   // the WebScopedVirtualTimePauser returned by this method is initially
   // unpaused.
   virtual WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const String& name,
       WebScopedVirtualTimePauser::VirtualTaskDuration) = 0;
 
   // Tells the scheduler that a provisional load has started, the scheduler may
diff --git a/third_party/WebKit/Source/platform/scheduler/public/page_scheduler.h b/third_party/WebKit/Source/platform/scheduler/public/page_scheduler.h
index 13b2227..7171ba2 100644
--- a/third_party/WebKit/Source/platform/scheduler/public/page_scheduler.h
+++ b/third_party/WebKit/Source/platform/scheduler/public/page_scheduler.h
@@ -35,6 +35,9 @@
   // service workers, shared workers, or fetch keep-alive.
   // If true, then the scheduler should not freeze relevant task queues.
   virtual void SetKeepActive(bool) = 0;
+  // Whether the main frame of this page is local or not (remote).
+  virtual bool IsMainFrameLocal() const = 0;
+  virtual void SetIsMainFrameLocal(bool) = 0;
 
   // Creates a new FrameScheduler. The caller is responsible for deleting
   // it. All tasks executed by the frame scheduler will be attributed to
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc b/third_party/WebKit/Source/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc
index b2398f1..cb0dd97 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/web_scoped_virtual_time_pauser.cc
@@ -5,6 +5,7 @@
 #include "public/platform/WebScopedVirtualTimePauser.h"
 
 #include "base/trace_event/trace_event.h"
+#include "platform/instrumentation/tracing/TracedValue.h"
 #include "platform/scheduler/main_thread/main_thread_scheduler.h"
 
 namespace blink {
@@ -14,9 +15,11 @@
 
 WebScopedVirtualTimePauser::WebScopedVirtualTimePauser(
     scheduler::RendererSchedulerImpl* scheduler,
-    VirtualTaskDuration duration)
+    VirtualTaskDuration duration,
+    const WebString& name)
     : duration_(duration),
       scheduler_(scheduler),
+      debug_name_(name),
       trace_id_(WebScopedVirtualTimePauser::next_trace_id_++) {}
 
 WebScopedVirtualTimePauser::~WebScopedVirtualTimePauser() {
@@ -30,6 +33,7 @@
   paused_ = other.paused_;
   duration_ = other.duration_;
   scheduler_ = std::move(other.scheduler_);
+  debug_name_ = std::move(other.debug_name_);
   other.scheduler_ = nullptr;
   trace_id_ = other.trace_id_;
 }
@@ -42,27 +46,32 @@
   paused_ = other.paused_;
   duration_ = other.duration_;
   scheduler_ = std::move(other.scheduler_);
+  debug_name_ = std::move(other.debug_name_);
   trace_id_ = other.trace_id_;
   other.scheduler_ = nullptr;
   return *this;
 }
 
-void WebScopedVirtualTimePauser::PauseVirtualTime(bool paused) {
-  if (paused == paused_ || !scheduler_)
+void WebScopedVirtualTimePauser::PauseVirtualTime() {
+  if (paused_ || !scheduler_)
     return;
 
-  paused_ = paused;
-  if (paused_) {
-    TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
-        "renderer.scheduler", "WebScopedVirtualTimePauser::PauseVirtualTime",
-        trace_id_);
-    virtual_time_when_paused_ = scheduler_->IncrementVirtualTimePauseCount();
-  } else {
-    TRACE_EVENT_NESTABLE_ASYNC_END0(
-        "renderer.scheduler", "WebScopedVirtualTimePauser::PauseVirtualTime",
-        trace_id_);
-    DecrementVirtualTimePauseCount();
-  }
+  paused_ = true;
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+      "renderer.scheduler", "WebScopedVirtualTimePauser::PauseVirtualTime",
+      trace_id_, "name", debug_name_.Latin1());
+  virtual_time_when_paused_ = scheduler_->IncrementVirtualTimePauseCount();
+}
+
+void WebScopedVirtualTimePauser::UnpauseVirtualTime() {
+  if (!paused_ || !scheduler_)
+    return;
+
+  paused_ = false;
+  TRACE_EVENT_NESTABLE_ASYNC_END0(
+      "renderer.scheduler", "WebScopedVirtualTimePauser::PauseVirtualTime",
+      trace_id_);
+  DecrementVirtualTimePauseCount();
 }
 
 void WebScopedVirtualTimePauser::DecrementVirtualTimePauseCount() {
diff --git a/third_party/WebKit/Source/platform/scheduler/test/fake_frame_scheduler.h b/third_party/WebKit/Source/platform/scheduler/test/fake_frame_scheduler.h
index 02e88c1..1303dc6 100644
--- a/third_party/WebKit/Source/platform/scheduler/test/fake_frame_scheduler.h
+++ b/third_party/WebKit/Source/platform/scheduler/test/fake_frame_scheduler.h
@@ -122,6 +122,7 @@
   }
   PageScheduler* GetPageScheduler() const override { return page_scheduler_; }
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const WTF::String& name,
       WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
     return WebScopedVirtualTimePauser();
   }
diff --git a/third_party/WebKit/Source/platform/scheduler/test/fake_page_scheduler.h b/third_party/WebKit/Source/platform/scheduler/test/fake_page_scheduler.h
index f4f93a94..efff7df 100644
--- a/third_party/WebKit/Source/platform/scheduler/test/fake_page_scheduler.h
+++ b/third_party/WebKit/Source/platform/scheduler/test/fake_page_scheduler.h
@@ -48,9 +48,12 @@
     return is_throttling_exempt_;
   }
 
+  // PageScheduler implementation:
   void SetPageVisible(bool is_page_visible) override {}
   void SetPageFrozen(bool is_page_frozen) override {}
   void SetKeepActive(bool keep_active) override {}
+  bool IsMainFrameLocal() const override { return true; }
+  void SetIsMainFrameLocal(bool is_local) override {}
 
   std::unique_ptr<FrameScheduler> CreateFrameScheduler(
       BlameContext* blame_context,
diff --git a/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc
index f420490..72ced3e 100644
--- a/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc
@@ -7,6 +7,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "build/build_config.h"
+#include "platform/wtf/text/WTFString.h"
 #include "public/platform/WebThread.h"
 
 namespace blink {
@@ -130,8 +131,10 @@
 
 WebScopedVirtualTimePauser
 FakeRendererScheduler::CreateWebScopedVirtualTimePauser(
+    const char* name,
     WebScopedVirtualTimePauser::VirtualTaskDuration duration) {
-  return WebScopedVirtualTimePauser(nullptr, duration);
+  return WebScopedVirtualTimePauser(nullptr, duration,
+                                    WebString(WTF::String(name)));
 }
 
 }  // namespace scheduler
diff --git a/third_party/WebKit/public/platform/WebScopedVirtualTimePauser.h b/third_party/WebKit/public/platform/WebScopedVirtualTimePauser.h
index 5ee1de2..e9d5a50c 100644
--- a/third_party/WebKit/public/platform/WebScopedVirtualTimePauser.h
+++ b/third_party/WebKit/public/platform/WebScopedVirtualTimePauser.h
@@ -7,6 +7,7 @@
 
 #include "WebCommon.h"
 #include "base/time/time.h"
+#include "third_party/WebKit/public/platform/WebString.h"
 
 namespace blink {
 namespace scheduler {
@@ -25,7 +26,8 @@
   // Note simply creating a WebScopedVirtualTimePauser doesn't cause VirtualTime
   // to pause, instead you need to call PauseVirtualTime.
   WebScopedVirtualTimePauser(scheduler::RendererSchedulerImpl*,
-                             VirtualTaskDuration);
+                             VirtualTaskDuration,
+                             const WebString& debug_name);
 
   WebScopedVirtualTimePauser();
   ~WebScopedVirtualTimePauser();
@@ -40,7 +42,8 @@
   // Virtual time will be paused if any WebScopedVirtualTimePauser votes to
   // pause it, and only unpaused only if all WebScopedVirtualTimePauser are
   // either destroyed or vote to unpause.
-  void PauseVirtualTime(bool paused);
+  void PauseVirtualTime();
+  void UnpauseVirtualTime();
 
  private:
   void DecrementVirtualTimePauseCount();
@@ -49,6 +52,7 @@
   bool paused_ = false;
   VirtualTaskDuration duration_ = VirtualTaskDuration::kInstant;
   scheduler::RendererSchedulerImpl* scheduler_;  // NOT OWNED
+  WebString debug_name_;
   int trace_id_;
 
   static int next_trace_id_;
diff --git a/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h
index 2b0ec0c..d8b75fd 100644
--- a/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h
@@ -64,6 +64,7 @@
       base::TimeDelta main_thread_responsiveness_threshold) override;
   void SetRendererProcessType(RendererProcessType type) override;
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const char* name,
       WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;
 
  private:
diff --git a/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h
index 4e36ff1..b49fe28 100644
--- a/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h
@@ -69,8 +69,9 @@
   MOCK_METHOD1(SetRAILModeObserver, void(RAILModeObserver*));
   MOCK_METHOD1(MainThreadSeemsUnresponsive, bool(base::TimeDelta));
   MOCK_METHOD1(SetRendererProcessType, void(RendererProcessType));
-  MOCK_METHOD1(CreateWebScopedVirtualTimePauser,
+  MOCK_METHOD2(CreateWebScopedVirtualTimePauser,
                WebScopedVirtualTimePauser(
+                   const char* name,
                    WebScopedVirtualTimePauser::VirtualTaskDuration));
 
  private:
diff --git a/third_party/WebKit/public/platform/scheduler/web_main_thread_scheduler.h b/third_party/WebKit/public/platform/scheduler/web_main_thread_scheduler.h
index a9eae512..3d142487 100644
--- a/third_party/WebKit/public/platform/scheduler/web_main_thread_scheduler.h
+++ b/third_party/WebKit/public/platform/scheduler/web_main_thread_scheduler.h
@@ -223,6 +223,7 @@
   // the WebScopedVirtualTimePauser returned by this method is initially
   // unpaused.
   virtual WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const char* name,
       WebScopedVirtualTimePauser::VirtualTaskDuration duration =
           WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant) = 0;
 
diff --git a/third_party/WebKit/public/platform/web_feature.mojom b/third_party/WebKit/public/platform/web_feature.mojom
index ed0d4f13..6532617 100644
--- a/third_party/WebKit/public/platform/web_feature.mojom
+++ b/third_party/WebKit/public/platform/web_feature.mojom
@@ -1891,6 +1891,8 @@
   kReadableStreamConstructor = 2399,
   kWritableStreamConstructor = 2400,
   kTransformStreamConstructor = 2401,
+  kNegativeBackgroundSize = 2402,
+  kNegativeMaskSize = 2403,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/tools/binary_size/README.md b/tools/binary_size/README.md
index 8b9a87c..20682703 100644
--- a/tools/binary_size/README.md
+++ b/tools/binary_size/README.md
@@ -1,8 +1,8 @@
 # Tools for Analyzing Chrome's Binary Size
 
-These tools currently focus on Android. They somewhat work with Linux builds,
-but not as well. As for Windows, some great tools already exist and are
-documented here:
+These tools currently focus on supporting Android. They somewhat work with
+Linux builds. As for Windows, some great tools already exist and are documented
+here:
 
  * https://www.chromium.org/developers/windows-binary-sizes
 
@@ -73,13 +73,17 @@
 
 `.size` files are gzipped plain text files that contain:
 
-1. A list of .so section sizes, as reported by `readelf -S`,
-1. Metadata (GN args, filenames, timestamps, git revision, build id),
+1. A list of section sizes, including:
+   * .so sections as reported by `readelf -S`
+   * .pak and .dex sections for apk files
+1. Metadata (apk size, GN args, filenames, timestamps, git revision, build id),
 1. A list of symbols, including name, address, size,
-  padding (caused by alignment), and associated `.o` / `.cc` files.
+  padding (caused by alignment), and associated source/object files.
 
 #### How are Symbols Collected?
 
+##### Native Symbols
+
 1. Symbol list is Extracted from linker `.map` file.
    * Map files contain some unique pieces of information compared to `nm` output,
       such as `** merge strings` entries, and some unnamed symbols (which
@@ -102,6 +106,46 @@
   set to `{shared}/$SYMBOL_COUNT`. This collapsing is done only for symbols owned
   by a large number of paths.
 
+##### Pak Symbols
+
+1. Grit creates a mapping between numeric id and textual id for grd files.
+   * A side effect of pak whitelist generation is a mapping of `.cc` to numeric
+     id.
+   * A complete per-apk mapping of numeric id to textual id is stored in the
+     `output_dir/size-info` dir.
+1. `supersize` uses these two mappings to find associated source files for the
+  pak entries found in all of the apk's `.pak` files.
+   * Pak entries with the same name are merged into a single symbol.
+     * This is the case of pak files for translations.
+   * The original grd file paths are stored in the full name of each symbol.
+
+##### Dex Symbols
+
+1. Java compile targets create a mapping between java fully qualified names
+  (FQN) and source files.
+   * For `.java` files the FQN of the public class is mapped to the file.
+   * For `.srcjar` files the FQN of the public class is mapped to the `.srcjar`
+     file path.
+   * A complete per-apk class FQN to source mapping is stored in the
+     `output_dir/size-info` dir.
+1. The `apkanalyzer` sdk tool is used to find the size and FQN of entries in
+  the dex file.
+   * If a proguard `.mapping` file is available, that is used to get back the
+     original FQN.
+1. The output from `apkanalyzer` is used by `supersize` along with the mapping
+  file to find associated source files for the dex entries found in all of the
+  apk's `.dex` files.
+
+##### Common Symbols
+
+1. Shared bytes are stored in symbols with names starting with `Overhead: `.
+   * Elf file, dex file, pak files, apk files all have compression overhead.
+   * These are treated as padding-only symbols to de-emphasize them in diffs.
+   * It is expected that these symbols have minor fluctuations since they are
+     affected by changes in compressibility.
+1. All other files in an apk have one symbol each under the `.other` section
+  with their corresponding path in the apk as their associated path.
+
 #### What Other Processing Happens?
 
 1. Path normalization:
@@ -122,7 +166,7 @@
      * `template_name`: Name without argument parameters.
      * `full_name`: Name with all parameters.
 
-1. Clustering
+1. Clustering:
    * Compiler & linker optimizations can cause symbols to be broken into
      multiple parts to become candidates for inlining ("partial inlining").
    * These symbols are sometimes suffixed with "`[clone]`" (removed by
@@ -132,9 +176,18 @@
    * Clustering is done by default on `SizeInfo.symbols`. To view unclustered
      symbols, use `SizeInfo.raw_symbols`.
 
-1. Diffing
+1. Diffing:
    * Some heuristics for matching up before/after symbols.
 
+1. Simulated compression:
+   * Only some `.pak` files are compressed and others are kept uncompressed.
+   * To get a reasonable idea of actual impact to final apk size, we use a
+     constant compression factor for all the compressed `.pak` files.
+     * This prevents swings in compressed sizes for all symbols when new
+       entries are added or old entries are removed.
+     * The constant is chosen so that it minimizes overall discrepancy with
+       actual total compressed sizes.
+
 #### Is Super Size a Generic Tool?
 
 No. Most of the logic is would could work for any ELF executable. However, being
@@ -142,7 +195,8 @@
 
  * Assumes `.ninja` build rules are available.
  * Heuristic for locating `.so` given `.apk`.
- * Roadmap includes `.pak` file analysis.
+ * Requires `size-info` dir in output directory to analyze `.pak` and `.dex`
+   files.
 
 ### Usage: archive
 
@@ -235,12 +289,6 @@
 ### Roadmap
 
 1. [Better Linux support](https://bugs.chromium.org/p/chromium/issues/detail?id=717550) (clang+lld+lto vs gcc+gold).
-1. More `archive` features:
-    * Find out more about 0xffffffffffffffff addresses, and why such large
-      gaps exist after them. ([crbug/709050](https://bugs.chromium.org/p/chromium/issues/detail?id=709050))
-    * Collect .pak file information (using .o.whitelist files)
-    * Collect java symbol information
-    * Collect .apk entry information
 1. More `console` features:
    * Add `SplitByName()` - Like `GroupByName()`, but recursive.
    * A canned query, that does what ShowGlobals does (as described in [Windows Binary Sizes](https://www.chromium.org/developers/windows-binary-sizes)).
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index 2930353..28de0c7 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -808,15 +808,15 @@
   return symbols
 
 
-def _ParseApkOtherSymbols(section_sizes, apk_path):
+def _ParseApkOtherSymbols(section_sizes, apk_path, apk_so_path):
   apk_name = os.path.basename(apk_path)
   apk_symbols = []
   zip_info_total = 0
   with zipfile.ZipFile(apk_path) as z:
     for zip_info in z.infolist():
       zip_info_total += zip_info.compress_size
-      # Skip shared library, pak, and dex files as they are accounted for.
-      if (zip_info.filename.endswith('.so')
+      # Skip main shared library, pak, and dex files as they are accounted for.
+      if (zip_info.filename == apk_so_path
           or zip_info.filename.endswith('.dex')
           or zip_info.filename.endswith('.pak')):
         continue
@@ -937,7 +937,8 @@
           section_sizes, metadata, apk_elf_result)
     raw_symbols.extend(
         _ParseDexSymbols(section_sizes, apk_path, output_directory))
-    raw_symbols.extend(_ParseApkOtherSymbols(section_sizes, apk_path))
+    raw_symbols.extend(
+        _ParseApkOtherSymbols(section_sizes, apk_path, apk_so_path))
   elif pak_files and pak_info_file:
     pak_symbols_by_id = _FindPakSymbolsFromFiles(
         pak_files, pak_info_file, output_directory)
diff --git a/tools/binary_size/libsupersize/integration_test.py b/tools/binary_size/libsupersize/integration_test.py
index 16f9cfd..54161f3 100755
--- a/tools/binary_size/libsupersize/integration_test.py
+++ b/tools/binary_size/libsupersize/integration_test.py
@@ -43,6 +43,7 @@
 
 # Generated file paths relative to apk
 _TEST_APK_SO_PATH = 'test.so'
+_TEST_APK_SMALL_SO_PATH = 'smalltest.so'
 _TEST_APK_DEX_PATH = 'test.dex'
 
 update_goldens = False
@@ -133,6 +134,9 @@
       elf_file.write(IntegrationTest._CreateBlankData(27))
     with zipfile.ZipFile(_TEST_APK_PATH, 'w') as apk_file:
       apk_file.write(_TEST_ELF_PATH, _TEST_APK_SO_PATH)
+      # Exactly 8MB of data (2^23).
+      apk_file.writestr(
+          _TEST_APK_SMALL_SO_PATH, IntegrationTest._CreateBlankData(23))
       pak_rel_path = os.path.relpath(_TEST_APK_PAK_PATH, _TEST_APK_ROOT_DIR)
       apk_file.write(_TEST_APK_PAK_PATH, pak_rel_path)
       # Exactly 8MB of data (2^23).
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
index 4fb4366..5faea8e 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_Apk.golden
@@ -1,5 +1,5 @@
 apk_file_name=test.apk
-apk_size=142613786
+apk_size=151002494
 elf_arch=arm
 elf_build_id=WhatAnAmazingBuildId
 elf_file_name=elf
@@ -42,8 +42,8 @@
 * Padding accounts for 0 bytes (0.0%)
 * Contains 0 aliases
 * 0 symbols have shared ownership
-Section .other: has 100.0% of 33984483 bytes accounted for from 2 symbols. 0 bytes are unaccounted for.
-* Padding accounts for 33984483 bytes (100.0%)
+Section .other: has 100.0% of 42373191 bytes accounted for from 3 symbols. 0 bytes are unaccounted for.
+* Padding accounts for 33984583 bytes (80.2%)
 * Contains 0 aliases
 * 0 symbols have shared ownership
 .data@2de7000(size_without_padding=4,padding=0,full_name=google::protobuf::internal::pLinuxKernelCmpxchg,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1)
@@ -254,7 +254,8 @@
 .dex@0(size_without_padding=975347,padding=0,full_name=org.chromium,object_path=test.apk/prebuilt/org/chromium,source_path=,flags={gen},num_aliases=1)
 .dex@0(size_without_padding=1792,padding=0,full_name=org,object_path=test.apk/prebuilt/org,source_path=,flags={gen},num_aliases=1)
 .dex@0(size_without_padding=4616803,padding=0,full_name=* Unattributed Dex,object_path=test.apk/prebuilt,source_path=,flags={gen},num_aliases=1)
-.other@0(size_without_padding=0,padding=312,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1)
+.other@0(size_without_padding=8388608,padding=0,full_name=smalltest.so,object_path=test.apk/other/smalltest.so,source_path=,flags={},num_aliases=1)
+.other@0(size_without_padding=0,padding=412,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1)
 .other@0(size_without_padding=0,padding=33984171,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1)
 .rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2)
 .rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 59bf22f..d819fee8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7737,6 +7737,14 @@
   <int value="3" label="User disabled"/>
 </enum>
 
+<enum name="CrosComponentManagerError">
+  <int value="0" label="NONE"/>
+  <int value="1" label="UNKNOWN_COMPONENT"/>
+  <int value="2" label="INSTALL_FAILURE"/>
+  <int value="3" label="MOUNT_FAILURE"/>
+  <int value="4" label="COMPATIBILITY_CHECK_FAILED"/>
+</enum>
+
 <enum name="CrosDisksArchiveType">
   <int value="0" label="Unknown"/>
   <int value="1" label="ZIP"/>
@@ -18124,6 +18132,8 @@
   <int value="2399" label="ReadableStreamConstructor"/>
   <int value="2400" label="WritableStreamConstructor"/>
   <int value="2401" label="TransformStreamConstructor"/>
+  <int value="2402" label="NegativeBackgroundSize"/>
+  <int value="2403" label="NegativeMaskSize"/>
 </enum>
 
 <enum name="FeedbackSource">
@@ -35079,6 +35089,13 @@
   <int value="2" label="Default"/>
 </enum>
 
+<enum name="PeerConnectionSdpFormatReceived">
+  <int value="0" label="No tracks"/>
+  <int value="1" label="Simple"/>
+  <int value="2" label="Complex (Plan B)"/>
+  <int value="3" label="Complex (Unified Plan)"/>
+</enum>
+
 <enum name="PeerConnectionSdpSemanticNegotiated">
   <int value="0" label="None"/>
   <int value="1" label="Plan B"/>
@@ -45986,6 +46003,7 @@
   <int value="4" label="Chrome needs audio permission for voice input"/>
   <int value="5" label="A non-specific unsupported feature was encountered"/>
   <int value="6" label="The keyboard version is out-of-date"/>
+  <int value="7" label="The default search engine wasn't selected"/>
 </enum>
 
 <enum name="VRViewerType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 872700f..506c8c3 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3012,6 +3012,8 @@
 </histogram>
 
 <histogram name="Arc.PlayStoreShown.TimeDelta" units="ms">
+<!-- Name completed by histogram_suffixes name="ArcUserTypes" -->
+
   <owner>yusukes@google.com</owner>
   <owner>khmel@google.com</owner>
   <summary>
@@ -3032,6 +3034,15 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.Provisioning.Result.Child" enum="ArcProvisioningResult">
+  <owner>alexchau@google.com</owner>
+  <owner>phweiss@google.com</owner>
+  <summary>
+    The result (success or the type of failure) of ARC provisioning for child
+    accounts.
+  </summary>
+</histogram>
+
 <histogram name="Arc.Provisioning.Result.Managed" enum="ArcProvisioningResult">
   <owner>alexchau@google.com</owner>
   <owner>phweiss@google.com</owner>
@@ -3061,6 +3072,15 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.Provisioning.TimeDelta.Failure.Child" units="ms">
+  <owner>alexchau@google.com</owner>
+  <owner>phweiss@google.com</owner>
+  <summary>
+    Elapsed time from the signing in process start to call to onSignInFailed for
+    child account.
+  </summary>
+</histogram>
+
 <histogram name="Arc.Provisioning.TimeDelta.Failure.Managed" units="ms">
   <owner>alexchau@google.com</owner>
   <owner>phweiss@google.com</owner>
@@ -3088,6 +3108,15 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.Provisioning.TimeDelta.Success.Child" units="ms">
+  <owner>alexchau@google.com</owner>
+  <owner>phweiss@google.com</owner>
+  <summary>
+    Elapsed time from the signing in process start to successful call to
+    onSignInComplete for child account.
+  </summary>
+</histogram>
+
 <histogram name="Arc.Provisioning.TimeDelta.Success.Managed" units="ms">
   <owner>alexchau@google.com</owner>
   <owner>phweiss@google.com</owner>
@@ -3116,6 +3145,14 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.Reauthorization.Result.Child" enum="ArcProvisioningResult">
+  <owner>khmel@google.com</owner>
+  <summary>
+    The result (success or the type of failure) of ARC reauthorization for child
+    account.
+  </summary>
+</histogram>
+
 <histogram name="Arc.Reauthorization.Result.Managed"
     enum="ArcProvisioningResult">
   <owner>khmel@google.com</owner>
@@ -10943,6 +10980,19 @@
   </summary>
 </histogram>
 
+<histogram name="ComponentUpdater.ChromeOS.InstallResult"
+    enum="CrosComponentManagerError">
+  <owner>xiaochu@chromium.org</owner>
+  <summary>
+    Chrome OS only. Installation error code in CrosComponentManager.
+  </summary>
+</histogram>
+
+<histogram name="ComponentUpdater.ChromeOS.MountTime" units="ms">
+  <owner>xiaochu@chromium.org</owner>
+  <summary>Chrome OS only. Time it takes to mount a component image.</summary>
+</histogram>
+
 <histogram name="ComponentUpdater.UpdateCompleteResult" enum="BooleanError">
   <owner>sorin@chromium.org</owner>
   <summary>The result of an install or an update check.</summary>
@@ -103111,6 +103161,20 @@
   </summary>
 </histogram>
 
+<histogram name="WebRTC.PeerConnection.SdpFormatReceived"
+    enum="PeerConnectionSdpFormatReceived">
+  <owner>steveanton@chromium.org</owner>
+  <summary>
+    What SDP format is received in the remote offer. The value &quot;no
+    tracks&quot; means that no audio or video tracks were received. The value
+    &quot;simple&quot; means that at most one audio and at most one video track
+    was received. The value &quot;complex&quot; means that more than one audio
+    or more than one video track was received, and how this was signaled is
+    indicated (&quot;Plan B&quot; meaning with a=ssrc lines within the same m=
+    section and &quot;Unified Plan&quot; meaning with a separate m= section).
+  </summary>
+</histogram>
+
 <histogram name="WebRTC.PeerConnection.SdpSemanticNegotiated"
     enum="PeerConnectionSdpSemanticNegotiated">
   <owner>hta@chromium.org</owner>
@@ -105703,7 +105767,10 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="ArcUserTypes" separator=".">
+  <suffix name="Child" label="User with child accounts."/>
   <suffix name="Managed" label="User with forced Play Store feature"/>
+  <suffix name="RobotAccount"
+      label="Managed devices with a robot account (Public Session or Kiosk)"/>
   <suffix name="Unmanaged" label="User with optional Play Store feature"/>
   <affected-histogram name="Arc.PlayStoreShown.TimeDelta"/>
 </histogram_suffixes>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index df3350a..397db437 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3452,6 +3452,18 @@
       SessionTracker::GetRoundedDurationInSeconds.
     </summary>
   </metric>
+  <metric name="StartAction">
+    <summary>
+      A metric to track specifically how the user got into XR presentation. 0:
+      Other, catch all. 1: RequestFrom2DBrowsing, the page requested
+      presentation while Chrome was in 2D mode. 2: RequestFromVRBrowsing, the
+      page requested presentation while Chrome was in VR browsing mode. 3:
+      HeadsetActivation, the user activated the VR headset while in 2D browsing
+      on the page, which listens for headset activations to request
+      presentation. 4: DeepLinkedApp, The page was launched in Chrome from the
+      VR system home (e.g., Daydream Home) and requested presentation.
+    </summary>
+  </metric>
 </event>
 
 </ukm-configuration>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index cdab3686..d5a3825 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -25,7 +25,7 @@
 gpu_perftests,"reveman@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU
 jetstream,hablich@chromium.org,
 kraken,hablich@chromium.org,
-load_library_perf_tests,,
+load_library_perf_tests,"xhwang@chromium.org, crouleau@chromium.org",Internals>Media>Encrypted
 loading.desktop,"kouhei@chromium.org, ksakamoto@chromium.org",
 loading.mobile,"kouhei@chromium.org, ksakamoto@chromium.org",
 media.desktop,"johnchen@chromium.org, crouleau@chromium.org",Internals>Media
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 529f4ad..613b336 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -913,7 +913,9 @@
         'Internals>GPU', False),
     'tracing_perftests': BenchmarkMetadata(
         'kkraynov@chromium.org, primiano@chromium.org', None, False),
-    'load_library_perf_tests': BenchmarkMetadata(None, None, False),
+    'load_library_perf_tests': BenchmarkMetadata(
+        'xhwang@chromium.org, crouleau@chromium.org',
+        'Internals>Media>Encrypted', False),
     'media_perftests': BenchmarkMetadata('crouleau@chromium.org', None, False),
     'performance_browser_tests': BenchmarkMetadata(
         'miu@chromium.org', None, False),
@@ -1079,6 +1081,8 @@
 #     of the isolate you are running.
 # shards: shard indices that you want the isolate to run on.  If omitted
 #     will run on all shards.
+# telemetry: boolean indicating if this is a telemetry test.  If omitted
+#     assumed to be true.
 NEW_PERF_RECIPE_FYI_TESTERS = {
   'testers' : {
     'One Buildbot Step Test Builder': {
@@ -1086,6 +1090,11 @@
         {
           'isolate': 'telemetry_perf_tests_experimental',
           'extra_args': ['--xvfb'],
+        },
+        {
+          'isolate': 'load_library_perf_tests',
+          'shards': [0],
+          'telemetry': False,
         }
       ],
       'platform': 'linux',
@@ -1106,10 +1115,9 @@
           'isolate': 'performance_test_suite',
         },
         {
-          'isolate': 'load_library_perf_tests_v2',
-          'test_suite_name': 'load_library_perf_tests',
-          'extra_args': ["--non-telemetry=true"],
-          'shards': [0]
+          'isolate': 'load_library_perf_tests',
+          'shards': [0],
+          'telemetry': False,
         }
       ],
       'platform': 'mac',
@@ -1184,7 +1192,7 @@
   return len(dimensions)
 
 
-def generate_performance_test(tester_config, test):
+def generate_telemetry_args(tester_config):
   # First determine the browser that you need based on the tester
   browser_name = ''
   # For trybot testing we always use the reference build
@@ -1203,20 +1211,39 @@
 
   test_args = [
     '-v',
-    '--browser=%s' % browser_name
+    '--browser=%s' % browser_name,
+    '--upload-results'
   ]
-  test_args += test.get('extra_args', [])
+
+  if browser_name == 'android-webview':
+    test_args.append(
+        '--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk')
 
   # Appending testing=true if we only want to run a subset of benchmarks
   # for quicker testing
   if tester_config.get('testing', False):
     test_args.append('--testing=true')
 
+  return test_args
+
+def generate_non_telemetry_args():
+  # --non-telemetry tells run_performance_tests.py that this test needs
+  #   to be executed differently
+  # --migrated-test tells run_performance_test_wrapper that this has
+  #   non-telemetry test has been migrated to the new recipe.
+  return [
+    '--non-telemetry=true',
+    '--migrated-test=true'
+  ]
+
+def generate_performance_test(tester_config, test):
+  if test.get('telemetry', True):
+    test_args = generate_telemetry_args(tester_config)
+  else:
+    test_args = generate_non_telemetry_args()
+  # Append any additional args specific to an isolate
+  test_args += test.get('extra_args', [])
   isolate_name = test['isolate']
-  if browser_name == 'android-webview':
-    test_args.append(
-        '--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk')
-    isolate_name = 'telemetry_perf_webview_tests'
 
   # Check to see if the name is different than the isolate
   test_suite = isolate_name
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index ddf03b3b..2433c2b1 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -210,6 +210,8 @@
 crbug.com/755556 [ Android ] smoothness.tough_animation_cases/balls_css_keyframe_animations_composited_transform.html [ Skip ]
 crbug.com/755556 [ Mac ] smoothness.tough_animation_cases/mix_blend_mode_animation_difference.html [ Skip ]
 crbug.com/755556 [ Mac ] smoothness.tough_animation_cases/mix_blend_mode_animation_hue.html [ Skip ]
+crbug.com/829499 [ Android_One ] smoothness.tough_animation_cases/css_animations_many_keyframes.html?N=0316 [ Skip ]
+crbug.com/829499 [ Android_One ] smoothness.tough_animation_cases/web_animations_many_keyframes.html?N=0316 [ Skip ]
 
 # Benchmark: smoothness.tough_canvas_cases
 crbug.com/785485 [ Android_Webview ] smoothness.tough_canvas_cases/http://www.kevs3d.co.uk/dev/canvask3d/k3d_test.html [ Skip ]
@@ -241,6 +243,7 @@
 crbug.com/574485 [ Android_Svelte ] smoothness.tough_webgl_ad_cases/* [ Skip ]
 
 # Benchmark: system_health.common_desktop
+crbug.com/828917 [ Mac ] system_health.common_desktop/multitab:misc:typical24 [ Skip ]
 crbug.com/728576 [ Mac ] system_health.common_desktop/browse:news:cnn [ Skip ]
 crbug.com/64939 [ All ] system_health.common_desktop/play:media:pandora [ Skip ]
 crbug.com/809146 [ All ] system_health.common_desktop/browse:media:tumblr [ Skip ]
@@ -405,6 +408,7 @@
 # Benchmark: v8.runtime_stats.top_25
 crbug.com/664318 [ Android ] v8.runtime_stats.top_25/* [ Skip ]
 crbug.com/664318 [ Win ] v8.runtime_stats.top_25/* [ Skip ]
+crbug.com/829504 [ Linux ] v8.runtime_stats.top_25/https://www.google.de/search?q=v8 [ Skip ]
 
 # Benchmark: wasm
 [ Android_One ] wasm/WasmSpaceBuggy [ Skip ]
diff --git a/tools/perf/metrics/timeline.py b/tools/perf/metrics/timeline.py
index 74deec2d..61042c70 100644
--- a/tools/perf/metrics/timeline.py
+++ b/tools/perf/metrics/timeline.py
@@ -16,7 +16,6 @@
 TimelineThreadCategories = {
     "Chrome_InProcGpuThread": "GPU",
     "CrGpuMain": "GPU",
-    "AsyncTransferThread": "GPU_transfer",
     "VizCompositorThread": "display_compositor",
     "CrBrowserMain": "browser",
     "Browser Compositor": "browser",
diff --git a/tools/perf/unowned_benchmarks.txt b/tools/perf/unowned_benchmarks.txt
index a9d36d73..1dd0cbe5 100644
--- a/tools/perf/unowned_benchmarks.txt
+++ b/tools/perf/unowned_benchmarks.txt
@@ -1,4 +1,3 @@
-load_library_perf_tests
 smoothness.tough_image_decode_cases
 thread_times.key_hit_test_cases
 thread_times.key_noop_cases
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
index 67e142a..fc09c0b7 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
@@ -61,6 +61,12 @@
         .Append(FILE_PATH_LITERAL("auditor"))
         .Append(FILE_PATH_LITERAL("safe_list.txt"));
 
+const base::FilePath kClangToolSwitchesPath =
+    base::FilePath(FILE_PATH_LITERAL("tools"))
+        .Append(FILE_PATH_LITERAL("traffic_annotation"))
+        .Append(FILE_PATH_LITERAL("auditor"))
+        .Append(FILE_PATH_LITERAL("traffic_annotation_extractor_switches.txt"));
+
 // The folder that includes the latest Clang built-in library. Inside this
 // folder, there should be another folder with version number, like
 // '.../lib/clang/6.0.0', which would be passed to the clang tool.
@@ -77,10 +83,6 @@
         .Append(FILE_PATH_LITERAL("scripts"))
         .Append(FILE_PATH_LITERAL("run_tool.py"));
 
-const std::string kClangToolSwitches[] = {
-    "-Wno-comment", "-Wno-tautological-unsigned-enum-zero-compare",
-    "-Wno-tautological-constant-compare", "-Wno-error=unknown-warning-option"};
-
 // Checks if the list of |path_filters| include the given |file_path|, or there
 // are path filters which are a folder (don't have a '.' in their name), and
 // match the file name.
@@ -112,6 +114,14 @@
   DCHECK(!source_path.empty());
   DCHECK(!build_path.empty());
   DCHECK(!clang_tool_path.empty());
+
+  std::string file_content;
+  if (base::ReadFileToString(kClangToolSwitchesPath, &file_content)) {
+    clang_tool_switches_ = base::SplitString(
+        file_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  } else {
+    LOG(ERROR) << "Could not read " << kClangToolSwitchesPath;
+  }
 }
 
 TrafficAnnotationAuditor::~TrafficAnnotationAuditor() = default;
@@ -168,8 +178,9 @@
       base::MakeAbsoluteFilePath(clang_tool_path_).MaybeAsASCII().c_str(),
       base::MakeAbsoluteFilePath(GetClangLibraryPath()).MaybeAsASCII().c_str());
 
-  for (const std::string& item : kClangToolSwitches)
+  for (const std::string& item : clang_tool_switches_)
     fprintf(options_file, "--tool-arg=--extra-arg=%s ", item.c_str());
+
   if (use_compile_commands)
     fprintf(options_file, "--all ");
 
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
index 62d6932e..7657437 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
@@ -159,6 +159,8 @@
   const base::FilePath build_path_;
   const base::FilePath clang_tool_path_;
 
+  std::vector<std::string> clang_tool_switches_;
+
   TrafficAnnotationExporter exporter_;
 
   std::string clang_tool_raw_output_;
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_extractor_switches.txt b/tools/traffic_annotation/auditor/traffic_annotation_extractor_switches.txt
new file mode 100644
index 0000000..2b5676e
--- /dev/null
+++ b/tools/traffic_annotation/auditor/traffic_annotation_extractor_switches.txt
@@ -0,0 +1,4 @@
+-Wno-comment
+-Wno-tautological-unsigned-enum-zero-compare
+-Wno-tautological-constant-compare
+-Wno-error=unknown-warning-option
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 5210c33..f95ea35 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -36,7 +36,6 @@
  <item id="cast_socket" hash_code="115192205" type="0" content_hash_code="63056899" os_list="linux,windows" file_path="components/cast_channel/cast_socket.cc"/>
  <item id="cast_udp_socket" hash_code="22573197" type="0" content_hash_code="75328301" os_list="linux,windows" file_path="components/mirroring/service/udp_socket_client.cc"/>
  <item id="cast_udp_transport" hash_code="5576536" type="0" content_hash_code="107643273" os_list="linux,windows" file_path="media/cast/net/udp_transport_impl.cc"/>
- <item id="certificate_reporting_service_test" hash_code="98123372" type="0" content_hash_code="136253658" os_list="linux,windows" file_path="chrome/browser/safe_browsing/certificate_reporting_service.cc"/>
  <item id="certificate_verifier" hash_code="113553577" type="0" content_hash_code="62346354" os_list="linux,windows" file_path="net/cert_net/cert_net_fetcher_impl.cc"/>
  <item id="chrome_apps_socket_api" hash_code="8591273" type="0" content_hash_code="70868355" os_list="linux,windows" file_path="extensions/browser/api/socket/socket.cc"/>
  <item id="chrome_cleaner" hash_code="27071967" type="0" content_hash_code="111240292" os_list="windows" file_path="chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_fetcher_win.cc"/>
diff --git a/ui/app_list/test/test_search_result.cc b/ui/app_list/test/test_search_result.cc
index 33969e45..1284c9d 100644
--- a/ui/app_list/test/test_search_result.cc
+++ b/ui/app_list/test/test_search_result.cc
@@ -16,9 +16,4 @@
   set_id(id);
 }
 
-std::unique_ptr<SearchResult> TestSearchResult::Duplicate() const {
-  NOTREACHED();
-  return nullptr;
-}
-
 }  // namespace app_list
diff --git a/ui/app_list/test/test_search_result.h b/ui/app_list/test/test_search_result.h
index 24d66e5..43d5f71 100644
--- a/ui/app_list/test/test_search_result.h
+++ b/ui/app_list/test/test_search_result.h
@@ -21,9 +21,6 @@
 
   void set_result_id(const std::string& id);
 
-  // SearchResult:
-  std::unique_ptr<SearchResult> Duplicate() const override;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(TestSearchResult);
 };
diff --git a/ui/aura/window_tree_host_platform.cc b/ui/aura/window_tree_host_platform.cc
index 43294f6..49f53a84 100644
--- a/ui/aura/window_tree_host_platform.cc
+++ b/ui/aura/window_tree_host_platform.cc
@@ -44,18 +44,7 @@
     : WindowTreeHostPlatform() {
   bounds_ = bounds;
   CreateCompositor();
-#if defined(USE_OZONE)
-  platform_window_ =
-      ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds);
-#elif defined(OS_WIN)
-  platform_window_.reset(new ui::WinWindow(this, bounds));
-#elif defined(OS_ANDROID)
-  platform_window_.reset(new ui::PlatformWindowAndroid(this));
-#elif defined(USE_X11)
-  platform_window_.reset(new ui::X11Window(this, bounds));
-#else
-  NOTIMPLEMENTED();
-#endif
+  CreateAndSetDefaultPlatformWindow();
 }
 
 WindowTreeHostPlatform::WindowTreeHostPlatform()
@@ -67,6 +56,21 @@
       widget_(gfx::kNullAcceleratedWidget),
       current_cursor_(ui::CursorType::kNull) {}
 
+void WindowTreeHostPlatform::CreateAndSetDefaultPlatformWindow() {
+#if defined(USE_OZONE)
+  platform_window_ =
+      ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds_);
+#elif defined(OS_WIN)
+  platform_window_.reset(new ui::WinWindow(this, bounds_));
+#elif defined(OS_ANDROID)
+  platform_window_.reset(new ui::PlatformWindowAndroid(this));
+#elif defined(USE_X11)
+  platform_window_.reset(new ui::X11Window(this, bounds_));
+#else
+  NOTIMPLEMENTED();
+#endif
+}
+
 void WindowTreeHostPlatform::SetPlatformWindow(
     std::unique_ptr<ui::PlatformWindow> window) {
   platform_window_ = std::move(window);
@@ -149,12 +153,10 @@
   float new_scale = ui::GetScaleFactorForNativeView(window());
   gfx::Rect old_bounds = bounds_;
   bounds_ = new_bounds;
-  if (bounds_.origin() != old_bounds.origin()) {
+  if (bounds_.origin() != old_bounds.origin())
     OnHostMovedInPixels(bounds_.origin());
-  }
-  if (bounds_.size() != old_bounds.size() || current_scale != new_scale) {
+  if (bounds_.size() != old_bounds.size() || current_scale != new_scale)
     OnHostResizedInPixels(bounds_.size());
-  }
 }
 
 void WindowTreeHostPlatform::OnDamageRect(const gfx::Rect& damage_rect) {
@@ -190,7 +192,9 @@
     gfx::AcceleratedWidget widget,
     float device_pixel_ratio) {
   widget_ = widget;
-  WindowTreeHost::OnAcceleratedWidgetAvailable();
+  // This may be called before the Compositor has been created.
+  if (compositor())
+    WindowTreeHost::OnAcceleratedWidgetAvailable();
 }
 
 void WindowTreeHostPlatform::OnAcceleratedWidgetDestroyed() {
diff --git a/ui/aura/window_tree_host_platform.h b/ui/aura/window_tree_host_platform.h
index 5935d163..d07b1f32 100644
--- a/ui/aura/window_tree_host_platform.h
+++ b/ui/aura/window_tree_host_platform.h
@@ -48,8 +48,15 @@
   WindowTreeHostPlatform();
   explicit WindowTreeHostPlatform(std::unique_ptr<WindowPort> window_port);
 
+  // Creates a ui::PlatformWindow appropriate for the current platform and
+  // installs it at as the PlatformWindow for this WindowTreeHostPlatform.
+  void CreateAndSetDefaultPlatformWindow();
+
   void SetPlatformWindow(std::unique_ptr<ui::PlatformWindow> window);
   ui::PlatformWindow* platform_window() { return platform_window_.get(); }
+  const ui::PlatformWindow* platform_window() const {
+    return platform_window_.get();
+  }
 
   // ui::PlatformWindowDelegate:
   void OnBoundsChanged(const gfx::Rect& new_bounds) override;
diff --git a/ui/base/material_design/material_design_controller.cc b/ui/base/material_design/material_design_controller.cc
index d4dd4b1..5abb641 100644
--- a/ui/base/material_design/material_design_controller.cc
+++ b/ui/base/material_design/material_design_controller.cc
@@ -120,7 +120,8 @@
 
 // static
 bool MaterialDesignController::IsSecondaryUiMaterial() {
-  return base::FeatureList::IsEnabled(features::kSecondaryUiMd);
+  return base::FeatureList::IsEnabled(features::kSecondaryUiMd) ||
+         GetMode() == MATERIAL_REFRESH;
 }
 
 // static
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
index 8d61a7f..c19111ef 100644
--- a/ui/display/BUILD.gn
+++ b/ui/display/BUILD.gn
@@ -113,6 +113,8 @@
     "test/display_matchers.cc",
     "test/display_matchers.h",
     "test/display_test_util.h",
+    "test/scoped_screen_override.cc",
+    "test/scoped_screen_override.h",
     "test/test_screen.cc",
     "test/test_screen.h",
     "win/test/screen_util_win.cc",
diff --git a/ui/display/test/scoped_screen_override.cc b/ui/display/test/scoped_screen_override.cc
new file mode 100644
index 0000000..11fadc1
--- /dev/null
+++ b/ui/display/test/scoped_screen_override.cc
@@ -0,0 +1,22 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/display/test/scoped_screen_override.h"
+
+#include "ui/display/screen.h"
+
+namespace display {
+namespace test {
+
+ScopedScreenOverride::ScopedScreenOverride(Screen* screen)
+    : original_screen_(display::Screen::GetScreen()) {
+  display::Screen::SetScreenInstance(screen);
+}
+
+ScopedScreenOverride::~ScopedScreenOverride() {
+  display::Screen::SetScreenInstance(original_screen_);
+}
+
+}  // namespace test
+}  // namespace display
diff --git a/ui/display/test/scoped_screen_override.h b/ui/display/test/scoped_screen_override.h
new file mode 100644
index 0000000..6166216
--- /dev/null
+++ b/ui/display/test/scoped_screen_override.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_DISPLAY_TEST_SCOPED_SCREEN_OVERRIDE_H_
+#define UI_DISPLAY_TEST_SCOPED_SCREEN_OVERRIDE_H_
+
+#include "base/macros.h"
+
+namespace display {
+
+class Screen;
+
+namespace test {
+
+// This class represents a RAII wrapper for global screen overriding. An object
+// of this class restores original display::Screen instance when it goes out of
+// scope. Prefer to use it instead of directly call of
+// display::Screen::SetScreenInstance().
+class ScopedScreenOverride {
+ public:
+  explicit ScopedScreenOverride(Screen* screen);
+  ~ScopedScreenOverride();
+
+ private:
+  Screen* original_screen_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedScreenOverride);
+};
+
+}  // namespace test
+}  // namespace display
+
+#endif  // UI_DISPLAY_TEST_SCOPED_SCREEN_OVERRIDE_H_
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index ff87840d..f1dda975 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -1907,12 +1907,15 @@
 
   // GSUs and GPUs in one sequence should be coalesced into 1 GSU and 1 GPU.
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
+  HandleGestureEvent(WebInputEvent::kGesturePinchBegin);
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20);
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -7);
   HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 2.0f, 13, 10);
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10);
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -6);
+  HandleGestureEvent(WebInputEvent::kGesturePinchEnd);
   HandleGestureEvent(WebInputEvent::kGestureScrollEnd);
+  HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
   HandleGestureEvent(WebInputEvent::kGesturePinchBegin);
   HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 0.2f, 2, 20);
   HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 10.0f, 1, 10);
@@ -1920,37 +1923,46 @@
   HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 0.25f, 3, 30);
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10);
   HandleGestureEvent(WebInputEvent::kGesturePinchEnd);
+  HandleGestureEvent(WebInputEvent::kGestureScrollEnd);
 
   // Only the first GSB was dispatched.
-  EXPECT_EQ(7ul, event_queue().size());
+  EXPECT_EQ(11ul, event_queue().size());
   EXPECT_EQ(1ul, event_disposition_recorder_.size());
 
-  EXPECT_EQ(WebInputEvent::kGestureScrollUpdate,
+  EXPECT_EQ(WebInputEvent::kGesturePinchBegin,
             event_queue()[0]->event().GetType());
-  EXPECT_EQ(
-      -35,
-      ToWebGestureEvent(event_queue()[0]->event()).data.scroll_update.delta_y);
-  EXPECT_EQ(WebInputEvent::kGesturePinchUpdate,
+  EXPECT_EQ(WebInputEvent::kGestureScrollUpdate,
             event_queue()[1]->event().GetType());
   EXPECT_EQ(
-      2.0f,
-      ToWebGestureEvent(event_queue()[1]->event()).data.pinch_update.scale);
-  EXPECT_EQ(WebInputEvent::kGestureScrollEnd,
+      -35,
+      ToWebGestureEvent(event_queue()[1]->event()).data.scroll_update.delta_y);
+  EXPECT_EQ(WebInputEvent::kGesturePinchUpdate,
             event_queue()[2]->event().GetType());
-  EXPECT_EQ(WebInputEvent::kGesturePinchBegin,
+  EXPECT_EQ(
+      2.0f,
+      ToWebGestureEvent(event_queue()[2]->event()).data.pinch_update.scale);
+  EXPECT_EQ(WebInputEvent::kGesturePinchEnd,
             event_queue()[3]->event().GetType());
-  EXPECT_EQ(WebInputEvent::kGestureScrollUpdate,
+  EXPECT_EQ(WebInputEvent::kGestureScrollEnd,
             event_queue()[4]->event().GetType());
+  EXPECT_EQ(WebInputEvent::kGestureScrollBegin,
+            event_queue()[5]->event().GetType());
+  EXPECT_EQ(WebInputEvent::kGesturePinchBegin,
+            event_queue()[6]->event().GetType());
+  EXPECT_EQ(WebInputEvent::kGestureScrollUpdate,
+            event_queue()[7]->event().GetType());
   EXPECT_EQ(
       -85,
-      ToWebGestureEvent(event_queue()[4]->event()).data.scroll_update.delta_y);
+      ToWebGestureEvent(event_queue()[7]->event()).data.scroll_update.delta_y);
   EXPECT_EQ(WebInputEvent::kGesturePinchUpdate,
-            event_queue()[5]->event().GetType());
+            event_queue()[8]->event().GetType());
   EXPECT_EQ(
       0.5f,
-      ToWebGestureEvent(event_queue()[5]->event()).data.pinch_update.scale);
+      ToWebGestureEvent(event_queue()[8]->event()).data.pinch_update.scale);
   EXPECT_EQ(WebInputEvent::kGesturePinchEnd,
-            event_queue()[6]->event().GetType());
+            event_queue()[9]->event().GetType());
+  EXPECT_EQ(WebInputEvent::kGestureScrollEnd,
+            event_queue()[10]->event().GetType());
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 }
 
@@ -1970,6 +1982,10 @@
   EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true))
       .Times(::testing::AtLeast(1));
 
+  EXPECT_CALL(mock_input_handler_, PinchGestureBegin());
+  EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(testing::_, testing::_));
+  EXPECT_CALL(mock_input_handler_, PinchGestureEnd(testing::_, testing::_));
+
   trace_analyzer::Start("*");
   // Simulate scroll.
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
@@ -1980,10 +1996,12 @@
 
   // Simulate scroll and pinch.
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
+  HandleGestureEvent(WebInputEvent::kGesturePinchBegin);
   HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 10.0f, 1, 10);
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10);
   HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 2.0f, 1, 10);
   HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30);
+  HandleGestureEvent(WebInputEvent::kGesturePinchEnd);
   HandleGestureEvent(WebInputEvent::kGestureScrollEnd);
 
   // Dispatch all events.
@@ -2001,8 +2019,8 @@
       trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END);
   analyzer->FindEvents(end_query, &end_events);
 
-  EXPECT_EQ(5ul, begin_events.size());
-  EXPECT_EQ(5ul, end_events.size());
+  EXPECT_EQ(7ul, begin_events.size());
+  EXPECT_EQ(7ul, end_events.size());
   EXPECT_EQ(WebInputEvent::kGestureScrollUpdate,
             end_events[0]->GetKnownArgAsInt("type"));
   EXPECT_EQ(3, end_events[0]->GetKnownArgAsInt("coalesced_count"));
@@ -2011,15 +2029,19 @@
 
   EXPECT_EQ(WebInputEvent::kGestureScrollBegin,
             end_events[2]->GetKnownArgAsInt("type"));
+  EXPECT_EQ(WebInputEvent::kGesturePinchBegin,
+            end_events[3]->GetKnownArgAsInt("type"));
   // Original scroll and pinch updates will be stored in the coalesced
   // PinchUpdate of the <ScrollUpdate, PinchUpdate> pair.
   // The ScrollUpdate of the pair doesn't carry original events and won't be
   // traced.
   EXPECT_EQ(WebInputEvent::kGesturePinchUpdate,
-            end_events[3]->GetKnownArgAsInt("type"));
-  EXPECT_EQ(4, end_events[3]->GetKnownArgAsInt("coalesced_count"));
-  EXPECT_EQ(WebInputEvent::kGestureScrollEnd,
             end_events[4]->GetKnownArgAsInt("type"));
+  EXPECT_EQ(4, end_events[4]->GetKnownArgAsInt("coalesced_count"));
+  EXPECT_EQ(WebInputEvent::kGesturePinchEnd,
+            end_events[5]->GetKnownArgAsInt("type"));
+  EXPECT_EQ(WebInputEvent::kGestureScrollEnd,
+            end_events[6]->GetKnownArgAsInt("type"));
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 }
 
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index 188cc48..54be4df 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -923,6 +923,28 @@
   }
 }
 
+bool ColorSpace::ToSkYUVColorSpace(SkYUVColorSpace* out) {
+  if (range_ == RangeID::FULL) {
+    *out = kJPEG_SkYUVColorSpace;
+    return true;
+  }
+  switch (matrix_) {
+    case MatrixID::BT709:
+      *out = kRec709_SkYUVColorSpace;
+      return true;
+
+    case MatrixID::BT470BG:
+    case MatrixID::SMPTE170M:
+    case MatrixID::SMPTE240M:
+      *out = kRec601_SkYUVColorSpace;
+      return true;
+
+    default:
+      break;
+  }
+  return false;
+}
+
 std::ostream& operator<<(std::ostream& out, const ColorSpace& color_space) {
   return out << color_space.ToString();
 }
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index 561cbc7..aa70e54 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
 #include "ui/gfx/color_space_export.h"
 
 namespace IPC {
@@ -187,6 +188,10 @@
   // range, and unspecified spaces.
   sk_sp<SkColorSpace> ToSkColorSpace() const;
 
+  // For YUV color spaces, return the closest SkYUVColorSpace.
+  // Returns true if a close match is found.
+  bool ToSkYUVColorSpace(SkYUVColorSpace* out);
+
   void GetPrimaryMatrix(SkMatrix44* to_XYZD50) const;
   bool GetTransferFunction(SkColorSpaceTransferFn* fn) const;
   bool GetInverseTransferFunction(SkColorSpaceTransferFn* fn) const;
diff --git a/ui/latency/windowed_analyzer.h b/ui/latency/windowed_analyzer.h
index cf03264..004ffc9 100644
--- a/ui/latency/windowed_analyzer.h
+++ b/ui/latency/windowed_analyzer.h
@@ -49,6 +49,18 @@
 // Tracks the current window of time that can be stored as the worst
 // window of time if a metric detects it as such.
 struct SharedWindowedAnalyzerClient {
+  SharedWindowedAnalyzerClient() : max_window_size(0) {}
+
+  explicit SharedWindowedAnalyzerClient(size_t max_window_size)
+      : max_window_size(max_window_size) {}
+
+  SharedWindowedAnalyzerClient(size_t max_window_size,
+                               base::TimeTicks window_begin,
+                               base::TimeTicks window_end)
+      : max_window_size(max_window_size),
+        window_begin(window_begin),
+        window_end(window_end) {}
+
   // Maximum window size in number of samples.
   size_t max_window_size;
 
diff --git a/ui/latency/windowed_analyzer_unittest.cc b/ui/latency/windowed_analyzer_unittest.cc
index 7ae681d5..96fb6788 100644
--- a/ui/latency/windowed_analyzer_unittest.cc
+++ b/ui/latency/windowed_analyzer_unittest.cc
@@ -17,9 +17,9 @@
 TEST(FrameMetricsWindowedAnalyzerTest, AllResultsTheSame) {
   // For this test, we don't care about the timeline, so just keep it constant.
   TestWindowedAnalyzerClient client;
-  SharedWindowedAnalyzerClient shared_client{
+  SharedWindowedAnalyzerClient shared_client(
       60, base::TimeTicks(),
-      base::TimeTicks() + base::TimeDelta::FromSeconds(1)};
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1));
 
   // Try adding a single sample vs. multiple samples.
   for (size_t samples : {1u, 100u}) {
@@ -71,9 +71,9 @@
   const uint32_t kSampleWeight = 100;
 
   TestWindowedAnalyzerClient client;
-  SharedWindowedAnalyzerClient shared_client{
+  SharedWindowedAnalyzerClient shared_client(
       kMaxWindowSize, base::TimeTicks(),
-      base::TimeTicks() + base::TimeDelta::FromSeconds(1)};
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   WindowedAnalyzer analyzer(&client, &shared_client);
 
   // Used to "clear" all the windowed accumulators.
@@ -95,15 +95,15 @@
 
   AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
   AddPatternHelper(&shared_client, &analyzer, pattern_max_mean, kSampleWeight);
-  SharedWindowedAnalyzerClient worst_mean_client = shared_client;
+  SharedWindowedAnalyzerClient worst_mean_client(shared_client);
 
   AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
   AddPatternHelper(&shared_client, &analyzer, pattern_max_smr, kSampleWeight);
-  SharedWindowedAnalyzerClient worst_smr_client = shared_client;
+  SharedWindowedAnalyzerClient worst_smr_client(shared_client);
 
   AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight);
   AddPatternHelper(&shared_client, &analyzer, pattern_max_rms, kSampleWeight);
-  SharedWindowedAnalyzerClient worst_rms_client = shared_client;
+  SharedWindowedAnalyzerClient worst_rms_client(shared_client);
 
   // If there is a tie, the first window detected wins.
   // This can go wrong if there's any accumulation of error because the
@@ -141,16 +141,16 @@
   const uint32_t kSampleWeight = 100;
 
   TestWindowedAnalyzerClient client;
-  SharedWindowedAnalyzerClient shared_client{
+  SharedWindowedAnalyzerClient shared_client(
       kMaxWindowSize, base::TimeTicks(),
-      base::TimeTicks() + base::TimeDelta::FromSeconds(1)};
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   WindowedAnalyzer analyzer(&client, &shared_client);
 
   const std::vector<uint32_t> pattern_short = {2, 2, 2};
   double expected_initial_value =
       2 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
   AddPatternHelper(&shared_client, &analyzer, pattern_short, kSampleWeight);
-  SharedWindowedAnalyzerClient short_client = shared_client;
+  SharedWindowedAnalyzerClient short_client(shared_client);
 
   FrameRegionResult worst_mean = analyzer.WorstMean();
   EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value);
@@ -175,9 +175,9 @@
   FrameRegionResult worst_mean, worst_smr, worst_rms;
 
   TestWindowedAnalyzerClient client;
-  SharedWindowedAnalyzerClient shared_client{
+  SharedWindowedAnalyzerClient shared_client(
       kMaxWindowSize, base::TimeTicks(),
-      base::TimeTicks() + base::TimeDelta::FromSeconds(1)};
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   WindowedAnalyzer analyzer(&client, &shared_client);
 
   // The 7's at the start will dominate the result if the implemenationd
@@ -187,7 +187,7 @@
   double expected_initial_value =
       7 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
   AddPatternHelper(&shared_client, &analyzer, pattern_short, kSampleWeight);
-  SharedWindowedAnalyzerClient short_client = shared_client;
+  SharedWindowedAnalyzerClient short_client(shared_client);
 
   worst_mean = analyzer.WorstMean();
   EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value);
@@ -213,7 +213,7 @@
   double expected_final_value =
       6 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
   AddPatternHelper(&shared_client, &analyzer, pattern_long, kSampleWeight);
-  SharedWindowedAnalyzerClient long_client = shared_client;
+  SharedWindowedAnalyzerClient long_client(shared_client);
 
   worst_mean = analyzer.WorstMean();
   EXPECT_DOUBLE_EQ(expected_final_value, worst_mean.value);
@@ -238,9 +238,9 @@
   FrameRegionResult worst_mean, worst_smr, worst_rms;
 
   TestWindowedAnalyzerClient client;
-  SharedWindowedAnalyzerClient shared_client{
+  SharedWindowedAnalyzerClient shared_client(
       kMaxWindowSize, base::TimeTicks(),
-      base::TimeTicks() + base::TimeDelta::FromSeconds(1)};
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   WindowedAnalyzer analyzer(&client, &shared_client);
 
   // Start off with the worst pattern.
@@ -248,7 +248,7 @@
   double expected_initial_value =
       9 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
   AddPatternHelper(&shared_client, &analyzer, pattern1, kSampleWeight);
-  SharedWindowedAnalyzerClient initial_client = shared_client;
+  SharedWindowedAnalyzerClient initial_client(shared_client);
 
   worst_mean = analyzer.WorstMean();
   EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value);
@@ -295,7 +295,7 @@
   double expected_final_value =
       4 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale;
   AddPatternHelper(&shared_client, &analyzer, pattern3, kSampleWeight);
-  SharedWindowedAnalyzerClient final_client = shared_client;
+  SharedWindowedAnalyzerClient final_client(shared_client);
 
   // Add a window of 1's here to verify it does not affect the window of 4's.
   const std::vector<uint32_t> pattern4 = {1, 1, 1, 1, 1, 1};
@@ -393,15 +393,15 @@
 
   // Set up the actual WindowedAnalyzer implementation.
   TestWindowedAnalyzerClient client_impl;
-  SharedWindowedAnalyzerClient shared_client_impl{
+  SharedWindowedAnalyzerClient shared_client_impl(
       kMaxWindowSize, base::TimeTicks(),
-      base::TimeTicks() + base::TimeDelta::FromSeconds(1)};
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   TestWindowedAnalyzer analyzer_impl(&client_impl, &shared_client_impl);
 
   // Set up the naive WindowedAnalyzer implementation.
-  SharedWindowedAnalyzerClient shared_client_naive{
+  SharedWindowedAnalyzerClient shared_client_naive(
       kMaxWindowSize, base::TimeTicks(),
-      base::TimeTicks() + base::TimeDelta::FromSeconds(1)};
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   WindowedAnalyzerNaive analyzer_naive(kMaxWindowSize);
 
   // Verify error keeps accumulating each time the bad pattern is applied.
diff --git a/ui/ozone/platform/cast/platform_window_cast.cc b/ui/ozone/platform/cast/platform_window_cast.cc
index 08ce52b..a24276ca 100644
--- a/ui/ozone/platform/cast/platform_window_cast.cc
+++ b/ui/ozone/platform/cast/platform_window_cast.cc
@@ -39,6 +39,10 @@
 void PlatformWindowCast::SetTitle(const base::string16& title) {
 }
 
+bool PlatformWindowCast::HasCapture() const {
+  return false;
+}
+
 PlatformImeController* PlatformWindowCast::GetPlatformImeController() {
   return nullptr;
 }
diff --git a/ui/ozone/platform/cast/platform_window_cast.h b/ui/ozone/platform/cast/platform_window_cast.h
index 4a494f2..a7930ee 100644
--- a/ui/ozone/platform/cast/platform_window_cast.h
+++ b/ui/ozone/platform/cast/platform_window_cast.h
@@ -31,6 +31,7 @@
   void PrepareForShutdown() override {}
   void SetCapture() override {}
   void ReleaseCapture() override {}
+  bool HasCapture() const override;
   void ToggleFullscreen() override {}
   void Maximize() override {}
   void Minimize() override {}
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
index fa48339..7a236a5 100644
--- a/ui/ozone/platform/drm/host/drm_display_host_manager.cc
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -322,7 +322,7 @@
         MapDevPathToSysPath(primary_graphics_card_path_);
 
     if (!handle) {
-      handle.reset(new DrmDeviceHandle());
+      handle = std::make_unique<DrmDeviceHandle>();
       if (!handle->Initialize(primary_graphics_card_path_,
                               drm_devices_[primary_graphics_card_path_]))
         LOG(FATAL) << "Failed to open primary graphics card";
@@ -331,8 +331,10 @@
 
   // Send the primary device first since this is used to initialize graphics
   // state.
-  proxy_->GpuAddGraphicsDevice(drm_devices_[primary_graphics_card_path_],
-                               handle->PassFD());
+  if (!proxy_->GpuAddGraphicsDevice(drm_devices_[primary_graphics_card_path_],
+                                    handle->PassFD())) {
+    LOG(ERROR) << "Failed to add primary graphics device.";
+  }
 }
 
 void DrmDisplayHostManager::OnGpuThreadReady() {
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
index 44d276df..09632b1 100644
--- a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
@@ -115,7 +115,8 @@
 }
 
 bool DrmGpuPlatformSupportHost::IsConnected() {
-  return host_id_ >= 0 && channel_established_;
+  base::AutoLock auto_lock(host_id_lock_);
+  return host_id_ >= 0;
 }
 
 void DrmGpuPlatformSupportHost::OnGpuServiceLaunched(
@@ -139,27 +140,23 @@
   DCHECK(!ui_runner_->BelongsToCurrentThread());
   TRACE_EVENT1("drm", "DrmGpuPlatformSupportHost::OnGpuProcessLaunched",
                "host_id", host_id);
-  host_id_ = host_id;
-  send_runner_ = std::move(send_runner);
-  send_callback_ = send_callback;
-
-  for (GpuThreadObserver& observer : gpu_thread_observers_)
-    observer.OnGpuProcessLaunched();
 
   ui_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&DrmGpuPlatformSupportHost::OnChannelEstablished,
-                     weak_ptr_));
+                     weak_ptr_, host_id, std::move(send_runner),
+                     std::move(send_callback)));
 }
 
 void DrmGpuPlatformSupportHost::OnChannelDestroyed(int host_id) {
   TRACE_EVENT1("drm", "DrmGpuPlatformSupportHost::OnChannelDestroyed",
                "host_id", host_id);
-
   if (host_id_ == host_id) {
+    {
+      base::AutoLock auto_lock(host_id_lock_);
+      host_id_ = -1;
+    }
     cursor_->ResetDrmCursorProxy();
-    host_id_ = -1;
-    channel_established_ = false;
     send_runner_ = nullptr;
     send_callback_.Reset();
     for (GpuThreadObserver& observer : gpu_thread_observers_)
@@ -200,9 +197,22 @@
   display_manager_ = nullptr;
 }
 
-void DrmGpuPlatformSupportHost::OnChannelEstablished() {
+void DrmGpuPlatformSupportHost::OnChannelEstablished(
+    int host_id,
+    scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+    const base::Callback<void(IPC::Message*)>& send_callback) {
+  DCHECK(ui_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("drm", "DrmGpuPlatformSupportHost::OnChannelEstablished");
-  channel_established_ = true;
+
+  send_runner_ = std::move(send_runner);
+  send_callback_ = send_callback;
+  {
+    base::AutoLock auto_lock(host_id_lock_);
+    host_id_ = host_id;
+  }
+
+  for (GpuThreadObserver& observer : gpu_thread_observers_)
+    observer.OnGpuProcessLaunched();
 
   for (GpuThreadObserver& observer : gpu_thread_observers_)
     observer.OnGpuThreadReady();
@@ -278,22 +288,8 @@
 
 bool DrmGpuPlatformSupportHost::GpuAddGraphicsDevice(const base::FilePath& path,
                                                      base::ScopedFD fd) {
-  IPC::Message* message = new OzoneGpuMsg_AddGraphicsDevice(
-      path, base::FileDescriptor(std::move(fd)));
-
-  // This function may be called from two places:
-  // - DrmDisplayHostManager::OnGpuProcessLaunched() invoked synchronously
-  //   by GpuProcessHost::Init() on IO thread, which is the same thread as
-  //   |send_runner_|. In this case we can synchronously send the IPC;
-  // - DrmDisplayHostManager::OnAddGraphicsDevice() on UI thread. In this
-  //   case we need to post the send task to IO thread.
-  if (send_runner_ && send_runner_->BelongsToCurrentThread()) {
-    DCHECK(!send_callback_.is_null());
-    send_callback_.Run(message);
-    return true;
-  }
-
-  return Send(message);
+  return Send(new OzoneGpuMsg_AddGraphicsDevice(
+      path, base::FileDescriptor(std::move(fd))));
 }
 
 bool DrmGpuPlatformSupportHost::GpuRemoveGraphicsDevice(
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
index ebcb9d5e..4f0f918 100644
--- a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/observer_list.h"
 #include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
@@ -99,7 +100,10 @@
                               const gfx::Rect& bounds) override;
 
  private:
-  void OnChannelEstablished();
+  void OnChannelEstablished(
+      int host_id,
+      scoped_refptr<base::SingleThreadTaskRunner> send_runner,
+      const base::Callback<void(IPC::Message*)>& send_callback);
   bool OnMessageReceivedForDrmDisplayHostManager(const IPC::Message& message);
   void OnUpdateNativeDisplays(
       const std::vector<DisplaySnapshot_Params>& displays);
@@ -117,7 +121,7 @@
                        const std::vector<OverlayCheckReturn_Params>& returns);
 
   int host_id_ = -1;
-  bool channel_established_ = false;
+  base::Lock host_id_lock_;
 
   scoped_refptr<base::SingleThreadTaskRunner> ui_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> send_runner_;
diff --git a/ui/ozone/platform/drm/host/drm_window_host.cc b/ui/ozone/platform/drm/host/drm_window_host.cc
index cb88e95..c3470c2 100644
--- a/ui/ozone/platform/drm/host/drm_window_host.cc
+++ b/ui/ozone/platform/drm/host/drm_window_host.cc
@@ -97,6 +97,10 @@
   window_manager_->UngrabEvents(widget_);
 }
 
+bool DrmWindowHost::HasCapture() const {
+  return widget_ == window_manager_->event_grabber();
+}
+
 void DrmWindowHost::ToggleFullscreen() {
 }
 
diff --git a/ui/ozone/platform/drm/host/drm_window_host.h b/ui/ozone/platform/drm/host/drm_window_host.h
index 01f0cce..1b17b20 100644
--- a/ui/ozone/platform/drm/host/drm_window_host.h
+++ b/ui/ozone/platform/drm/host/drm_window_host.h
@@ -67,6 +67,7 @@
   void SetTitle(const base::string16& title) override;
   void SetCapture() override;
   void ReleaseCapture() override;
+  bool HasCapture() const override;
   void ToggleFullscreen() override;
   void Maximize() override;
   void Minimize() override;
diff --git a/ui/ozone/platform/drm/host/gpu_thread_observer.h b/ui/ozone/platform/drm/host/gpu_thread_observer.h
index ed6da90b..24b5b19 100644
--- a/ui/ozone/platform/drm/host/gpu_thread_observer.h
+++ b/ui/ozone/platform/drm/host/gpu_thread_observer.h
@@ -7,20 +7,19 @@
 
 namespace ui {
 
-// Observes the channel state.
+// Observes the channel state. All calls should happen on the same thread that
+// OzonePlatform::InitializeForUI() is called on. This can be the browser UI
+// thread or the WS thread for mus/mash.
 class GpuThreadObserver {
  public:
   virtual ~GpuThreadObserver() {}
 
   // Called when the GPU process is launched.
-  // This is called from browser IO thread.
   virtual void OnGpuProcessLaunched() = 0;
   // Called when a GPU thread implementation has become available.
-  // This is called from browser UI thread.
   virtual void OnGpuThreadReady() = 0;
   // Called when the GPU thread implementation has ceased to be
   // available.
-  // This is called from browser UI thread.
   virtual void OnGpuThreadRetired() = 0;
 };
 
diff --git a/ui/ozone/platform/headless/headless_window.cc b/ui/ozone/platform/headless/headless_window.cc
index 2c038ad..5bf6e7c9 100644
--- a/ui/ozone/platform/headless/headless_window.cc
+++ b/ui/ozone/platform/headless/headless_window.cc
@@ -56,6 +56,10 @@
 
 void HeadlessWindow::ReleaseCapture() {}
 
+bool HeadlessWindow::HasCapture() const {
+  return false;
+}
+
 void HeadlessWindow::ToggleFullscreen() {}
 
 void HeadlessWindow::Maximize() {}
diff --git a/ui/ozone/platform/headless/headless_window.h b/ui/ozone/platform/headless/headless_window.h
index 4e909cf..2051d14 100644
--- a/ui/ozone/platform/headless/headless_window.h
+++ b/ui/ozone/platform/headless/headless_window.h
@@ -32,6 +32,7 @@
   void PrepareForShutdown() override;
   void SetCapture() override;
   void ReleaseCapture() override;
+  bool HasCapture() const override;
   void ToggleFullscreen() override;
   void Maximize() override;
   void Minimize() override;
diff --git a/ui/ozone/platform/wayland/wayland_connection.h b/ui/ozone/platform/wayland/wayland_connection.h
index e745efd..6372854 100644
--- a/ui/ozone/platform/wayland/wayland_connection.h
+++ b/ui/ozone/platform/wayland/wayland_connection.h
@@ -53,6 +53,9 @@
 
   int GetKeyboardModifiers();
 
+  // Returns the current pointer, which may be null.
+  WaylandPointer* pointer() { return pointer_.get(); }
+
  private:
   void Flush();
   void DispatchUiEvent(Event* event);
diff --git a/ui/ozone/platform/wayland/wayland_pointer.cc b/ui/ozone/platform/wayland/wayland_pointer.cc
index 9d6f4ae..8d74daf 100644
--- a/ui/ozone/platform/wayland/wayland_pointer.cc
+++ b/ui/ozone/platform/wayland/wayland_pointer.cc
@@ -23,6 +23,12 @@
   return flags == original_flags;
 }
 
+bool HasAnyButtonFlag(int flags) {
+  return (flags & (EF_LEFT_MOUSE_BUTTON | EF_MIDDLE_MOUSE_BUTTON |
+                   EF_RIGHT_MOUSE_BUTTON | EF_BACK_MOUSE_BUTTON |
+                   EF_FORWARD_MOUSE_BUTTON)) != 0;
+}
+
 }  // namespace
 
 WaylandPointer::WaylandPointer(wl_pointer* pointer,
@@ -38,7 +44,12 @@
   cursor_.reset(new WaylandCursor);
 }
 
-WaylandPointer::~WaylandPointer() {}
+WaylandPointer::~WaylandPointer() {
+  if (window_with_pointer_focus_) {
+    window_with_pointer_focus_->set_pointer_focus(false);
+    window_with_pointer_focus_->set_has_implicit_grab(false);
+  }
+}
 
 // static
 void WaylandPointer::Enter(void* data,
@@ -50,8 +61,11 @@
   WaylandPointer* pointer = static_cast<WaylandPointer*>(data);
   pointer->location_.SetPoint(wl_fixed_to_double(surface_x),
                               wl_fixed_to_double(surface_y));
-  if (surface)
-    WaylandWindow::FromSurface(surface)->set_pointer_focus(true);
+  if (surface) {
+    WaylandWindow* window = WaylandWindow::FromSurface(surface);
+    window->set_pointer_focus(true);
+    pointer->window_with_pointer_focus_ = window;
+  }
 }
 
 // static
@@ -63,8 +77,11 @@
   MouseEvent event(ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
                    EventTimeForNow(), pointer->flags_, 0);
   pointer->callback_.Run(&event);
-  if (surface)
-    WaylandWindow::FromSurface(surface)->set_pointer_focus(false);
+  if (surface) {
+    WaylandWindow* window = WaylandWindow::FromSurface(surface);
+    window->set_pointer_focus(false);
+    pointer->window_with_pointer_focus_ = nullptr;
+  }
 }
 
 // static
@@ -92,22 +109,22 @@
                             uint32_t button,
                             uint32_t state) {
   WaylandPointer* pointer = static_cast<WaylandPointer*>(data);
-  int flag;
+  int changed_button;
   switch (button) {
     case BTN_LEFT:
-      flag = EF_LEFT_MOUSE_BUTTON;
+      changed_button = EF_LEFT_MOUSE_BUTTON;
       break;
     case BTN_MIDDLE:
-      flag = EF_MIDDLE_MOUSE_BUTTON;
+      changed_button = EF_MIDDLE_MOUSE_BUTTON;
       break;
     case BTN_RIGHT:
-      flag = EF_RIGHT_MOUSE_BUTTON;
+      changed_button = EF_RIGHT_MOUSE_BUTTON;
       break;
     case BTN_BACK:
-      flag = EF_BACK_MOUSE_BUTTON;
+      changed_button = EF_BACK_MOUSE_BUTTON;
       break;
     case BTN_FORWARD:
-      flag = EF_FORWARD_MOUSE_BUTTON;
+      changed_button = EF_FORWARD_MOUSE_BUTTON;
       break;
     default:
       return;
@@ -116,17 +133,24 @@
   EventType type;
   if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
     type = ET_MOUSE_PRESSED;
-    pointer->flags_ |= flag;
+    pointer->flags_ |= changed_button;
     pointer->connection_->set_serial(serial);
   } else {
     type = ET_MOUSE_RELEASED;
-    pointer->flags_ &= ~flag;
+    pointer->flags_ &= ~changed_button;
   }
 
-  int flags = pointer->GetFlagsWithKeyboardModifiers() | flag;
+  if (pointer->window_with_pointer_focus_) {
+    pointer->window_with_pointer_focus_->set_has_implicit_grab(
+        HasAnyButtonFlag(pointer->flags_));
+  }
+
+  // MouseEvent's flags should contain the button that was released too.
+  const int flags = pointer->GetFlagsWithKeyboardModifiers() | changed_button;
   MouseEvent event(type, gfx::Point(), gfx::Point(),
                    base::TimeTicks() + base::TimeDelta::FromMilliseconds(time),
-                   flags, flag);
+                   flags, changed_button);
+
   event.set_location_f(pointer->location_);
   event.set_root_location_f(pointer->location_);
   pointer->callback_.Run(&event);
diff --git a/ui/ozone/platform/wayland/wayland_pointer.h b/ui/ozone/platform/wayland/wayland_pointer.h
index 9550da69..9515dfc 100644
--- a/ui/ozone/platform/wayland/wayland_pointer.h
+++ b/ui/ozone/platform/wayland/wayland_pointer.h
@@ -5,6 +5,7 @@
 #ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_POINTER_H_
 #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_POINTER_H_
 
+#include "base/macros.h"
 #include "ui/events/ozone/evdev/event_dispatch_callback.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/ozone/platform/wayland/wayland_cursor.h"
@@ -12,6 +13,8 @@
 
 namespace ui {
 
+class WaylandWindow;
+
 class WaylandPointer {
  public:
   WaylandPointer(wl_pointer* pointer, const EventDispatchCallback& callback);
@@ -26,6 +29,10 @@
 
   WaylandCursor* cursor() { return cursor_.get(); }
 
+  void reset_window_with_pointer_focus() {
+    window_with_pointer_focus_ = nullptr;
+  }
+
  private:
   // wl_pointer_listener
   static void Enter(void* data,
@@ -60,11 +67,18 @@
   wl::Object<wl_pointer> obj_;
   EventDispatchCallback callback_;
   gfx::PointF location_;
+  // Flags is a bitmask of EventFlags corresponding to the pointer/keyboard
+  // state.
   int flags_ = 0;
 
   // Keeps track of current modifiers. These are needed in order to properly
   // update |flags_| with newest modifiers.
   int keyboard_modifiers_ = 0;
+
+  // The window the mouse is over.
+  WaylandWindow* window_with_pointer_focus_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(WaylandPointer);
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/wayland_pointer_unittest.cc b/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
index 8675f4d..bc86a49 100644
--- a/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
@@ -122,27 +122,42 @@
 TEST_P(WaylandPointerTest, ButtonPress) {
   wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(),
                         wl_fixed_from_int(200), wl_fixed_from_int(150));
-  wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_RIGHT,
-                         WL_POINTER_BUTTON_STATE_PRESSED);
-
   Sync();
 
-  std::unique_ptr<Event> event;
-  EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event));
+  wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_RIGHT,
+                         WL_POINTER_BUTTON_STATE_PRESSED);
+  std::unique_ptr<Event> right_press_event;
+  EXPECT_CALL(delegate_, DispatchEvent(_))
+      .WillOnce(CloneEvent(&right_press_event));
+  Sync();
+  ASSERT_TRUE(right_press_event);
+  ASSERT_TRUE(right_press_event->IsMouseEvent());
+  auto* right_press_mouse_event = right_press_event->AsMouseEvent();
+  EXPECT_EQ(ET_MOUSE_PRESSED, right_press_mouse_event->type());
+  EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, right_press_mouse_event->button_flags());
+  EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON,
+            right_press_mouse_event->changed_button_flags());
+
+  std::unique_ptr<Event> left_press_event;
+  EXPECT_CALL(delegate_, DispatchEvent(_))
+      .WillOnce(CloneEvent(&left_press_event));
   wl_pointer_send_button(pointer_->resource(), 3, 1003, BTN_LEFT,
                          WL_POINTER_BUTTON_STATE_PRESSED);
 
   Sync();
 
-  ASSERT_TRUE(event);
-  ASSERT_TRUE(event->IsMouseEvent());
-  auto* mouse_event = event->AsMouseEvent();
-  EXPECT_EQ(ET_MOUSE_PRESSED, mouse_event->type());
+  ASSERT_TRUE(left_press_event);
+  ASSERT_TRUE(left_press_event->IsMouseEvent());
+  auto* left_press_mouse_event = left_press_event->AsMouseEvent();
+  EXPECT_EQ(ET_MOUSE_PRESSED, left_press_mouse_event->type());
   EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON,
-            mouse_event->button_flags());
-  EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, mouse_event->changed_button_flags());
-  EXPECT_EQ(gfx::PointF(200, 150), mouse_event->location_f());
-  EXPECT_EQ(gfx::PointF(200, 150), mouse_event->root_location_f());
+            left_press_mouse_event->button_flags());
+  EXPECT_EQ(EF_LEFT_MOUSE_BUTTON,
+            left_press_mouse_event->changed_button_flags());
+  EXPECT_EQ(EF_LEFT_MOUSE_BUTTON,
+            left_press_mouse_event->changed_button_flags());
+  EXPECT_EQ(gfx::PointF(200, 150), left_press_mouse_event->location_f());
+  EXPECT_EQ(gfx::PointF(200, 150), left_press_mouse_event->root_location_f());
 }
 
 TEST_P(WaylandPointerTest, ButtonRelease) {
diff --git a/ui/ozone/platform/wayland/wayland_window.cc b/ui/ozone/platform/wayland/wayland_window.cc
index 196ad28..e5b7cd4 100644
--- a/ui/ozone/platform/wayland/wayland_window.cc
+++ b/ui/ozone/platform/wayland/wayland_window.cc
@@ -12,6 +12,7 @@
 #include "ui/events/event.h"
 #include "ui/events/ozone/events_ozone.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
+#include "ui/ozone/platform/wayland/wayland_pointer.h"
 #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v5.h"
 #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v6.h"
 
@@ -55,6 +56,8 @@
     PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
     connection_->RemoveWindow(surface_.id());
   }
+  if (has_pointer_focus_)
+    connection_->pointer()->reset_window_with_pointer_focus();
 }
 
 // static
@@ -134,13 +137,21 @@
 }
 
 void WaylandWindow::SetCapture() {
+  // Wayland does implicit grabs, and doesn't allow for explicit grabs. The
+  // exception to that seems to be popups, which can do a grab during show. Need
+  // to evaluate under what circumstances we need this.
   NOTIMPLEMENTED();
 }
 
 void WaylandWindow::ReleaseCapture() {
+  // See comment in SetCapture() for details on wayland and grabs.
   NOTIMPLEMENTED();
 }
 
+bool WaylandWindow::HasCapture() const {
+  return has_implicit_grab_;
+}
+
 void WaylandWindow::ToggleFullscreen() {
   DCHECK(xdg_surface_);
 
@@ -267,14 +278,21 @@
   else
     state_ = PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL;
 
-  if (old_state != state_)
-    delegate_->OnWindowStateChanged(state_);
+  // Update state before notifying delegate.
+  const bool did_active_change = is_active_ != is_activated;
+  is_active_ = is_activated;
 
   // Rather than call SetBounds here for every configure event, just save the
   // most recent bounds, and have WaylandConnection call ApplyPendingBounds
   // when it has finished processing events. We may get many configure events
   // in a row during an interactive resize, and only the last one matters.
   SetPendingBounds(width, height);
+
+  if (old_state != state_)
+    delegate_->OnWindowStateChanged(state_);
+
+  if (did_active_change)
+    delegate_->OnActivationChanged(is_active_);
 }
 
 void WaylandWindow::OnCloseRequest() {
diff --git a/ui/ozone/platform/wayland/wayland_window.h b/ui/ozone/platform/wayland/wayland_window.h
index 8d205fa4..b534566e 100644
--- a/ui/ozone/platform/wayland/wayland_window.h
+++ b/ui/ozone/platform/wayland/wayland_window.h
@@ -43,6 +43,7 @@
 
   // Set whether this window has pointer focus and should dispatch mouse events.
   void set_pointer_focus(bool focus) { has_pointer_focus_ = focus; }
+  bool has_pointer_focus() const { return has_pointer_focus_; }
 
   // Set whether this window has keyboard focus and should dispatch key events.
   void set_keyboard_focus(bool focus) { has_keyboard_focus_ = focus; }
@@ -50,6 +51,13 @@
   // Set whether this window has touch focus and should dispatch touch events.
   void set_touch_focus(bool focus) { has_touch_focus_ = focus; }
 
+  // Set whether this window has an implicit grab (often referred to as capture
+  // in Chrome code). Implicit grabs happen while a pointer is down.
+  void set_has_implicit_grab(bool value) { has_implicit_grab_ = value; }
+  bool has_implicit_grab() const { return has_implicit_grab_; }
+
+  bool is_active() const { return is_active_; }
+
   // PlatformWindow
   void Show() override;
   void Hide() override;
@@ -60,6 +68,7 @@
   void SetTitle(const base::string16& title) override;
   void SetCapture() override;
   void ReleaseCapture() override;
+  bool HasCapture() const override;
   void ToggleFullscreen() override;
   void Maximize() override;
   void Minimize() override;
@@ -113,10 +122,13 @@
   bool has_pointer_focus_ = false;
   bool has_keyboard_focus_ = false;
   bool has_touch_focus_ = false;
+  bool has_implicit_grab_ = false;
 
   // Stores current states of the window.
   ui::PlatformWindowState state_;
 
+  bool is_active_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(WaylandWindow);
 };
 
diff --git a/ui/ozone/platform/wayland/wayland_window_unittest.cc b/ui/ozone/platform/wayland/wayland_window_unittest.cc
index 5cfa7b5..476272b 100644
--- a/ui/ozone/platform/wayland/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_window_unittest.cc
@@ -323,13 +323,18 @@
 }
 
 TEST_P(WaylandWindowTest, CanDispatchMouseEventFocus) {
+  // set_pointer_focus(true) requires a WaylandPointer.
+  wl_seat_send_capabilities(server_.seat()->resource(),
+                            WL_SEAT_CAPABILITY_POINTER);
+  Sync();
+  ASSERT_TRUE(connection_->pointer());
   window_->set_pointer_focus(true);
   EXPECT_TRUE(window_->CanDispatchEvent(&test_mouse_event_));
 }
 
 TEST_P(WaylandWindowTest, CanDispatchMouseEventUnfocus) {
-  window_->set_pointer_focus(true);
-  EXPECT_TRUE(window_->CanDispatchEvent(&test_mouse_event_));
+  EXPECT_FALSE(window_->has_pointer_focus());
+  EXPECT_FALSE(window_->CanDispatchEvent(&test_mouse_event_));
 }
 
 ACTION_P(CloneEvent, ptr) {
@@ -352,6 +357,35 @@
             test_mouse_event_.changed_button_flags());
 }
 
+TEST_P(WaylandWindowTest, HasCaptureUpdatedOnPointerEvents) {
+  wl_seat_send_capabilities(server_.seat()->resource(),
+                            WL_SEAT_CAPABILITY_POINTER);
+
+  Sync();
+
+  wl::MockPointer* pointer = server_.seat()->pointer_.get();
+  ASSERT_TRUE(pointer);
+
+  wl_pointer_send_enter(pointer->resource(), 1, surface_->resource(), 0, 0);
+  Sync();
+  EXPECT_FALSE(window_->HasCapture());
+
+  wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_LEFT,
+                         WL_POINTER_BUTTON_STATE_PRESSED);
+  Sync();
+  EXPECT_TRUE(window_->HasCapture());
+
+  wl_pointer_send_motion(pointer->resource(), 1003, wl_fixed_from_int(400),
+                         wl_fixed_from_int(500));
+  Sync();
+  EXPECT_TRUE(window_->HasCapture());
+
+  wl_pointer_send_button(pointer->resource(), 4, 1004, BTN_LEFT,
+                         WL_POINTER_BUTTON_STATE_RELEASED);
+  Sync();
+  EXPECT_FALSE(window_->HasCapture());
+}
+
 TEST_P(WaylandWindowTest, ConfigureEvent) {
   wl_array states;
   wl_array_init(&states);
@@ -385,6 +419,26 @@
   EXPECT_CALL(*xdg_surface_, AckConfigure(14));
 }
 
+TEST_P(WaylandWindowTest, OnActivationChanged) {
+  EXPECT_FALSE(window_->is_active());
+
+  {
+    wl_array states;
+    InitializeWlArrayWithActivatedState(&states);
+    EXPECT_CALL(delegate_, OnActivationChanged(Eq(true)));
+    SendConfigureEvent(0, 0, 1, &states);
+    Sync();
+    EXPECT_TRUE(window_->is_active());
+  }
+
+  wl_array states;
+  wl_array_init(&states);
+  EXPECT_CALL(delegate_, OnActivationChanged(Eq(false)));
+  SendConfigureEvent(0, 0, 2, &states);
+  Sync();
+  EXPECT_FALSE(window_->is_active());
+}
+
 INSTANTIATE_TEST_CASE_P(XdgVersionV5Test,
                         WaylandWindowTest,
                         ::testing::Values(kXdgShellV5));
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index 58c114b..2457d0b 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -42,8 +42,11 @@
   EnsureInstance();
   if (g_platform_initialized_ui)
     return;
-  g_platform_initialized_ui = true;
   instance_->InitializeUI(args);
+  {
+    base::AutoLock lock(GetOzoneInstanceLock());
+    g_platform_initialized_ui = true;
+  }
   // This is deliberately created after initializing so that the platform can
   // create its own version of DDM.
   DeviceDataManager::CreateInstance();
@@ -56,8 +59,11 @@
   EnsureInstance();
   if (g_platform_initialized_gpu)
     return;
-  g_platform_initialized_gpu = true;
   instance_->InitializeGPU(args);
+  {
+    base::AutoLock lock(GetOzoneInstanceLock());
+    g_platform_initialized_gpu = true;
+  }
   if (!args.single_process && !instance_callback.Get().is_null())
     std::move(instance_callback.Get()).Run(instance_);
 }
diff --git a/ui/platform_window/android/platform_window_android.cc b/ui/platform_window/android/platform_window_android.cc
index c32b24a3..ed53df5 100644
--- a/ui/platform_window/android/platform_window_android.cc
+++ b/ui/platform_window/android/platform_window_android.cc
@@ -196,6 +196,11 @@
   NOTIMPLEMENTED();
 }
 
+bool PlatformWindowAndroid::HasCapture() const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
 void PlatformWindowAndroid::ToggleFullscreen() {
   NOTIMPLEMENTED();
 }
diff --git a/ui/platform_window/android/platform_window_android.h b/ui/platform_window/android/platform_window_android.h
index 599ff718..803ab18 100644
--- a/ui/platform_window/android/platform_window_android.h
+++ b/ui/platform_window/android/platform_window_android.h
@@ -71,6 +71,7 @@
   void SetTitle(const base::string16& title) override;
   void SetCapture() override;
   void ReleaseCapture() override;
+  bool HasCapture() const override;
   void ToggleFullscreen() override;
   void Maximize() override;
   void Minimize() override;
diff --git a/ui/platform_window/platform_window.h b/ui/platform_window/platform_window.h
index 87a8b1d..1af46942 100644
--- a/ui/platform_window/platform_window.h
+++ b/ui/platform_window/platform_window.h
@@ -45,6 +45,7 @@
 
   virtual void SetCapture() = 0;
   virtual void ReleaseCapture() = 0;
+  virtual bool HasCapture() const = 0;
 
   virtual void ToggleFullscreen() = 0;
   virtual void Maximize() = 0;
diff --git a/ui/platform_window/stub/stub_window.cc b/ui/platform_window/stub/stub_window.cc
index e646ca4..d5fc54c 100644
--- a/ui/platform_window/stub/stub_window.cc
+++ b/ui/platform_window/stub/stub_window.cc
@@ -53,6 +53,10 @@
 void StubWindow::ReleaseCapture() {
 }
 
+bool StubWindow::HasCapture() const {
+  return false;
+}
+
 void StubWindow::ToggleFullscreen() {
 }
 
diff --git a/ui/platform_window/stub/stub_window.h b/ui/platform_window/stub/stub_window.h
index fa01974c..c94a847 100644
--- a/ui/platform_window/stub/stub_window.h
+++ b/ui/platform_window/stub/stub_window.h
@@ -37,6 +37,7 @@
   void SetCapture() override;
   void ReleaseCapture() override;
   void ToggleFullscreen() override;
+  bool HasCapture() const override;
   void Maximize() override;
   void Minimize() override;
   void Restore() override;
diff --git a/ui/platform_window/win/win_window.cc b/ui/platform_window/win/win_window.cc
index 2f32390a..39fd290b 100644
--- a/ui/platform_window/win/win_window.cc
+++ b/ui/platform_window/win/win_window.cc
@@ -101,15 +101,19 @@
 }
 
 void WinWindow::SetCapture() {
-  if (::GetCapture() != hwnd())
+  if (!HasCapture())
     ::SetCapture(hwnd());
 }
 
 void WinWindow::ReleaseCapture() {
-  if (::GetCapture() == hwnd())
+  if (HasCapture())
     ::ReleaseCapture();
 }
 
+bool WinWindow::HasCapture() const {
+  return ::GetCapture() == hwnd();
+}
+
 void WinWindow::ToggleFullscreen() {}
 
 void WinWindow::Maximize() {}
diff --git a/ui/platform_window/win/win_window.h b/ui/platform_window/win/win_window.h
index c6774fd..c2f7c224 100644
--- a/ui/platform_window/win/win_window.h
+++ b/ui/platform_window/win/win_window.h
@@ -36,6 +36,7 @@
   void SetTitle(const base::string16& title) override;
   void SetCapture() override;
   void ReleaseCapture() override;
+  bool HasCapture() const override;
   void ToggleFullscreen() override;
   void Maximize() override;
   void Minimize() override;
diff --git a/ui/platform_window/x11/x11_window_base.cc b/ui/platform_window/x11/x11_window_base.cc
index 75cc3eef..b6c4214e 100644
--- a/ui/platform_window/x11/x11_window_base.cc
+++ b/ui/platform_window/x11/x11_window_base.cc
@@ -224,6 +224,10 @@
 
 void X11WindowBase::ReleaseCapture() {}
 
+bool X11WindowBase::HasCapture() const {
+  return false;
+}
+
 void X11WindowBase::ToggleFullscreen() {
   ui::SetWMSpecState(xwindow_, !IsFullscreen(),
                      gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), x11::None);
diff --git a/ui/platform_window/x11/x11_window_base.h b/ui/platform_window/x11/x11_window_base.h
index b419dc19..1a062b9 100644
--- a/ui/platform_window/x11/x11_window_base.h
+++ b/ui/platform_window/x11/x11_window_base.h
@@ -39,6 +39,7 @@
   void SetTitle(const base::string16& title) override;
   void SetCapture() override;
   void ReleaseCapture() override;
+  bool HasCapture() const override;
   void ToggleFullscreen() override;
   void Maximize() override;
   void Minimize() override;
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 8edb409..2dcfa7fd5 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -1217,6 +1217,6 @@
 
   data_deps = [
     "//ui/resources:ui_test_pak_data",
-    "//testing:run_gtest_perf_test",
+    "//testing:run_perf_test",
   ]
 }
diff --git a/ui/views/controls/scroll_view_unittest.cc b/ui/views/controls/scroll_view_unittest.cc
index d57fbb05..16ec528c8 100644
--- a/ui/views/controls/scroll_view_unittest.cc
+++ b/ui/views/controls/scroll_view_unittest.cc
@@ -134,12 +134,12 @@
   DISALLOW_COPY_AND_ASSIGN(CustomView);
 };
 
-void CheckScrollbarVisibility(const ScrollView& scroll_view,
+void CheckScrollbarVisibility(const ScrollView* scroll_view,
                               ScrollBarOrientation orientation,
                               bool should_be_visible) {
   const ScrollBar* scrollbar = orientation == HORIZONTAL
-                                   ? scroll_view.horizontal_scroll_bar()
-                                   : scroll_view.vertical_scroll_bar();
+                                   ? scroll_view->horizontal_scroll_bar()
+                                   : scroll_view->vertical_scroll_bar();
   if (should_be_visible) {
     ASSERT_TRUE(scrollbar);
     EXPECT_TRUE(scrollbar->visible());
@@ -179,11 +179,16 @@
  public:
   ScrollViewTest() {}
 
+  void SetUp() override {
+    ViewsTestBase::SetUp();
+    scroll_view_ = std::make_unique<ScrollView>();
+  }
+
   View* InstallContents() {
     const gfx::Rect default_outer_bounds(0, 0, 100, 100);
     View* contents = new View;
-    scroll_view_.SetContents(contents);
-    scroll_view_.SetBoundsRect(default_outer_bounds);
+    scroll_view_->SetContents(contents);
+    scroll_view_->SetBoundsRect(default_outer_bounds);
     return contents;
   }
 
@@ -208,14 +213,14 @@
  protected:
 #endif
   int VerticalScrollBarWidth() {
-    return scroll_view_.vertical_scroll_bar()->GetThickness();
+    return scroll_view_->vertical_scroll_bar()->GetThickness();
   }
 
   int HorizontalScrollBarHeight() {
-    return scroll_view_.horizontal_scroll_bar()->GetThickness();
+    return scroll_view_->horizontal_scroll_bar()->GetThickness();
   }
 
-  ScrollView scroll_view_;
+  std::unique_ptr<ScrollView> scroll_view_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ScrollViewTest);
@@ -332,7 +337,7 @@
 // Verifies the viewport is sized to fit the available space.
 TEST_F(ScrollViewTest, ViewportSizedToFit) {
   View* contents = InstallContents();
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString());
 }
 
@@ -340,9 +345,9 @@
 // bounded scroll view.
 TEST_F(ScrollViewTest, BoundedViewportSizedToFit) {
   View* contents = InstallContents();
-  scroll_view_.ClipHeightTo(100, 200);
-  scroll_view_.SetBorder(CreateSolidBorder(2, 0));
-  scroll_view_.Layout();
+  scroll_view_->ClipHeightTo(100, 200);
+  scroll_view_->SetBorder(CreateSolidBorder(2, 0));
+  scroll_view_->Layout();
   EXPECT_EQ("2,2 96x96", contents->parent()->bounds().ToString());
 
   // Make sure the width of |contents| is set properly not to overflow the
@@ -355,11 +360,11 @@
 TEST_F(ScrollViewTest, VerticalScrollbarDoesNotAppearUnnecessarily) {
   const gfx::Rect default_outer_bounds(0, 0, 100, 100);
   View* contents = new VerticalResizingView;
-  scroll_view_.SetContents(contents);
-  scroll_view_.SetBoundsRect(default_outer_bounds);
-  scroll_view_.Layout();
-  EXPECT_FALSE(scroll_view_.vertical_scroll_bar()->visible());
-  EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible());
+  scroll_view_->SetContents(contents);
+  scroll_view_->SetBoundsRect(default_outer_bounds);
+  scroll_view_->Layout();
+  EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->visible());
+  EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible());
 }
 
 // Verifies the scrollbars are added as necessary.
@@ -369,54 +374,54 @@
 
   // Size the contents such that vertical scrollbar is needed.
   contents->SetBounds(0, 0, 50, 400);
-  scroll_view_.Layout();
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(),
+  scroll_view_->Layout();
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
             contents->parent()->width());
   EXPECT_EQ(100, contents->parent()->height());
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, true);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false);
-  EXPECT_TRUE(!scroll_view_.horizontal_scroll_bar() ||
-              !scroll_view_.horizontal_scroll_bar()->visible());
-  ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible());
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false);
+  EXPECT_TRUE(!scroll_view_->horizontal_scroll_bar() ||
+              !scroll_view_->horizontal_scroll_bar()->visible());
+  ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible());
 
   // Size the contents such that horizontal scrollbar is needed.
   contents->SetBounds(0, 0, 400, 50);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(100, contents->parent()->width());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(),
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(),
             contents->parent()->height());
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, false);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, false);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true);
 
   // Both horizontal and vertical.
   contents->SetBounds(0, 0, 300, 400);
-  scroll_view_.Layout();
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(),
+  scroll_view_->Layout();
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
             contents->parent()->width());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(),
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(),
             contents->parent()->height());
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, true);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true);
 
   // Add a border, test vertical scrollbar.
   const int kTopPadding = 1;
   const int kLeftPadding = 2;
   const int kBottomPadding = 3;
   const int kRightPadding = 4;
-  scroll_view_.SetBorder(CreateEmptyBorder(kTopPadding, kLeftPadding,
-                                           kBottomPadding, kRightPadding));
+  scroll_view_->SetBorder(CreateEmptyBorder(kTopPadding, kLeftPadding,
+                                            kBottomPadding, kRightPadding));
   contents->SetBounds(0, 0, 50, 400);
-  scroll_view_.Layout();
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth() - kLeftPadding -
+  scroll_view_->Layout();
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth() - kLeftPadding -
                 kRightPadding,
             contents->parent()->width());
   EXPECT_EQ(100 - kTopPadding - kBottomPadding, contents->parent()->height());
-  EXPECT_TRUE(!scroll_view_.horizontal_scroll_bar() ||
-              !scroll_view_.horizontal_scroll_bar()->visible());
-  ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible());
-  gfx::Rect bounds = scroll_view_.vertical_scroll_bar()->bounds();
+  EXPECT_TRUE(!scroll_view_->horizontal_scroll_bar() ||
+              !scroll_view_->horizontal_scroll_bar()->visible());
+  ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible());
+  gfx::Rect bounds = scroll_view_->vertical_scroll_bar()->bounds();
   EXPECT_EQ(100 - VerticalScrollBarWidth() - kRightPadding, bounds.x());
   EXPECT_EQ(100 - kRightPadding, bounds.right());
   EXPECT_EQ(kTopPadding, bounds.y());
@@ -424,16 +429,16 @@
 
   // Horizontal with border.
   contents->SetBounds(0, 0, 400, 50);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(100 - kLeftPadding - kRightPadding, contents->parent()->width());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - kTopPadding -
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - kTopPadding -
                 kBottomPadding,
             contents->parent()->height());
-  ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible());
-  EXPECT_TRUE(!scroll_view_.vertical_scroll_bar() ||
-              !scroll_view_.vertical_scroll_bar()->visible());
-  bounds = scroll_view_.horizontal_scroll_bar()->bounds();
+  ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible());
+  EXPECT_TRUE(!scroll_view_->vertical_scroll_bar() ||
+              !scroll_view_->vertical_scroll_bar()->visible());
+  bounds = scroll_view_->horizontal_scroll_bar()->bounds();
   EXPECT_EQ(kLeftPadding, bounds.x());
   EXPECT_EQ(100 - kRightPadding, bounds.right());
   EXPECT_EQ(100 - kBottomPadding - HorizontalScrollBarHeight(), bounds.y());
@@ -441,26 +446,26 @@
 
   // Both horizontal and vertical with border.
   contents->SetBounds(0, 0, 300, 400);
-  scroll_view_.Layout();
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth() - kLeftPadding -
+  scroll_view_->Layout();
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth() - kLeftPadding -
                 kRightPadding,
             contents->parent()->width());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - kTopPadding -
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - kTopPadding -
                 kBottomPadding,
             contents->parent()->height());
-  bounds = scroll_view_.horizontal_scroll_bar()->bounds();
+  bounds = scroll_view_->horizontal_scroll_bar()->bounds();
   // Check horiz.
-  ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible());
-  bounds = scroll_view_.horizontal_scroll_bar()->bounds();
+  ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible());
+  bounds = scroll_view_->horizontal_scroll_bar()->bounds();
   EXPECT_EQ(kLeftPadding, bounds.x());
   EXPECT_EQ(100 - kRightPadding - VerticalScrollBarWidth(), bounds.right());
   EXPECT_EQ(100 - kBottomPadding - HorizontalScrollBarHeight(), bounds.y());
   EXPECT_EQ(100 - kBottomPadding, bounds.bottom());
   // Check vert.
-  ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible());
-  bounds = scroll_view_.vertical_scroll_bar()->bounds();
+  ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible());
+  bounds = scroll_view_->vertical_scroll_bar()->bounds();
   EXPECT_EQ(100 - VerticalScrollBarWidth() - kRightPadding, bounds.x());
   EXPECT_EQ(100 - kRightPadding, bounds.right());
   EXPECT_EQ(kTopPadding, bounds.y());
@@ -471,11 +476,11 @@
 // Assertions around adding a header.
 TEST_F(ScrollViewTest, Header) {
   CustomView* header = new CustomView;
-  scroll_view_.SetHeader(header);
+  scroll_view_->SetHeader(header);
   View* header_parent = header->parent();
   View* contents = InstallContents();
 
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   // |header|s preferred size is empty, which should result in all space going
   // to contents.
   EXPECT_EQ("0,0 100x0", header->parent()->bounds().ToString());
@@ -502,7 +507,7 @@
   EXPECT_EQ("0,0 0x0", contents->bounds().ToString());
 
   // Remove the header.
-  scroll_view_.SetHeader(NULL);
+  scroll_view_->SetHeader(NULL);
   // SetHeader(NULL) deletes header.
   header = NULL;
   EXPECT_EQ("0,0 100x0", header_parent->bounds().ToString());
@@ -512,111 +517,111 @@
 // Verifies the scrollbars are added as necessary when a header is present.
 TEST_F(ScrollViewTest, ScrollBarsWithHeader) {
   CustomView* header = new CustomView;
-  scroll_view_.SetHeader(header);
+  scroll_view_->SetHeader(header);
   View* contents = InstallContents();
 
   header->SetPreferredSize(gfx::Size(10, 20));
 
   // Size the contents such that vertical scrollbar is needed.
   contents->SetBounds(0, 0, 50, 400);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(0, contents->parent()->x());
   EXPECT_EQ(20, contents->parent()->y());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(),
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
             contents->parent()->width());
   EXPECT_EQ(80, contents->parent()->height());
   EXPECT_EQ(0, header->parent()->x());
   EXPECT_EQ(0, header->parent()->y());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(),
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
             header->parent()->width());
   EXPECT_EQ(20, header->parent()->height());
-  EXPECT_TRUE(!scroll_view_.horizontal_scroll_bar() ||
-              !scroll_view_.horizontal_scroll_bar()->visible());
-  ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible());
+  EXPECT_TRUE(!scroll_view_->horizontal_scroll_bar() ||
+              !scroll_view_->horizontal_scroll_bar()->visible());
+  ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible());
   // Make sure the vertical scrollbar overlaps the header for traditional
   // scrollbars and doesn't overlap the header for overlay scrollbars.
   const int expected_scrollbar_y =
-      scroll_view_.vertical_scroll_bar()->OverlapsContent()
+      scroll_view_->vertical_scroll_bar()->OverlapsContent()
           ? header->bounds().bottom()
           : header->y();
-  EXPECT_EQ(expected_scrollbar_y, scroll_view_.vertical_scroll_bar()->y());
+  EXPECT_EQ(expected_scrollbar_y, scroll_view_->vertical_scroll_bar()->y());
   EXPECT_EQ(header->y(), contents->y());
 
   // Size the contents such that horizontal scrollbar is needed.
   contents->SetBounds(0, 0, 400, 50);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(0, contents->parent()->x());
   EXPECT_EQ(20, contents->parent()->y());
   EXPECT_EQ(100, contents->parent()->width());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - 20,
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - 20,
             contents->parent()->height());
   EXPECT_EQ(0, header->parent()->x());
   EXPECT_EQ(0, header->parent()->y());
   EXPECT_EQ(100, header->parent()->width());
   EXPECT_EQ(20, header->parent()->height());
-  ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible());
-  EXPECT_TRUE(!scroll_view_.vertical_scroll_bar() ||
-              !scroll_view_.vertical_scroll_bar()->visible());
+  ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible());
+  EXPECT_TRUE(!scroll_view_->vertical_scroll_bar() ||
+              !scroll_view_->vertical_scroll_bar()->visible());
 
   // Both horizontal and vertical.
   contents->SetBounds(0, 0, 300, 400);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(0, contents->parent()->x());
   EXPECT_EQ(20, contents->parent()->y());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(),
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
             contents->parent()->width());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - 20,
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - 20,
             contents->parent()->height());
   EXPECT_EQ(0, header->parent()->x());
   EXPECT_EQ(0, header->parent()->y());
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(),
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
             header->parent()->width());
   EXPECT_EQ(20, header->parent()->height());
-  ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible());
-  ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL);
-  EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible());
+  ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible());
+  ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL);
+  EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible());
 }
 
 // Verifies the header scrolls horizontally with the content.
 TEST_F(ScrollViewTest, HeaderScrollsWithContent) {
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
   CustomView* contents = new CustomView;
-  scroll_view_.SetContents(contents);
+  scroll_view_->SetContents(contents);
   contents->SetPreferredSize(gfx::Size(500, 500));
 
   CustomView* header = new CustomView;
-  scroll_view_.SetHeader(header);
+  scroll_view_->SetHeader(header);
   header->SetPreferredSize(gfx::Size(500, 20));
 
-  scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
+  scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
   EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString());
   EXPECT_EQ("0,0", header->origin().ToString());
 
   // Scroll the horizontal scrollbar.
-  ASSERT_TRUE(scroll_view_.horizontal_scroll_bar());
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 1);
+  ASSERT_TRUE(scroll_view_->horizontal_scroll_bar());
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 1);
   EXPECT_EQ("-1,0", test_api.IntegralViewOffset().ToString());
   EXPECT_EQ("-1,0", header->origin().ToString());
 
   // Scrolling the vertical scrollbar shouldn't effect the header.
-  ASSERT_TRUE(scroll_view_.vertical_scroll_bar());
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 1);
+  ASSERT_TRUE(scroll_view_->vertical_scroll_bar());
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 1);
   EXPECT_EQ("-1,-1", test_api.IntegralViewOffset().ToString());
   EXPECT_EQ("-1,0", header->origin().ToString());
 }
 
 // Verifies ScrollRectToVisible() on the child works.
 TEST_F(ScrollViewTest, ScrollRectToVisible) {
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
   CustomView* contents = new CustomView;
-  scroll_view_.SetContents(contents);
+  scroll_view_->SetContents(contents);
   contents->SetPreferredSize(gfx::Size(500, 1000));
 
-  scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
-  scroll_view_.Layout();
+  scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
+  scroll_view_->Layout();
   EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString());
 
   // Scroll to y=405 height=10, this should make the y position of the content
@@ -625,7 +630,7 @@
   const int viewport_height = test_api.contents_viewport()->height();
 
   // Expect there to be a horizontal scrollbar, making the viewport shorter.
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(), viewport_height);
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(), viewport_height);
 
   gfx::ScrollOffset offset = test_api.CurrentOffset();
   EXPECT_EQ(415 - viewport_height, offset.y());
@@ -637,17 +642,17 @@
 
 // Verifies that child scrolls into view when it's focused.
 TEST_F(ScrollViewTest, ScrollChildToVisibleOnFocus) {
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
   CustomView* contents = new CustomView;
-  scroll_view_.SetContents(contents);
+  scroll_view_->SetContents(contents);
   contents->SetPreferredSize(gfx::Size(500, 1000));
   FixedView* child = new FixedView;
   child->SetPreferredSize(gfx::Size(10, 10));
   child->SetPosition(gfx::Point(0, 405));
   contents->AddChildView(child);
 
-  scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 100));
-  scroll_view_.Layout();
+  scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
+  scroll_view_->Layout();
   EXPECT_EQ(gfx::Point(), test_api.IntegralViewOffset());
 
   // Set focus to the child control. This should cause the control to scroll to
@@ -657,7 +662,7 @@
   const int viewport_height = test_api.contents_viewport()->height();
 
   // Expect there to be a horizontal scrollbar, making the viewport shorter.
-  EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(), viewport_height);
+  EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(), viewport_height);
 
   gfx::ScrollOffset offset = test_api.CurrentOffset();
   EXPECT_EQ(415 - viewport_height, offset.y());
@@ -666,138 +671,138 @@
 // Verifies ClipHeightTo() uses the height of the content when it is between the
 // minimum and maximum height values.
 TEST_F(ScrollViewTest, ClipHeightToNormalContentHeight) {
-  scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight);
+  scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight);
 
   const int kNormalContentHeight = 75;
-  scroll_view_.SetContents(
+  scroll_view_->SetContents(
       new views::StaticSizedView(gfx::Size(kWidth, kNormalContentHeight)));
 
   EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight),
-            scroll_view_.GetPreferredSize());
+            scroll_view_->GetPreferredSize());
 
-  scroll_view_.SizeToPreferredSize();
-  scroll_view_.Layout();
+  scroll_view_->SizeToPreferredSize();
+  scroll_view_->Layout();
 
   EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight),
-            scroll_view_.contents()->size());
-  EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), scroll_view_.size());
+            scroll_view_->contents()->size());
+  EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), scroll_view_->size());
 }
 
 // Verifies ClipHeightTo() uses the minimum height when the content is shorter
 // than the minimum height value.
 TEST_F(ScrollViewTest, ClipHeightToShortContentHeight) {
-  scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight);
+  scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight);
 
   const int kShortContentHeight = 10;
   View* contents =
       new views::StaticSizedView(gfx::Size(kWidth, kShortContentHeight));
-  scroll_view_.SetContents(contents);
+  scroll_view_->SetContents(contents);
 
-  EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_.GetPreferredSize());
+  EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_->GetPreferredSize());
 
-  scroll_view_.SizeToPreferredSize();
-  scroll_view_.Layout();
+  scroll_view_->SizeToPreferredSize();
+  scroll_view_->Layout();
 
   // Layered scrolling requires the contents to fill the viewport.
   if (contents->layer()) {
-    EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_.contents()->size());
+    EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_->contents()->size());
   } else {
     EXPECT_EQ(gfx::Size(kWidth, kShortContentHeight),
-              scroll_view_.contents()->size());
+              scroll_view_->contents()->size());
   }
-  EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_.size());
+  EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_->size());
 }
 
 // Verifies ClipHeightTo() uses the maximum height when the content is longer
 // thamn the maximum height value.
 TEST_F(ScrollViewTest, ClipHeightToTallContentHeight) {
-  scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight);
+  scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight);
 
   const int kTallContentHeight = 1000;
-  scroll_view_.SetContents(
+  scroll_view_->SetContents(
       new views::StaticSizedView(gfx::Size(kWidth, kTallContentHeight)));
 
-  EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_.GetPreferredSize());
+  EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_->GetPreferredSize());
 
-  scroll_view_.SizeToPreferredSize();
-  scroll_view_.Layout();
+  scroll_view_->SizeToPreferredSize();
+  scroll_view_->Layout();
 
   // The width may be less than kWidth if the scroll bar takes up some width.
-  EXPECT_GE(kWidth, scroll_view_.contents()->width());
-  EXPECT_EQ(kTallContentHeight, scroll_view_.contents()->height());
-  EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_.size());
+  EXPECT_GE(kWidth, scroll_view_->contents()->width());
+  EXPECT_EQ(kTallContentHeight, scroll_view_->contents()->height());
+  EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_->size());
 }
 
 // Verifies that when ClipHeightTo() produces a scrollbar, it reduces the width
 // of the inner content of the ScrollView.
 TEST_F(ScrollViewTest, ClipHeightToScrollbarUsesWidth) {
-  scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight);
+  scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight);
 
   // Create a view that will be much taller than it is wide.
-  scroll_view_.SetContents(new views::ProportionallySizedView(1000));
+  scroll_view_->SetContents(new views::ProportionallySizedView(1000));
 
   // Without any width, it will default to 0,0 but be overridden by min height.
-  scroll_view_.SizeToPreferredSize();
-  EXPECT_EQ(gfx::Size(0, kMinHeight), scroll_view_.GetPreferredSize());
+  scroll_view_->SizeToPreferredSize();
+  EXPECT_EQ(gfx::Size(0, kMinHeight), scroll_view_->GetPreferredSize());
 
-  gfx::Size new_size(kWidth, scroll_view_.GetHeightForWidth(kWidth));
-  scroll_view_.SetSize(new_size);
-  scroll_view_.Layout();
+  gfx::Size new_size(kWidth, scroll_view_->GetHeightForWidth(kWidth));
+  scroll_view_->SetSize(new_size);
+  scroll_view_->Layout();
 
-  int expected_width = kWidth - scroll_view_.GetScrollBarLayoutWidth();
-  EXPECT_EQ(scroll_view_.contents()->size().width(), expected_width);
-  EXPECT_EQ(scroll_view_.contents()->size().height(), 1000 * expected_width);
-  EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_.size());
+  int expected_width = kWidth - scroll_view_->GetScrollBarLayoutWidth();
+  EXPECT_EQ(scroll_view_->contents()->size().width(), expected_width);
+  EXPECT_EQ(scroll_view_->contents()->size().height(), 1000 * expected_width);
+  EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_->size());
 }
 
 TEST_F(ScrollViewTest, CornerViewVisibility) {
   View* contents = InstallContents();
-  View* corner_view = ScrollViewTestApi(&scroll_view_).corner_view();
+  View* corner_view = ScrollViewTestApi(scroll_view_.get()).corner_view();
 
   contents->SetBounds(0, 0, 200, 200);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
 
   // Corner view should not exist if using overlay scrollbars.
-  if (scroll_view_.vertical_scroll_bar()->OverlapsContent()) {
+  if (scroll_view_->vertical_scroll_bar()->OverlapsContent()) {
     EXPECT_FALSE(corner_view->parent());
     return;
   }
 
   // Corner view should be visible when both scrollbars are visible.
-  EXPECT_EQ(&scroll_view_, corner_view->parent());
+  EXPECT_EQ(scroll_view_.get(), corner_view->parent());
   EXPECT_TRUE(corner_view->visible());
 
   // Corner view should be aligned to the scrollbars.
-  EXPECT_EQ(scroll_view_.vertical_scroll_bar()->x(), corner_view->x());
-  EXPECT_EQ(scroll_view_.horizontal_scroll_bar()->y(), corner_view->y());
-  EXPECT_EQ(scroll_view_.GetScrollBarLayoutWidth(), corner_view->width());
-  EXPECT_EQ(scroll_view_.GetScrollBarLayoutHeight(), corner_view->height());
+  EXPECT_EQ(scroll_view_->vertical_scroll_bar()->x(), corner_view->x());
+  EXPECT_EQ(scroll_view_->horizontal_scroll_bar()->y(), corner_view->y());
+  EXPECT_EQ(scroll_view_->GetScrollBarLayoutWidth(), corner_view->width());
+  EXPECT_EQ(scroll_view_->GetScrollBarLayoutHeight(), corner_view->height());
 
   // Corner view should be removed when only the vertical scrollbar is visible.
   contents->SetBounds(0, 0, 50, 200);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_FALSE(corner_view->parent());
 
   // ... or when only the horizontal scrollbar is visible.
   contents->SetBounds(0, 0, 200, 50);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_FALSE(corner_view->parent());
 
   // ... or when no scrollbar is visible.
   contents->SetBounds(0, 0, 50, 50);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_FALSE(corner_view->parent());
 
   // Corner view should reappear when both scrollbars reappear.
   contents->SetBounds(0, 0, 200, 200);
-  scroll_view_.Layout();
-  EXPECT_EQ(&scroll_view_, corner_view->parent());
+  scroll_view_->Layout();
+  EXPECT_EQ(scroll_view_.get(), corner_view->parent());
   EXPECT_TRUE(corner_view->visible());
 }
 
 TEST_F(ScrollViewTest, ChildWithLayerTest) {
   View* contents = InstallContents();
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   if (test_api.contents_viewport()->layer())
     return;
@@ -812,7 +817,7 @@
   EXPECT_TRUE(test_api.contents_viewport()->layer()->fills_bounds_opaquely());
 
   // Setting a transparent color should make fills opaquely false.
-  scroll_view_.SetBackgroundColor(SK_ColorTRANSPARENT);
+  scroll_view_->SetBackgroundColor(SK_ColorTRANSPARENT);
   EXPECT_FALSE(test_api.contents_viewport()->layer()->fills_bounds_opaquely());
 
   child->DestroyLayer();
@@ -829,12 +834,12 @@
 // is added to the ScrollView's viewport.
 TEST_F(ScrollViewTest, DontCreateLayerOnViewportIfLayerOnScrollViewCreated) {
   View* contents = InstallContents();
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   if (test_api.contents_viewport()->layer())
     return;
 
-  scroll_view_.SetPaintToLayer();
+  scroll_view_->SetPaintToLayer();
 
   View* child = new View();
   contents->AddChildView(child);
@@ -853,35 +858,35 @@
   // Size the contents such that vertical scrollbar is needed.
   // Since it is overlaid, the ViewPort size should match the ScrollView.
   contents->SetBounds(0, 0, 50, 400);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(100, contents->parent()->width());
   EXPECT_EQ(100, contents->parent()->height());
-  EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutWidth());
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, true);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false);
+  EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutWidth());
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false);
 
   // Size the contents such that horizontal scrollbar is needed.
   contents->SetBounds(0, 0, 400, 50);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(100, contents->parent()->width());
   EXPECT_EQ(100, contents->parent()->height());
-  EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutHeight());
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, false);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true);
+  EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutHeight());
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, false);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true);
 
   // Both horizontal and vertical scrollbars.
   contents->SetBounds(0, 0, 300, 400);
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(100, contents->parent()->width());
   EXPECT_EQ(100, contents->parent()->height());
-  EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutWidth());
-  EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutHeight());
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, true);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true);
+  EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutWidth());
+  EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutHeight());
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true);
 
   // Make sure the horizontal and vertical scrollbars don't overlap each other.
-  gfx::Rect vert_bounds = scroll_view_.vertical_scroll_bar()->bounds();
-  gfx::Rect horiz_bounds = scroll_view_.horizontal_scroll_bar()->bounds();
+  gfx::Rect vert_bounds = scroll_view_->vertical_scroll_bar()->bounds();
+  gfx::Rect horiz_bounds = scroll_view_->horizontal_scroll_bar()->bounds();
   EXPECT_EQ(vert_bounds.x(), horiz_bounds.right());
   EXPECT_EQ(horiz_bounds.y(), vert_bounds.bottom());
 
@@ -982,11 +987,11 @@
 // Test that increasing the size of the viewport "below" scrolled content causes
 // the content to scroll up so that it still fills the viewport.
 TEST_F(ScrollViewTest, ConstrainScrollToBounds) {
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   View* contents = InstallContents();
   contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
-  scroll_view_.Layout();
+  scroll_view_->Layout();
 
   EXPECT_EQ(gfx::ScrollOffset(), test_api.CurrentOffset());
 
@@ -996,49 +1001,49 @@
   EXPECT_NE(gfx::ScrollOffset(), fully_scrolled);
 
   // Making the viewport 55 pixels taller should scroll up the same amount.
-  scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 155));
-  scroll_view_.Layout();
+  scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 155));
+  scroll_view_->Layout();
   EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y());
   EXPECT_EQ(fully_scrolled.x(), test_api.CurrentOffset().x());
 
   // And 77 pixels wider should scroll left. Also make it short again: the y-
   // offset from the last change should remain.
-  scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 177, 100));
-  scroll_view_.Layout();
+  scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 177, 100));
+  scroll_view_->Layout();
   EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y());
   EXPECT_EQ(fully_scrolled.x() - 77, test_api.CurrentOffset().x());
 }
 
 // Calling Layout on ScrollView should not reset the scroll location.
 TEST_F(ScrollViewTest, ContentScrollNotResetOnLayout) {
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   CustomView* contents = new CustomView;
   contents->SetPreferredSize(gfx::Size(300, 300));
-  scroll_view_.SetContents(contents);
-  scroll_view_.ClipHeightTo(0, 150);
-  scroll_view_.SizeToPreferredSize();
+  scroll_view_->SetContents(contents);
+  scroll_view_->ClipHeightTo(0, 150);
+  scroll_view_->SizeToPreferredSize();
   // ScrollView preferred width matches that of |contents|, with the height
   // capped at the value we clipped to.
-  EXPECT_EQ(gfx::Size(300, 150), scroll_view_.size());
+  EXPECT_EQ(gfx::Size(300, 150), scroll_view_->size());
 
   // Scroll down.
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 25);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 25);
   EXPECT_EQ(25, test_api.CurrentOffset().y());
   // Call Layout; no change to scroll position.
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(25, test_api.CurrentOffset().y());
   // Change contents of |contents|, call Layout; still no change to scroll
   // position.
   contents->SetPreferredSize(gfx::Size(300, 500));
   contents->InvalidateLayout();
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(25, test_api.CurrentOffset().y());
 
   // Change |contents| to be shorter than the ScrollView's clipped height.
   // This /will/ change the scroll location due to ConstrainScrollToBounds.
   contents->SetPreferredSize(gfx::Size(300, 50));
-  scroll_view_.Layout();
+  scroll_view_->Layout();
   EXPECT_EQ(0, test_api.CurrentOffset().y());
 }
 
@@ -1046,16 +1051,16 @@
 TEST_F(ScrollViewTest, VerticalOverflowIndicators) {
   const int kWidth = 100;
 
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   // Set up with vertical scrollbar.
   FixedView* contents = new FixedView;
   contents->SetPreferredSize(gfx::Size(kWidth, kMaxHeight * 5));
-  scroll_view_.SetContents(contents);
-  scroll_view_.ClipHeightTo(0, kMaxHeight);
+  scroll_view_->SetContents(contents);
+  scroll_view_->ClipHeightTo(0, kMaxHeight);
 
   // Make sure the size is set such that no horizontal scrollbar gets shown.
-  scroll_view_.SetSize(
+  scroll_view_->SetSize(
       gfx::Size(kWidth + test_api.GetBaseScrollBar(VERTICAL)->GetThickness(),
                 kMaxHeight));
 
@@ -1064,8 +1069,8 @@
 
   // The vertical scroll bar should be visible and the horizontal scroll bar
   // should not.
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, true);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false);
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false);
 
   // The overflow indicator on the bottom should be visible.
   EXPECT_TRUE(test_api.more_content_bottom()->visible());
@@ -1079,7 +1084,7 @@
 
   // Now scroll the view to someplace in the middle of the scrollable region.
   int offset = kMaxHeight * 2;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
   EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset());
 
   // At this point, both overflow indicators on the top and bottom should be
@@ -1093,7 +1098,7 @@
 
   // Finally scroll the view to end of the scrollable region.
   offset = kMaxHeight * 4;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
   EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset());
 
   // The overflow indicator on the bottom should not be visible.
@@ -1111,15 +1116,15 @@
   const int kWidth = 100;
   const int kHeight = 100;
 
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   // Set up with horizontal scrollbar.
   FixedView* contents = new FixedView;
   contents->SetPreferredSize(gfx::Size(kWidth * 5, kHeight));
-  scroll_view_.SetContents(contents);
+  scroll_view_->SetContents(contents);
 
   // Make sure the size is set such that no vertical scrollbar gets shown.
-  scroll_view_.SetSize(gfx::Size(
+  scroll_view_->SetSize(gfx::Size(
       kWidth, kHeight + test_api.GetBaseScrollBar(HORIZONTAL)->GetThickness()));
 
   contents->SetBounds(0, 0, kWidth * 5, kHeight);
@@ -1129,8 +1134,8 @@
 
   // The horizontal scroll bar should be visible and the vertical scroll bar
   // should not.
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true);
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, false);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, false);
 
   // The overflow indicator on the right should be visible.
   EXPECT_TRUE(test_api.more_content_right()->visible());
@@ -1144,7 +1149,7 @@
 
   // Now scroll the view to someplace in the middle of the scrollable region.
   int offset = kWidth * 2;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset);
   EXPECT_EQ(gfx::ScrollOffset(offset, 0), test_api.CurrentOffset());
 
   // At this point, both overflow indicators on the left and right should be
@@ -1158,7 +1163,7 @@
 
   // Finally scroll the view to end of the scrollable region.
   offset = kWidth * 4;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset);
   EXPECT_EQ(gfx::ScrollOffset(offset, 0), test_api.CurrentOffset());
 
   // The overflow indicator on the right should not be visible.
@@ -1176,22 +1181,22 @@
   const int kWidth = 100;
   const int kHeight = 100;
 
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   // Set up with both horizontal and vertical scrollbars.
   FixedView* contents = new FixedView;
   contents->SetPreferredSize(gfx::Size(kWidth * 5, kHeight * 5));
-  scroll_view_.SetContents(contents);
+  scroll_view_->SetContents(contents);
 
   // Make sure the size is set such that both scrollbars are shown.
-  scroll_view_.SetSize(gfx::Size(kWidth, kHeight));
+  scroll_view_->SetSize(gfx::Size(kWidth, kHeight));
 
   // Make sure the initial origin is 0,0
   EXPECT_EQ(gfx::ScrollOffset(0, 0), test_api.CurrentOffset());
 
   // The horizontal and vertical scroll bars should be visible.
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true);
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true);
 
   // The overflow indicators on the right and bottom should not be visible since
   // they are against the scrollbars.
@@ -1205,8 +1210,8 @@
   // Now scroll the view to someplace in the middle of the horizontal scrollable
   // region.
   int offset_x = kWidth * 2;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL),
-                                offset_x);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL),
+                                 offset_x);
   EXPECT_EQ(gfx::ScrollOffset(offset_x, 0), test_api.CurrentOffset());
 
   // Since there is a vertical scrollbar only the overflow indicator on the left
@@ -1220,8 +1225,8 @@
 
   // Next, scroll the view to end of the scrollable region.
   offset_x = kWidth * 4;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL),
-                                offset_x);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL),
+                                 offset_x);
   EXPECT_EQ(gfx::ScrollOffset(offset_x, 0), test_api.CurrentOffset());
 
   // The overflow indicator on the right should still not be visible.
@@ -1237,7 +1242,7 @@
   EXPECT_FALSE(test_api.more_content_bottom()->visible());
 
   // Return the view back to the horizontal origin.
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 0);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 0);
   EXPECT_EQ(gfx::ScrollOffset(0, 0), test_api.CurrentOffset());
 
   // The overflow indicators on the right and bottom should not be visible since
@@ -1253,7 +1258,7 @@
   // Now scroll the view to somplace in the middle of the vertical scrollable
   // region.
   int offset_y = kHeight * 2;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y);
   EXPECT_EQ(gfx::ScrollOffset(0, offset_y), test_api.CurrentOffset());
 
   // Similar to the above, since there is a horizontal scrollbar only the
@@ -1268,7 +1273,7 @@
 
   // Finally, for the vertical test scroll the region all the way to the end.
   offset_y = kHeight * 4;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y);
   EXPECT_EQ(gfx::ScrollOffset(0, offset_y), test_api.CurrentOffset());
 
   // The overflow indicator on the bottom should still not be visible.
@@ -1286,8 +1291,8 @@
   // Back to the horizontal. Scroll all the way to the end in the horizontal
   // direction.
   offset_x = kWidth * 4;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL),
-                                offset_x);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL),
+                                 offset_x);
   EXPECT_EQ(gfx::ScrollOffset(offset_x, offset_y), test_api.CurrentOffset());
 
   // The overflow indicator on the bottom and right should still not be visible.
@@ -1302,19 +1307,19 @@
 TEST_F(ScrollViewTest, VerticalWithHeaderOverflowIndicators) {
   const int kWidth = 100;
 
-  ScrollViewTestApi test_api(&scroll_view_);
+  ScrollViewTestApi test_api(scroll_view_.get());
 
   // Set up with vertical scrollbar and a header.
   FixedView* contents = new FixedView;
   CustomView* header = new CustomView;
   contents->SetPreferredSize(gfx::Size(kWidth, kMaxHeight * 5));
-  scroll_view_.SetContents(contents);
+  scroll_view_->SetContents(contents);
   header->SetPreferredSize(gfx::Size(10, 20));
-  scroll_view_.SetHeader(header);
-  scroll_view_.ClipHeightTo(0, kMaxHeight + header->height());
+  scroll_view_->SetHeader(header);
+  scroll_view_->ClipHeightTo(0, kMaxHeight + header->height());
 
   // Make sure the size is set such that no horizontal scrollbar gets shown.
-  scroll_view_.SetSize(
+  scroll_view_->SetSize(
       gfx::Size(kWidth + test_api.GetBaseScrollBar(VERTICAL)->GetThickness(),
                 kMaxHeight + header->height()));
 
@@ -1323,8 +1328,8 @@
 
   // The vertical scroll bar should be visible and the horizontal scroll bar
   // should not.
-  CheckScrollbarVisibility(scroll_view_, VERTICAL, true);
-  CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false);
+  CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true);
+  CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false);
 
   // The overflow indicator on the bottom should be visible.
   EXPECT_TRUE(test_api.more_content_bottom()->visible());
@@ -1338,7 +1343,7 @@
 
   // Now scroll the view to someplace in the middle of the scrollable region.
   int offset = kMaxHeight * 2;
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
   EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset());
 
   // At this point, only the overflow indicator on the bottom should be visible
@@ -1353,7 +1358,7 @@
 
   // Finally scroll the view to end of the scrollable region.
   offset = test_api.GetBaseScrollBar(VERTICAL)->GetMaxPosition();
-  scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
+  scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset);
   EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset());
 
   // The overflow indicator on the bottom should not be visible now.
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
index 082ea64..f499e20 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
@@ -32,7 +32,10 @@
 
 class TabbedPaneTest : public ViewsTestBase {
  public:
-  TabbedPaneTest() {
+  TabbedPaneTest() = default;
+
+  void SetUp() override {
+    ViewsTestBase::SetUp();
     tabbed_pane_ = std::make_unique<TabbedPane>();
     tabbed_pane_->set_owned_by_client();
   }
diff --git a/ui/views/mus/remote_view/remote_view_host.cc b/ui/views/mus/remote_view/remote_view_host.cc
index 3023825..c5f9e57 100644
--- a/ui/views/mus/remote_view/remote_view_host.cc
+++ b/ui/views/mus/remote_view/remote_view_host.cc
@@ -24,9 +24,21 @@
   // Only works with mus.
   DCHECK_EQ(aura::Env::Mode::MUS, aura::Env::GetInstanceDontCreate()->mode());
 
+  embed_token_ = embed_token;
+  embed_flags_ = embed_flags;
+  embed_callback_ = std::move(callback);
+
+  if (GetWidget())
+    CreateEmbeddingRoot();
+}
+
+void RemoteViewHost::CreateEmbeddingRoot() {
   // Should not be attached to anything.
   DCHECK(!native_view());
 
+  // There is a pending embed request.
+  DCHECK(!embed_token_.is_empty());
+
   embedding_root_ = std::make_unique<aura::Window>(nullptr);
   embedding_root_->set_owned_by_parent(false);
 
@@ -36,33 +48,29 @@
   embedding_root_->SetType(aura::client::WINDOW_TYPE_CONTROL);
   embedding_root_->Init(ui::LAYER_NOT_DRAWN);
 
+  // Must happen before EmbedUsingToken call for window server to figure out
+  // the relevant display.
+  Attach(embedding_root_.get());
+
   aura::WindowPortMus::Get(embedding_root_.get())
-      ->EmbedUsingToken(embed_token, embed_flags,
+      ->EmbedUsingToken(embed_token_, embed_flags_,
                         base::BindOnce(&RemoteViewHost::OnEmbedResult,
-                                       weak_ptr_factory_.GetWeakPtr(),
-                                       embed_token, std::move(callback)));
+                                       weak_ptr_factory_.GetWeakPtr()));
 }
 
-void RemoteViewHost::OnEmbedResult(const base::UnguessableToken& token,
-                                   EmbedCallback callback,
-                                   bool success) {
-  LOG_IF(ERROR, !success) << "Failed to embed, token=" << token;
-
-  // Attach to |embedding_root_| if embed succeeds and this view is added to a
-  // widget but has not yet attached to |embedding_root_|.
-  if (success && GetWidget() && !native_view())
-    Attach(embedding_root_.get());
+void RemoteViewHost::OnEmbedResult(bool success) {
+  LOG_IF(ERROR, !success) << "Failed to embed, token=" << embed_token_;
 
   if (!success && embedding_root_)
     embedding_root_.reset();
 
-  if (callback)
-    std::move(callback).Run(success);
+  if (embed_callback_)
+    std::move(embed_callback_).Run(success);
 }
 
 void RemoteViewHost::AddedToWidget() {
-  if (embedding_root_ && !native_view())
-    Attach(embedding_root_.get());
+  if (!native_view() && !embed_token_.is_empty())
+    CreateEmbeddingRoot();
 }
 
 }  // namespace views
diff --git a/ui/views/mus/remote_view/remote_view_host.h b/ui/views/mus/remote_view/remote_view_host.h
index d52ddaf..44b1552 100644
--- a/ui/views/mus/remote_view/remote_view_host.h
+++ b/ui/views/mus/remote_view/remote_view_host.h
@@ -23,25 +23,32 @@
   RemoteViewHost();
   ~RemoteViewHost() override;
 
-  // Creates an aura::window to embed the remote contents and attach to it when
-  // the embed succeeds. |embed_token| is the token obtained from the WindowTree
-  // embed API (ScheduleEmbed/ForExistingClient). |embed_flags| are the
-  // embedding flags (see window_tree_constants.mojom). |callback| is an
-  // optional callback invoked with the embed result.
+  // Embeds the remote contents after this view is added to a widget.
+  // |embed_token| is the token obtained from the WindowTree embed API
+  // (ScheduleEmbed/ForExistingClient). |embed_flags| are the embedding flags
+  // (see window_tree_constants.mojom). |callback| is an optional callback
+  // invoked with the embed result.
+  // Note that |callback| should not be used to add the view to a widget because
+  // the actual embedding only happens after the view is added.
   using EmbedCallback = base::OnceCallback<void(bool success)>;
   void EmbedUsingToken(const base::UnguessableToken& embed_token,
                        int embed_flags,
                        EmbedCallback callback);
 
  private:
+  // Creates the embedding aura::Window and attach to it.
+  void CreateEmbeddingRoot();
+
   // Invoked after the embed operation.
-  void OnEmbedResult(const base::UnguessableToken& token,
-                     EmbedCallback callback,
-                     bool success);
+  void OnEmbedResult(bool success);
 
   // views::NativeViewHost:
   void AddedToWidget() override;
 
+  base::UnguessableToken embed_token_;
+  int embed_flags_ = 0;
+  EmbedCallback embed_callback_;
+
   std::unique_ptr<aura::Window> embedding_root_;
   base::WeakPtrFactory<RemoteViewHost> weak_ptr_factory_{this};
 
diff --git a/ui/views/mus/remote_view/remote_view_host_unittest.cc b/ui/views/mus/remote_view/remote_view_host_unittest.cc
index 6497543..611c001 100644
--- a/ui/views/mus/remote_view/remote_view_host_unittest.cc
+++ b/ui/views/mus/remote_view/remote_view_host_unittest.cc
@@ -87,13 +87,13 @@
 
   // Ownership will be passed to |widget| later.
   RemoteViewHost* host = new RemoteViewHost();
+  std::unique_ptr<views::Widget> widget = CreateTestWidget(host);
+  EXPECT_FALSE(host->native_view());
 
   // Embed fails with unknown token.
   EXPECT_FALSE(Embed(host, unknown_token));
 
-  // |host| is not attached before and after adding to a widget.
-  EXPECT_FALSE(host->native_view());
-  std::unique_ptr<views::Widget> widget = CreateTestWidget(host);
+  // |host| is not attached after adding to a widget.
   EXPECT_FALSE(host->native_view());
 }
 
@@ -117,29 +117,6 @@
   EXPECT_TRUE(host->native_view());
 }
 
-// Tests when RemoveViewHost is added to a widget while embedding.
-TEST_F(RemoteViewHostTest, AddToWidgetWhileEmbedding) {
-  const base::UnguessableToken token = base::UnguessableToken::Create();
-  window_tree()->AddScheduledEmbedToken(token);
-
-  // Ownership will be passed to |widget| later.
-  RemoteViewHost* host = new RemoteViewHost();
-
-  // |host| is not attached because embed operation is not performed.
-  EXPECT_FALSE(host->native_view());
-
-  base::RunLoop run_loop;
-  std::unique_ptr<views::Widget> widget;
-  host->EmbedUsingToken(
-      token, 0u /* no flags */,
-      base::BindOnce(&RemoteViewHostTest::CreateTestWidgetWhileEmbeddingHelper,
-                     base::Unretained(this), &run_loop, host, &widget));
-  run_loop.Run();
-
-  // |host| is attached to the embedding window.
-  EXPECT_TRUE(host->native_view());
-}
-
 // Tests when RemoveViewHost is added to a widget after embedding.
 TEST_F(RemoteViewHostTest, AddToWidgetAfterEmbed) {
   const base::UnguessableToken token = base::UnguessableToken::Create();
@@ -148,12 +125,26 @@
   // Ownership will be passed to |widget| later.
   RemoteViewHost* host = new RemoteViewHost();
 
-  // Embed succeeds.
-  EXPECT_TRUE(Embed(host, token));
+  // Request embedding but it will be deferred until added to a widget.
+  base::RunLoop run_loop;
+  bool embed_result = false;
+  host->EmbedUsingToken(
+      token, 0u /* no flags */,
+      base::BindOnce(
+          [](base::RunLoop* run_loop, bool* result, bool success) {
+            *result = success;
+            run_loop->Quit();
+          },
+          &run_loop, &embed_result));
 
-  // |host| is attached after adding to a widget.
+  // |host| is not attached before adding to a widget.
   EXPECT_FALSE(host->native_view());
+
+  // Add to a widget and wait for embed to finish.
   std::unique_ptr<views::Widget> widget = CreateTestWidget(host);
+  run_loop.Run();
+
+  // |host| is attached after added to a widget.
   EXPECT_TRUE(host->native_view());
 }
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 635c932..dbe61e9 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -31,14 +31,22 @@
 
 void DesktopWindowTreeHostPlatform::SetBoundsInDIP(
     const gfx::Rect& bounds_in_dip) {
+  DCHECK_NE(0, device_scale_factor());
   SetBoundsInPixels(
       gfx::ConvertRectToPixel(device_scale_factor(), bounds_in_dip));
 }
 
 void DesktopWindowTreeHostPlatform::Init(const Widget::InitParams& params) {
+  CreateAndSetDefaultPlatformWindow();
+  // TODO(sky): this should be |params.force_software_compositing|, figure out
+  // why it has to be true now.
+  const bool use_software_compositing = true;
+  CreateCompositor(viz::FrameSinkId(), use_software_compositing);
+  aura::WindowTreeHost::OnAcceleratedWidgetAvailable();
+  InitHost();
   if (!params.bounds.IsEmpty())
     SetBoundsInDIP(params.bounds);
-  InitHost();
+  window()->Show();
 }
 
 void DesktopWindowTreeHostPlatform::OnNativeWidgetCreated(
@@ -59,7 +67,7 @@
 DesktopWindowTreeHostPlatform::CreateDragDropClient(
     DesktopNativeCursorManager* cursor_manager) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return nullptr;
 }
 
@@ -94,8 +102,10 @@
 
 void DesktopWindowTreeHostPlatform::ShowWindowWithState(
     ui::WindowShowState show_state) {
-  if (compositor())
+  if (compositor()) {
     platform_window()->Show();
+    compositor()->SetVisible(true);
+  }
 
   switch (show_state) {
     case ui::SHOW_STATE_MAXIMIZED:
@@ -137,7 +147,7 @@
 
 bool DesktopWindowTreeHostPlatform::IsVisible() const {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return true;
 }
 
@@ -149,11 +159,11 @@
 }
 
 void DesktopWindowTreeHostPlatform::StackAbove(aura::Window* window) {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::StackAtTop() {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::CenterWindow(const gfx::Size& size) {
@@ -178,7 +188,7 @@
 void DesktopWindowTreeHostPlatform::GetWindowPlacement(
     gfx::Rect* bounds,
     ui::WindowShowState* show_state) const {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   *bounds = gfx::Rect(0, 0, 640, 840);
   *show_state = ui::SHOW_STATE_NORMAL;
 }
@@ -200,7 +210,7 @@
 }
 
 gfx::Rect DesktopWindowTreeHostPlatform::GetRestoredBounds() const {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return gfx::Rect(0, 0, 640, 840);
 }
 
@@ -217,22 +227,22 @@
 
 void DesktopWindowTreeHostPlatform::SetShape(
     std::unique_ptr<Widget::ShapeRects> native_shape) {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::Activate() {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::Deactivate() {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 bool DesktopWindowTreeHostPlatform::IsActive() const {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
@@ -250,25 +260,23 @@
 
 bool DesktopWindowTreeHostPlatform::IsMaximized() const {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
 bool DesktopWindowTreeHostPlatform::IsMinimized() const {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
 bool DesktopWindowTreeHostPlatform::HasCapture() const {
-  // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
-  return false;
+  return platform_window()->HasCapture();
 }
 
 void DesktopWindowTreeHostPlatform::SetAlwaysOnTop(bool always_on_top) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 bool DesktopWindowTreeHostPlatform::IsAlwaysOnTop() const {
@@ -279,7 +287,7 @@
 void DesktopWindowTreeHostPlatform::SetVisibleOnAllWorkspaces(
     bool always_visible) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 bool DesktopWindowTreeHostPlatform::IsVisibleOnAllWorkspaces() const {
@@ -290,13 +298,13 @@
 bool DesktopWindowTreeHostPlatform::SetWindowTitle(
     const base::string16& title) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
 void DesktopWindowTreeHostPlatform::ClearNativeFocus() {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 Widget::MoveLoopResult DesktopWindowTreeHostPlatform::RunMoveLoop(
@@ -304,19 +312,19 @@
     Widget::MoveLoopSource source,
     Widget::MoveLoopEscapeBehavior escape_behavior) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return Widget::MOVE_LOOP_CANCELED;
 }
 
 void DesktopWindowTreeHostPlatform::EndMoveLoop() {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::SetVisibilityChangedAnimationsEnabled(
     bool value) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 NonClientFrameView* DesktopWindowTreeHostPlatform::CreateNonClientFrameView() {
@@ -335,54 +343,54 @@
 
 void DesktopWindowTreeHostPlatform::SetFullscreen(bool fullscreen) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 bool DesktopWindowTreeHostPlatform::IsFullscreen() const {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
 void DesktopWindowTreeHostPlatform::SetOpacity(float opacity) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::SetWindowIcons(
     const gfx::ImageSkia& window_icon,
     const gfx::ImageSkia& app_icon) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::InitModalType(ui::ModalType modal_type) {
   // TODO: needs PlatformWindow support (alternatively, remove as
   // DesktopWindowTreeHostX11 doesn't support at all).
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void DesktopWindowTreeHostPlatform::FlashFrame(bool flash_frame) {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 bool DesktopWindowTreeHostPlatform::IsAnimatingClosed() const {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
 bool DesktopWindowTreeHostPlatform::IsTranslucentWindowOpacitySupported()
     const {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
 void DesktopWindowTreeHostPlatform::SizeConstraintsChanged() {
   // TODO: needs PlatformWindow support.
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 bool DesktopWindowTreeHostPlatform::ShouldUpdateWindowTransparency() const {
@@ -415,4 +423,5 @@
 Widget* DesktopWindowTreeHostPlatform::GetWidget() {
   return native_widget_delegate_->AsWidget();
 }
+
 }  // namespace views