diff --git a/.gn b/.gn
index 8fb99ced..6f277297 100644
--- a/.gn
+++ b/.gn
@@ -323,7 +323,6 @@
   "//third_party/accessibility_test_framework/*",
   "//third_party/adobe/*",
   "//third_party/afl/*",
-  "//third_party/analytics/*",
   "//third_party/android_build_tools/*",
   "//third_party/android_crazy_linker/*",
   "//third_party/android_data_chart/*",
diff --git a/DEPS b/DEPS
index 034d32ea..f40187f 100644
--- a/DEPS
+++ b/DEPS
@@ -116,7 +116,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '73bc258c965df798cbae6c86d1d543bd51c1c084',
+  'v8_revision': 'ab168019ec9085b4e6c0c1e4fee8f3b763682eaf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -124,7 +124,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '4f2b94cb9a6bb640110ea65a7637872b83acbba8',
+  'angle_revision': '752d220a5f31c68ecba0fc845493dcc36bc40eb4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -136,7 +136,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': 'adb9e705092bbf03eebe0edaf687090266b64fcc',
+  'pdfium_revision': '3e2cdea95cd8803638194a51aefe57f16dba55df',
   # 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.
@@ -651,7 +651,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'bd884bbd838523f137e7493248b08ccbb3d28ad7',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '36900aad7d961f0efd79da61cff67075758ac9c7',
       'condition': 'checkout_linux',
   },
 
@@ -957,7 +957,7 @@
 
   'src/third_party/nasm': {
       'url': Var('chromium_git') + '/chromium/deps/nasm.git' + '@' +
-      'a0a6951e259bd347c133969740348bb5ebb468c4'
+      '4ee6a69ce33be1e96fd3c44a6e3ae3d8177453da'
   },
 
   'src/third_party/netty-tcnative/src': {
@@ -1008,7 +1008,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '1561e23284f165220888a9f6ea36c77c952344e8',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'fb903439b2138e98d4e4de3c2850134cec0cd35b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1171,7 +1171,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '5b6cbd789b9b91b4e46dde883c9f2ecb31eddade',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6b3d18164b4ac2eb954663d162644a12eeb13ea6',
+    Var('webrtc_git') + '/src.git' + '@' + '59cfd35438753acd69859e8a2f98f6490e68d6a0',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1202,7 +1202,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@af1cdca8c14045c56c5ca603878997e9ffbfcea2',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7f2a2ae33b386bbbed06a86263b9273d93259e1b',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 0cf07c5c..7003b2d 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -235,7 +235,6 @@
 
 AwContents::AwContents(std::unique_ptr<WebContents> web_contents)
     : content::WebContentsObserver(web_contents.get()),
-      functor_(nullptr),
       browser_view_renderer_(
           this,
           base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})),
@@ -395,16 +394,8 @@
 }
 
 void AwContents::SetAwGLFunctor(AwGLFunctor* functor) {
-  if (functor == functor_) {
-    return;
-  }
-  functor_ = functor;
-  if (functor_) {
-    browser_view_renderer_.SetCurrentCompositorFrameConsumer(
-        functor_->GetCompositorFrameConsumer());
-  } else {
-    browser_view_renderer_.SetCurrentCompositorFrameConsumer(nullptr);
-  }
+  browser_view_renderer_.SetCurrentCompositorFrameConsumer(
+      functor ? functor->GetCompositorFrameConsumer() : nullptr);
 }
 
 void AwContents::SetAwGLFunctor(JNIEnv* env,
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index 5abbceb..6dbeb700 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -374,7 +374,6 @@
   void SetAwGLFunctor(AwGLFunctor* functor);
 
   JavaObjectWeakGlobalRef java_ref_;
-  AwGLFunctor* functor_;
   BrowserViewRenderer browser_view_renderer_;  // Must outlive |web_contents_|.
   std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<AwWebContentsDelegate> web_contents_delegate_;
diff --git a/android_webview/common/aw_content_client.cc b/android_webview/common/aw_content_client.cc
index 1514c944..babb0fe 100644
--- a/android_webview/common/aw_content_client.cc
+++ b/android_webview/common/aw_content_client.cc
@@ -45,6 +45,7 @@
   schemes->local_schemes.push_back(url::kContentScheme);
   schemes->secure_schemes.push_back(
       android_webview::kAndroidWebViewVideoPosterScheme);
+  schemes->allow_non_standard_schemes_in_origins = true;
 }
 
 std::string AwContentClient::GetProduct() const {
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
index d77fc1c..e34c01d 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -265,7 +265,7 @@
                     || mAppTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP);
 
             mAwContents = new AwContents(mFactory.getBrowserContextOnUiThread(), mWebView, mContext,
-                    new InternalAccessAdapter(), new WebViewNativeDrawGLFunctorFactory(),
+                    new InternalAccessAdapter(), new WebViewNativeDrawFunctorFactory(),
                     mContentsClientAdapter, mWebSettings.getAwSettings(),
                     new AwContents.DependencyFactory() {
                         @Override
@@ -2275,11 +2275,10 @@
         checkThread();
         return new AwPrintDocumentAdapter(mAwContents.getPdfExporter(), documentName);
     }
-    // AwContents.NativeDrawGLFunctorFactory implementation ----------------------------------
-    private class WebViewNativeDrawGLFunctorFactory
-            implements AwContents.NativeDrawGLFunctorFactory {
+    // AwContents.NativeDrawFunctorFactory implementation ----------------------------------
+    private class WebViewNativeDrawFunctorFactory implements AwContents.NativeDrawFunctorFactory {
         @Override
-        public AwContents.NativeDrawGLFunctor createFunctor(long context) {
+        public AwContents.NativeDrawGLFunctor createGLFunctor(long context) {
             return new DrawGLFunctor(context, mFactory.getWebViewDelegate());
         }
     }
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 85b9d55..58871c3 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -255,11 +255,11 @@
      * calling back into Chromium code to render the the contents of a Chromium frame into
      * an Android view.
      */
-    public interface NativeDrawGLFunctorFactory {
+    public interface NativeDrawFunctorFactory {
         /**
-         * Create a functor associated with native context |context|.
+         * Create a GL functor associated with native context |context|.
          */
-        NativeDrawGLFunctor createFunctor(long context);
+        NativeDrawGLFunctor createGLFunctor(long context);
     }
 
     /**
@@ -360,7 +360,7 @@
     private final AwContentsIoThreadClient mIoThreadClient;
     private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
     private InternalAccessDelegate mInternalAccessAdapter;
-    private final NativeDrawGLFunctorFactory mNativeDrawGLFunctorFactory;
+    private final NativeDrawFunctorFactory mNativeDrawFunctorFactory;
     private final AwLayoutSizer mLayoutSizer;
     private final AwZoomControls mZoomControls;
     private final AwScrollOffsetManager mScrollOffsetManager;
@@ -816,7 +816,7 @@
      * @param containerView the view-hierarchy item this object will be bound to.
      * @param context the context to use, usually containerView.getContext().
      * @param internalAccessAdapter to access private methods on containerView.
-     * @param nativeGLDelegate to access the GL functor provided by the WebView.
+     * @param nativeDrawFunctorFactory to access the functor provided by the WebView.
      * @param contentsClient will receive API callbacks from this WebView Contents.
      * @param awSettings AwSettings instance used to configure the AwContents.
      *
@@ -824,10 +824,10 @@
      */
     public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
             InternalAccessDelegate internalAccessAdapter,
-            NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory, AwContentsClient contentsClient,
+            NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
             AwSettings awSettings) {
         this(browserContext, containerView, context, internalAccessAdapter,
-                nativeDrawGLFunctorFactory, contentsClient, awSettings, new DependencyFactory());
+                nativeDrawFunctorFactory, contentsClient, awSettings, new DependencyFactory());
     }
 
     /**
@@ -839,7 +839,7 @@
      */
     public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
             InternalAccessDelegate internalAccessAdapter,
-            NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory, AwContentsClient contentsClient,
+            NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
             AwSettings settings, DependencyFactory dependencyFactory) {
         try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwContents.constructor")) {
             mRendererPriority = RendererPriority.HIGH;
@@ -859,7 +859,7 @@
             mAutofillProvider = dependencyFactory.createAutofillProvider(context, mContainerView);
             mAppTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
             mInternalAccessAdapter = internalAccessAdapter;
-            mNativeDrawGLFunctorFactory = nativeDrawGLFunctorFactory;
+            mNativeDrawFunctorFactory = nativeDrawFunctorFactory;
             mContentsClient = contentsClient;
             mContentsClient.getCallbackHelper().setCancelCallbackPoller(
                     () -> AwContents.this.isDestroyedOrNoOperation(NO_WARN));
@@ -3381,7 +3381,7 @@
             }
 
             if (canvas.isHardwareAccelerated() && mDrawFunctor == null) {
-                setFunctor(new AwGLFunctor(mNativeDrawGLFunctorFactory, mContainerView));
+                setFunctor(new AwGLFunctor(mNativeDrawFunctorFactory, mContainerView));
             }
 
             mScrollOffsetManager.syncScrollOffsetFromOnDraw();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java b/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
index c067cc4..2893030 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
@@ -27,10 +27,10 @@
     // Counts outstanding requestDrawGL calls as well as window attach count.
     private int mRefCount;
 
-    public AwGLFunctor(AwContents.NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-            ViewGroup containerView) {
+    public AwGLFunctor(
+            AwContents.NativeDrawFunctorFactory nativeDrawFunctorFactory, ViewGroup containerView) {
         mNativeAwGLFunctor = nativeCreate(this);
-        mNativeDrawGLFunctor = nativeDrawGLFunctorFactory.createFunctor(
+        mNativeDrawGLFunctor = nativeDrawFunctorFactory.createGLFunctor(
                 nativeGetAwDrawGLViewContext(mNativeAwGLFunctor));
         mContainerView = containerView;
         if (mNativeDrawGLFunctor.supportsDrawGLFunctorReleasedCallback()) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
index 314ebe7..b2701be 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -21,7 +21,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.DependencyFactory;
 import org.chromium.android_webview.AwContents.InternalAccessDelegate;
-import org.chromium.android_webview.AwContents.NativeDrawGLFunctorFactory;
+import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory;
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.test.util.GraphicsTestUtils;
@@ -360,7 +360,7 @@
         AwContents awContents = testDependencyFactory.createAwContents(mBrowserContext,
                 testContainerView, testContainerView.getContext(),
                 testContainerView.getInternalAccessDelegate(),
-                testContainerView.getNativeDrawGLFunctorFactory(), awContentsClient, awSettings,
+                testContainerView.getNativeDrawFunctorFactory(), awContentsClient, awSettings,
                 testDependencyFactory);
         testContainerView.initialize(awContents);
         return testContainerView;
@@ -608,11 +608,10 @@
 
         public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             return new AwContents(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
         }
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
index 4d604258..4d1380f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
@@ -570,8 +570,7 @@
         AwSettings contentSettings = mActivityTestRule.getAwSettingsOnUiThread(mAwContents);
         contentSettings.setJavaScriptEnabled(true);
         loadDataWithBaseUrlSync("", "text/html", false, baseUri, null);
-        // TODO(dcheng): https://crbug.com/896059 this should be fixed as "x-thread://".
-        Assert.assertEquals("\"null\"",
+        Assert.assertEquals("\"x-thread://\"",
                 mActivityTestRule.executeJavaScriptAndWaitForResult(
                         mAwContents, mContentsClient, "window.origin;"));
     }
@@ -588,8 +587,7 @@
     @Feature({"AndroidWebView"})
     // https://crbug.com/900528
     public void testXhrForCustomSchemeUrl() throws Throwable {
-        // TODO(dcheng): this should be fixed by https://crbug.com/900528.
-        Assert.assertFalse(verifyXhrForUrls("myscheme://mydomain/1", "myscheme://mydomain/2"));
+        Assert.assertTrue(verifyXhrForUrls("myscheme://mydomain/1", "myscheme://mydomain/2"));
     }
 
     /**
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index eba1882..7b871f7 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -27,7 +27,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.DependencyFactory;
 import org.chromium.android_webview.AwContents.InternalAccessDelegate;
-import org.chromium.android_webview.AwContents.NativeDrawGLFunctorFactory;
+import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory;
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwContentsStatics;
 import org.chromium.android_webview.AwSafeBrowsingConfigHelper;
@@ -233,11 +233,10 @@
 
         public MockAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             super(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
             mCanShowInterstitial = true;
             mCanShowBigInterstitial = true;
         }
@@ -300,11 +299,10 @@
         @Override
         public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             return new MockAwContents(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
         }
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java
index af2d2c3..bfaf711 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContents.java
@@ -45,10 +45,10 @@
 
     public TestAwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context,
             InternalAccessDelegate internalAccessAdapter,
-            NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory, AwContentsClient contentsClient,
+            NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
             AwSettings settings, DependencyFactory dependencyFactory) {
         super(browserContext, containerView, context, internalAccessAdapter,
-                nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
 
         mRenderProcessGoneHelper = new RenderProcessGoneHelper();
         mRenderProcessGoneObservers = new ArrayList<RenderProcessGoneObserver>();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java
index b78b3f23..d534e50 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/services/VisualStateCallbackTest.java
@@ -20,7 +20,7 @@
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.DependencyFactory;
 import org.chromium.android_webview.AwContents.InternalAccessDelegate;
-import org.chromium.android_webview.AwContents.NativeDrawGLFunctorFactory;
+import org.chromium.android_webview.AwContents.NativeDrawFunctorFactory;
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwRenderProcessGoneDetail;
 import org.chromium.android_webview.AwSettings;
@@ -78,11 +78,10 @@
         public VisualStateCallbackTestAwContents(AwBrowserContext browserContext,
                 ViewGroup containerView, Context context,
                 InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             super(browserContext, containerView, context, internalAccessAdapter,
-                    nativeDrawGLFunctorFactory, contentsClient, settings, dependencyFactory);
+                    nativeDrawFunctorFactory, contentsClient, settings, dependencyFactory);
             mVisualStateCallbackHelper = new VisualStateCallbackHelper();
         }
 
@@ -113,11 +112,10 @@
         @Override
         public AwContents createAwContents(AwBrowserContext browserContext, ViewGroup containerView,
                 Context context, InternalAccessDelegate internalAccessAdapter,
-                NativeDrawGLFunctorFactory nativeDrawGLFunctorFactory,
-                AwContentsClient contentsClient, AwSettings settings,
-                DependencyFactory dependencyFactory) {
+                NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient,
+                AwSettings settings, DependencyFactory dependencyFactory) {
             return new VisualStateCallbackTestAwContents(browserContext, containerView, context,
-                    internalAccessAdapter, nativeDrawGLFunctorFactory, contentsClient, settings,
+                    internalAccessAdapter, nativeDrawFunctorFactory, contentsClient, settings,
                     dependencyFactory);
         }
     }
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
index 653679e..0d97cef 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
@@ -219,7 +219,7 @@
 
         testContainerView.initialize(new AwContents(mBrowserContext, testContainerView,
                 testContainerView.getContext(), testContainerView.getInternalAccessDelegate(),
-                testContainerView.getNativeDrawGLFunctorFactory(), awContentsClient, awSettings));
+                testContainerView.getNativeDrawFunctorFactory(), awContentsClient, awSettings));
         testContainerView.getAwContents().getSettings().setJavaScriptEnabled(true);
         if (mDevToolsServer == null) {
             mDevToolsServer = new AwDevToolsServer();
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
index 8d79205c..39284c4 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwTestContainerView.java
@@ -277,8 +277,8 @@
         return mAwContents;
     }
 
-    public AwContents.NativeDrawGLFunctorFactory getNativeDrawGLFunctorFactory() {
-        return new NativeDrawGLFunctorFactory();
+    public AwContents.NativeDrawFunctorFactory getNativeDrawFunctorFactory() {
+        return new NativeDrawFunctorFactory();
     }
 
     public AwContents.InternalAccessDelegate getInternalAccessDelegate() {
@@ -436,9 +436,9 @@
         return mAwContents.performAccessibilityAction(action, arguments);
     }
 
-    private class NativeDrawGLFunctorFactory implements AwContents.NativeDrawGLFunctorFactory {
+    private class NativeDrawFunctorFactory implements AwContents.NativeDrawFunctorFactory {
         @Override
-        public NativeDrawGLFunctor createFunctor(long context) {
+        public NativeDrawGLFunctor createGLFunctor(long context) {
             return new NativeDrawGLFunctor(context);
         }
     }
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index 4bf33f9..14d8924 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -518,8 +518,8 @@
               focused_view());
 
     // Clean up
-    textfield->SetText(base::UTF8ToUTF16(""));
     textfield->RequestFocus();
+    textfield->SetText(base::UTF8ToUTF16(""));
   }
 
   AppListView* app_list_view() { return view_; }
diff --git a/ash/app_list/views/folder_header_view.cc b/ash/app_list/views/folder_header_view.cc
index 7665d4f..488b390 100644
--- a/ash/app_list/views/folder_header_view.cc
+++ b/ash/app_list/views/folder_header_view.cc
@@ -43,6 +43,7 @@
   ~FolderNameView() override = default;
 
   void OnFocus() override {
+    SetText(base::UTF8ToUTF16(folder_header_view_->folder_item_->name()));
     SelectAll(false);
     Textfield::OnFocus();
   }
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index 19ad49b..41fa4b35 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -28,6 +28,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "chromeos/chromeos_switches.h"
+#include "ui/base/ime/composition_text.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
@@ -361,7 +362,7 @@
 }
 
 void SearchBoxView::ProcessAutocomplete() {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
   SearchResult* const first_visible_result =
@@ -425,25 +426,14 @@
 }
 
 void SearchBoxView::AcceptAutocompleteText() {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
-  if (HasAutocompleteText())
-    ContentsChanged(search_box(), search_box()->text());
-}
-
-void SearchBoxView::AcceptOneCharInAutocompleteText() {
-  if (!is_app_list_search_autocomplete_enabled_)
-    return;
-
-  highlight_range_.set_start(highlight_range_.start() + 1);
-  highlight_range_.set_end(search_box()->text().length());
-  const base::string16 original_text = search_box()->text();
-  search_box()->SetText(
-      search_box()->text().substr(0, highlight_range_.start()));
-  ContentsChanged(search_box(), search_box()->text());
-  search_box()->SetText(original_text);
-  search_box()->SetSelectionRange(highlight_range_);
+  // Do not trigger another search here in case the user is left clicking to
+  // select existing autocomplete text. (This also matches omnibox behavior.)
+  DCHECK(HasAutocompleteText());
+  search_box()->ClearSelection();
+  ResetHighlightRange();
 }
 
 bool SearchBoxView::HasAutocompleteText() {
@@ -451,23 +441,28 @@
   // autocomplete or selected by the user. If the recorded autocomplete
   // |highlight_range_| matches the selection range, this text is suggested by
   // autocomplete.
-  return search_box()->GetSelectedRange() == highlight_range_ &&
+  return search_box()->GetSelectedRange().EqualsIgnoringDirection(
+             highlight_range_) &&
          highlight_range_.length() > 0;
 }
 
 void SearchBoxView::ClearAutocompleteText() {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
-  search_box()->SetText(
-      search_box()->text().substr(0, highlight_range_.start()));
+  // Avoid triggering subsequent query by temporarily setting controller to
+  // nullptr.
+  search_box()->set_controller(nullptr);
+  search_box()->ClearCompositionText();
+  search_box()->set_controller(this);
+  ResetHighlightRange();
 }
 
 void SearchBoxView::ContentsChanged(views::Textfield* sender,
                                     const base::string16& new_contents) {
   // Update autocomplete text highlight range to track user typed text.
-  if (is_app_list_search_autocomplete_enabled_)
-    highlight_range_.set_start(search_box()->text().length());
+  if (ShouldProcessAutocomplete())
+    ResetHighlightRange();
   search_box::SearchBoxViewBase::ContentsChanged(sender, new_contents);
   app_list_view_->SetStateFromSearchBoxView(
       IsSearchBoxTrimmedQueryEmpty(), true /*triggered_by_contents_change*/);
@@ -475,7 +470,7 @@
 
 void SearchBoxView::SetAutocompleteText(
     const base::string16& autocomplete_text) {
-  if (!is_app_list_search_autocomplete_enabled_)
+  if (!ShouldProcessAutocomplete())
     return;
 
   const base::string16& current_text = search_box()->text();
@@ -486,27 +481,37 @@
   if (autocomplete_text.length() == current_text.length())
     return;
 
-  const base::string16& user_typed_text =
-      current_text.substr(0, highlight_range_.start());
   const base::string16& highlighted_text =
       autocomplete_text.substr(highlight_range_.start());
-  search_box()->SetText(user_typed_text + highlighted_text);
+
+  // Don't set autocomplete text if the highlighted text is the same as before.
+  if (highlighted_text == search_box()->GetSelectedText())
+    return;
+
   highlight_range_.set_end(autocomplete_text.length());
-  search_box()->SelectRange(highlight_range_);
+  ui::CompositionText composition_text;
+  composition_text.text = highlighted_text;
+  composition_text.selection = gfx::Range(0, highlighted_text.length());
+
+  // Avoid triggering subsequent query by temporarily setting controller to
+  // nullptr.
+  search_box()->set_controller(nullptr);
+  search_box()->SetCompositionText(composition_text);
+  search_box()->set_controller(this);
 }
 
 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
                                    const ui::KeyEvent& key_event) {
   if (search_box()->HasFocus() && is_search_box_active() &&
-      !search_box()->text().empty() &&
-      is_app_list_search_autocomplete_enabled_) {
+      !search_box()->text().empty() && ShouldProcessAutocomplete()) {
     // If the search box has no text in it currently, autocomplete should not
     // work.
     last_key_pressed_ = key_event.key_code();
     if (key_event.type() == ui::ET_KEY_PRESSED &&
         key_event.key_code() != ui::VKEY_BACK) {
-      if (key_event.key_code() == ui::VKEY_TAB) {
+      if (key_event.key_code() == ui::VKEY_TAB && HasAutocompleteText()) {
         AcceptAutocompleteText();
+        return true;
       } else if ((key_event.key_code() == ui::VKEY_UP ||
                   key_event.key_code() == ui::VKEY_DOWN ||
                   key_event.key_code() == ui::VKEY_LEFT ||
@@ -514,17 +519,6 @@
                  HasAutocompleteText()) {
         ClearAutocompleteText();
         return true;
-      } else {
-        const base::string16 pending_text = search_box()->GetSelectedText();
-        // Hitting the next key in the autocompete suggestion continues
-        // autocomplete suggestion. If the selected range doesn't match the
-        // recorded highlight range, the selection should be overwritten.
-        if (!pending_text.empty() &&
-            key_event.GetCharacter() == pending_text[0] &&
-            pending_text.length() == highlight_range_.length()) {
-          AcceptOneCharInAutocompleteText();
-          return true;
-        }
       }
     }
   }
@@ -609,6 +603,20 @@
   }
 }
 
+bool SearchBoxView::ShouldProcessAutocomplete() {
+  // IME sets composition text while the user is typing, so avoid handle
+  // autocomplete in this case to avoid conflicts.
+  return is_app_list_search_autocomplete_enabled_ &&
+         !(search_box()->IsIMEComposing() && highlight_range_.is_empty());
+}
+
+void SearchBoxView::ResetHighlightRange() {
+  DCHECK(ShouldProcessAutocomplete());
+  const uint32_t text_length = search_box()->text().length();
+  highlight_range_.set_start(text_length);
+  highlight_range_.set_end(text_length);
+}
+
 void SearchBoxView::SetupAssistantButton() {
   if (search_model_ && !search_model_->search_box()->show_assistant_button()) {
     return;
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index 57695030..dfbafed 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -92,6 +92,10 @@
   }
   ContentsView* contents_view() { return contents_view_; }
 
+  void set_highlight_range_for_test(const gfx::Range& range) {
+    highlight_range_ = range;
+  }
+
  private:
   // Gets the wallpaper prominent colors.
   void GetWallpaperProminentColors(
@@ -105,9 +109,6 @@
   // Notifies SearchBoxViewDelegate that the autocomplete text is valid.
   void AcceptAutocompleteText();
 
-  // Accepts one character in the autocomplete text and fires query.
-  void AcceptOneCharInAutocompleteText();
-
   // Returns true if there is currently an autocomplete suggestion in
   // search_box().
   bool HasAutocompleteText();
@@ -136,6 +137,12 @@
   void SearchEngineChanged() override;
   void ShowAssistantChanged() override;
 
+  // Returns true if the event to trigger autocomplete should be handled.
+  bool ShouldProcessAutocomplete();
+
+  // Clear highlight range.
+  void ResetHighlightRange();
+
   // The range of highlighted text for autocomplete.
   gfx::Range highlight_range_;
 
diff --git a/ash/app_list/views/search_box_view_unittest.cc b/ash/app_list/views/search_box_view_unittest.cc
index 249b2e14..50826b06 100644
--- a/ash/app_list/views/search_box_view_unittest.cc
+++ b/ash/app_list/views/search_box_view_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "ui/base/ime/composition_text.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
 #include "ui/chromeos/search_box/search_box_view_delegate.h"
 #include "ui/events/base_event_utils.h"
@@ -307,22 +308,22 @@
   // expect only typed characters otherwise.
   void ExpectAutocompleteSuggestion(bool should_autocomplete) {
     if (should_autocomplete) {
-      // Search box autocomplete suggestion is accepted and reflected in Search
-      // Model.
-      EXPECT_EQ(view()->search_box()->text(),
+      // Search box autocomplete suggestion is accepted, but it should not
+      // trigger another query, thus it is not reflected in Search Model.
+      EXPECT_EQ(base::ASCIIToUTF16("hello world!"),
+                view()->search_box()->text());
+      EXPECT_EQ(base::ASCIIToUTF16("he"),
                 view_delegate()->GetSearchModel()->search_box()->text());
-      EXPECT_EQ(view()->search_box()->text(),
-                base::ASCIIToUTF16("hello world!"));
     } else {
       // Search box autocomplete suggestion is removed and is reflected in
       // SearchModel.
       EXPECT_EQ(view()->search_box()->text(),
                 view_delegate()->GetSearchModel()->search_box()->text());
-      EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("he"));
+      EXPECT_EQ(base::ASCIIToUTF16("he"), view()->search_box()->text());
       // ProcessAutocomplete should be a no-op.
       view()->ProcessAutocomplete();
       // The autocomplete suggestion should still not be present.
-      EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("he"));
+      EXPECT_EQ(base::ASCIIToUTF16("he"), view()->search_box()->text());
     }
   }
 
@@ -530,15 +531,16 @@
   KeyPress(ui::VKEY_H);
   KeyPress(ui::VKEY_E);
   view()->ProcessAutocomplete();
-  // Forward the next key in the autocomplete suggestion to HandleKeyEvent(). We
-  // use HandleKeyEvent() because KeyPress() will replace the existing
-  // highlighted text and add a repeat character.
-  ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_L, ui::EF_NONE);
-  static_cast<views::TextfieldController*>(view())->HandleKeyEvent(
-      view()->search_box(), event);
+
+  // After typing L, the highlighted text will be replaced by L.
+  KeyPress(ui::VKEY_L);
   base::string16 selected_text = view()->search_box()->GetSelectedText();
-  // The autocomplete text should be preserved after hitting the next key in the
-  // suggestion.
+  EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("hel"));
+  EXPECT_EQ(base::ASCIIToUTF16(""), selected_text);
+
+  // After handling autocomplete, the highlighted text will show again.
+  view()->ProcessAutocomplete();
+  selected_text = view()->search_box()->GetSelectedText();
   EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("hello world!"));
   EXPECT_EQ(base::ASCIIToUTF16("lo world!"), selected_text);
 }
@@ -567,5 +569,33 @@
                false);
 }
 
+// Tests that autocomplete is not handled if IME is using composition text.
+TEST_F(SearchBoxViewAutocompleteTest, SearchBoxAutocompletesNotHandledForIME) {
+  // Add a search result with a non-empty title field.
+  CreateSearchResult(ash::SearchResultDisplayType::kList, 1.0,
+                     base::ASCIIToUTF16("hello world!"), base::string16());
+
+  // Simulate uncomposited text. The autocomplete should be handled.
+  view()->search_box()->SetText(base::ASCIIToUTF16("he"));
+  view()->set_highlight_range_for_test(gfx::Range(2, 2));
+  view()->ProcessAutocomplete();
+
+  base::string16 selected_text = view()->search_box()->GetSelectedText();
+  EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("hello world!"));
+  EXPECT_EQ(base::ASCIIToUTF16("llo world!"), selected_text);
+  view()->search_box()->SetText(base::string16());
+
+  // Simulate IME composition text. The autocomplete should not be handled.
+  ui::CompositionText composition_text;
+  composition_text.text = base::ASCIIToUTF16("he");
+  view()->search_box()->SetCompositionText(composition_text);
+  view()->set_highlight_range_for_test(gfx::Range(2, 2));
+  view()->ProcessAutocomplete();
+
+  selected_text = view()->search_box()->GetSelectedText();
+  EXPECT_EQ(view()->search_box()->text(), base::ASCIIToUTF16("he"));
+  EXPECT_EQ(base::ASCIIToUTF16(""), selected_text);
+}
+
 }  // namespace test
 }  // namespace app_list
diff --git a/ash/assistant/ui/assistant_web_view.cc b/ash/assistant/ui/assistant_web_view.cc
index d8a09b4..bf7b63e 100644
--- a/ash/assistant/ui/assistant_web_view.cc
+++ b/ash/assistant/ui/assistant_web_view.cc
@@ -182,7 +182,7 @@
   contents_->AddObserver(this);
 
   // Navigate to the url associated with the received deep link.
-  contents_->Navigate(assistant::util::GetWebUrl(type).value());
+  contents_->Navigate(assistant::util::GetWebUrl(type, params).value());
 }
 
 void AssistantWebView::DidAutoResizeView(const gfx::Size& new_size) {
diff --git a/ash/assistant/util/deep_link_util.cc b/ash/assistant/util/deep_link_util.cc
index 9f7c648b..ad6d7220 100644
--- a/ash/assistant/util/deep_link_util.cc
+++ b/ash/assistant/util/deep_link_util.cc
@@ -23,6 +23,7 @@
 
 // Supported deep link param keys. These values must be kept in sync with the
 // server. See more details at go/cros-assistant-deeplink.
+constexpr char kIdParamKey[] = "id";
 constexpr char kQueryParamKey[] = "q";
 constexpr char kPageParamKey[] = "page";
 constexpr char kRelaunchParamKey[] = "relaunch";
@@ -90,6 +91,7 @@
     DeepLinkParam param) {
   // Map of supported deep link params to their keys.
   static const std::map<DeepLinkParam, std::string> kDeepLinkParamKeys = {
+      {DeepLinkParam::kId, kIdParamKey},
       {DeepLinkParam::kPage, kPageParamKey},
       {DeepLinkParam::kQuery, kQueryParamKey},
       {DeepLinkParam::kRelaunch, kRelaunchParamKey}};
@@ -145,6 +147,17 @@
   return GetDeepLinkType(url) != DeepLinkType::kUnsupported;
 }
 
+GURL GetAssistantRemindersUrl(const base::Optional<std::string>& id) {
+  // TODO(b/113357196): Make these URLs configurable for development purposes.
+  static constexpr char kAssistantRemindersWebUrl[] =
+      "https://assistant.google.com/reminders/mainview";
+  static constexpr char kAssistantRemindersByIdWebUrl[] =
+      "https://assistant.google.com/reminders/id/";
+  return (id && !id.value().empty())
+             ? CreateLocalizedGURL(kAssistantRemindersByIdWebUrl + id.value())
+             : CreateLocalizedGURL(kAssistantRemindersWebUrl);
+}
+
 GURL GetChromeSettingsUrl(const base::Optional<std::string>& page) {
   static constexpr char kChromeSettingsUrl[] = "chrome://settings/";
 
@@ -161,13 +174,13 @@
 }
 
 base::Optional<GURL> GetWebUrl(const GURL& deep_link) {
-  return GetWebUrl(GetDeepLinkType(deep_link));
+  return GetWebUrl(GetDeepLinkType(deep_link), GetDeepLinkParams(deep_link));
 }
 
-base::Optional<GURL> GetWebUrl(DeepLinkType type) {
+base::Optional<GURL> GetWebUrl(
+    DeepLinkType type,
+    const std::map<std::string, std::string>& params) {
   // TODO(b/113357196): Make these URLs configurable for development purposes.
-  static constexpr char kAssistantRemindersWebUrl[] =
-      "https://assistant.google.com/reminders/mainview";
   static constexpr char kAssistantSettingsWebUrl[] =
       "https://assistant.google.com/settings/mainpage";
 
@@ -176,7 +189,8 @@
 
   switch (type) {
     case DeepLinkType::kReminders:
-      return CreateLocalizedGURL(kAssistantRemindersWebUrl);
+      return GetAssistantRemindersUrl(
+          GetDeepLinkParam(params, DeepLinkParam::kId));
     case DeepLinkType::kSettings:
       return CreateLocalizedGURL(kAssistantSettingsWebUrl);
     case DeepLinkType::kUnsupported:
diff --git a/ash/assistant/util/deep_link_util.h b/ash/assistant/util/deep_link_util.h
index 30f4f50..4400724 100644
--- a/ash/assistant/util/deep_link_util.h
+++ b/ash/assistant/util/deep_link_util.h
@@ -33,6 +33,7 @@
 
 // Enumeration of deep link parameters.
 enum class DeepLinkParam {
+  kId,
   kPage,
   kQuery,
   kRelaunch,
@@ -72,6 +73,10 @@
 // Returns true if the specified |url| is a deep link, false otherwise.
 ASH_EXPORT bool IsDeepLinkUrl(const GURL& url);
 
+// Returns the URL for the specified Assistant reminder |id|. If id is absent,
+// the returned URL will be for top-level Assistant Reminders.
+ASH_EXPORT GURL GetAssistantRemindersUrl(const base::Optional<std::string>& id);
+
 // Returns the URL for the specified Chrome Settings |page|. If page is absent
 // or not allowed, the URL will be for top-level Chrome Settings.
 ASH_EXPORT GURL GetChromeSettingsUrl(const base::Optional<std::string>& page);
@@ -81,10 +86,12 @@
 // IsWebDeepLink(GURL) API.
 ASH_EXPORT base::Optional<GURL> GetWebUrl(const GURL& deep_link);
 
-// Returns the web URL for a deep link of the specified |type|. A return value
-// will only be present if the deep link type is a web deep link type as
-// identified by the IsWebDeepLinkType(DeepLinkType) API.
-ASH_EXPORT base::Optional<GURL> GetWebUrl(DeepLinkType type);
+// Returns the web URL for a deep link of the specified |type| with the given
+// |params|. A return value will only be present if the deep link type is a web
+// deep link type as identified by the IsWebDeepLinkType(DeepLinkType) API.
+ASH_EXPORT base::Optional<GURL> GetWebUrl(
+    DeepLinkType type,
+    const std::map<std::string, std::string>& params);
 
 // Returns true if the specified |deep_link| is a web deep link.
 ASH_EXPORT bool IsWebDeepLink(const GURL& deep_link);
diff --git a/ash/assistant/util/deep_link_util_unittest.cc b/ash/assistant/util/deep_link_util_unittest.cc
index e4cacbe..2d4e034 100644
--- a/ash/assistant/util/deep_link_util_unittest.cc
+++ b/ash/assistant/util/deep_link_util_unittest.cc
@@ -262,6 +262,22 @@
     ASSERT_EQ(test_case.second, IsDeepLinkUrl(GURL(test_case.first)));
 }
 
+TEST_F(DeepLinkUnitTest, GetAssistantRemindersUrl) {
+  const std::map<base::Optional<std::string>, std::string> test_cases = {
+      // OK: Absent/empty id.
+      {base::nullopt,
+       "https://assistant.google.com/reminders/mainview?hl=en-US"},
+      {base::Optional<std::string>(std::string()),
+       "https://assistant.google.com/reminders/mainview?hl=en-US"},
+
+      // OK: Specified id.
+      {base::Optional<std::string>("123456"),
+       "https://assistant.google.com/reminders/id/123456?hl=en-US"}};
+
+  for (const auto& test_case : test_cases)
+    ASSERT_EQ(test_case.second, GetAssistantRemindersUrl(test_case.first));
+}
+
 TEST_F(DeepLinkUnitTest, GetChromeSettingsUrl) {
   const std::map<base::Optional<std::string>, std::string> test_cases = {
       // OK: Absent/empty page.
@@ -293,8 +309,8 @@
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // OK: Parameterized deep links.
-      {"googleassistant://reminders?param=true",
-       GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
+      {"googleassistant://reminders?id=123456",
+       GURL("https://assistant.google.com/reminders/id/123456?hl=en-US")},
       {"googleassistant://settings?param=true",
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
@@ -329,28 +345,51 @@
 }
 
 TEST_F(DeepLinkUnitTest, GetWebUrlByType) {
-  const std::map<DeepLinkType, base::Optional<GURL>> test_cases = {
+  using DeepLinkParams = std::map<std::string, std::string>;
+  using TestCase = std::pair<DeepLinkType, DeepLinkParams>;
+
+  // Creates a test case with a single parameter.
+  auto CreateTestCaseWithParam =
+      [](DeepLinkType type,
+         base::Optional<std::pair<std::string, std::string>> param =
+             base::nullopt) {
+        DeepLinkParams params;
+        if (param)
+          params.insert(param.value());
+        return std::make_pair(type, params);
+      };
+
+  // Creates a test case with no parameters.
+  auto CreateTestCase = [&CreateTestCaseWithParam](DeepLinkType type) {
+    return CreateTestCaseWithParam(type);
+  };
+
+  const std::map<TestCase, base::Optional<GURL>> test_cases = {
       // OK: Supported web deep link types.
-      {DeepLinkType::kReminders,
+      {CreateTestCase(DeepLinkType::kReminders),
        GURL("https://assistant.google.com/reminders/mainview?hl=en-US")},
-      {DeepLinkType::kSettings,
+      {CreateTestCaseWithParam(DeepLinkType::kReminders,
+                               std::make_pair("id", "123456")),
+       GURL("https://assistant.google.com/reminders/id/123456?hl=en-US")},
+      {CreateTestCase(DeepLinkType::kSettings),
        GURL("https://assistant.google.com/settings/mainpage?hl=en-US")},
 
       // FAIL: Non-web deep link types.
-      {DeepLinkType::kChromeSettings, base::nullopt},
-      {DeepLinkType::kFeedback, base::nullopt},
-      {DeepLinkType::kOnboarding, base::nullopt},
-      {DeepLinkType::kQuery, base::nullopt},
-      {DeepLinkType::kScreenshot, base::nullopt},
-      {DeepLinkType::kTaskManager, base::nullopt},
-      {DeepLinkType::kWhatsOnMyScreen, base::nullopt},
+      {CreateTestCase(DeepLinkType::kChromeSettings), base::nullopt},
+      {CreateTestCase(DeepLinkType::kFeedback), base::nullopt},
+      {CreateTestCase(DeepLinkType::kOnboarding), base::nullopt},
+      {CreateTestCase(DeepLinkType::kQuery), base::nullopt},
+      {CreateTestCase(DeepLinkType::kScreenshot), base::nullopt},
+      {CreateTestCase(DeepLinkType::kTaskManager), base::nullopt},
+      {CreateTestCase(DeepLinkType::kWhatsOnMyScreen), base::nullopt},
 
       // FAIL: Unsupported deep link types.
-      {DeepLinkType::kUnsupported, base::nullopt}};
+      {CreateTestCase(DeepLinkType::kUnsupported), base::nullopt}};
 
   for (const auto& test_case : test_cases) {
     const base::Optional<GURL>& expected = test_case.second;
-    const base::Optional<GURL> actual = GetWebUrl(test_case.first);
+    const base::Optional<GURL> actual = GetWebUrl(
+        /*type=*/test_case.first.first, /*params=*/test_case.first.second);
 
     // Assert |has_value| equivalence.
     ASSERT_EQ(expected, actual);
diff --git a/ash/session/test_session_controller_client.cc b/ash/session/test_session_controller_client.cc
index 9660f3f..1e783be 100644
--- a/ash/session/test_session_controller_client.cc
+++ b/ash/session/test_session_controller_client.cc
@@ -184,6 +184,7 @@
 
 void TestSessionControllerClient::RequestSignOut() {
   Reset();
+  ++request_sign_out_count_;
 }
 
 void TestSessionControllerClient::SwitchActiveUser(
diff --git a/ash/session/test_session_controller_client.h b/ash/session/test_session_controller_client.h
index cc466c2..07cfb39 100644
--- a/ash/session/test_session_controller_client.h
+++ b/ash/session/test_session_controller_client.h
@@ -47,6 +47,8 @@
     use_lower_case_user_id_ = value;
   }
 
+  int request_sign_out_count() const { return request_sign_out_count_; }
+
   // Helpers to set SessionController state.
   void SetCanLockScreen(bool can_lock);
   void SetShouldLockScreenAutomatically(bool should_lock);
@@ -100,6 +102,7 @@
   mojom::SessionInfoPtr session_info_;
 
   bool use_lower_case_user_id_ = true;
+  int request_sign_out_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(TestSessionControllerClient);
 };
diff --git a/ash/system/locale/locale_detailed_view.cc b/ash/system/locale/locale_detailed_view.cc
index 2c2741f0..e646e80 100644
--- a/ash/system/locale/locale_detailed_view.cc
+++ b/ash/system/locale/locale_detailed_view.cc
@@ -4,14 +4,108 @@
 
 #include "ash/system/locale/locale_detailed_view.h"
 
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/model/system_tray_model.h"
-#include "ash/system/tray/hover_highlight_view.h"
+#include "ash/system/tray/actionable_view.h"
+#include "ash/system/tray/tray_popup_item_style.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "base/i18n/case_conversion.h"
 #include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
 #include "ui/views/controls/scroll_view.h"
+#include "ui/views/layout/fill_layout.h"
 
 namespace ash {
+
+namespace {
+
+// The item that corresponds to a single locale in the locale list. The language
+// portion of |iso_code| is shown at the beginning of the row, and
+// |display_name| is shown in the middle. A checkmark is shown in the end if
+// |checked| is true.
+class LocaleItem : public ActionableView {
+ public:
+  LocaleItem(tray::LocaleDetailedView* locale_detailed_view,
+             const std::string& iso_code,
+             const base::string16& display_name,
+             bool checked)
+      : ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS),
+        locale_detailed_view_(locale_detailed_view),
+        checked_(checked) {
+    SetInkDropMode(InkDropMode::ON);
+
+    TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
+    AddChildView(tri_view);
+    SetLayoutManager(std::make_unique<views::FillLayout>());
+
+    views::Label* iso_code_label = TrayPopupUtils::CreateDefaultLabel();
+    iso_code_label->SetEnabledColor(kUnifiedMenuTextColor);
+    iso_code_label->SetAutoColorReadabilityEnabled(false);
+    iso_code_label->SetText(base::i18n::ToUpper(
+        base::UTF8ToUTF16(l10n_util::GetLanguage(iso_code))));
+    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+    const gfx::FontList& base_font_list =
+        rb.GetFontList(ui::ResourceBundle::MediumBoldFont);
+    iso_code_label->SetFontList(base_font_list);
+    tri_view->AddView(TriView::Container::START, iso_code_label);
+
+    auto* display_name_view = TrayPopupUtils::CreateDefaultLabel();
+    display_name_view->SetText(display_name);
+    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
+                             true /* use_unified_theme */);
+    style.SetupLabel(display_name_view);
+
+    display_name_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    tri_view->AddView(TriView::Container::CENTER, display_name_view);
+
+    if (checked_) {
+      views::ImageView* checked_image = TrayPopupUtils::CreateMainImageView();
+      checked_image->SetImage(gfx::CreateVectorIcon(
+          kCheckCircleIcon, kMenuIconSize, gfx::kGoogleGreen700));
+      tri_view->AddView(TriView::Container::END, checked_image);
+    }
+    SetAccessibleName(display_name_view->text());
+  }
+
+  ~LocaleItem() override = default;
+
+  // ActionableView:
+  bool PerformAction(const ui::Event& event) override {
+    locale_detailed_view_->HandleViewClicked(this);
+    return true;
+  }
+
+  // views::View:
+  void OnFocus() override {
+    ActionableView::OnFocus();
+    ScrollViewToVisible();
+  }
+
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
+    ActionableView::GetAccessibleNodeData(node_data);
+    node_data->role = ax::mojom::Role::kCheckBox;
+    node_data->SetCheckedState(checked_ ? ax::mojom::CheckedState::kTrue
+                                        : ax::mojom::CheckedState::kFalse);
+  }
+
+ private:
+  tray::LocaleDetailedView* locale_detailed_view_;
+  const bool checked_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocaleItem);
+};
+
+}  // namespace
+
 namespace tray {
 
 LocaleDetailedView::LocaleDetailedView(DetailedViewDelegate* delegate)
@@ -32,16 +126,14 @@
     const bool checked =
         entry->iso_code ==
         Shell::Get()->system_tray_model()->current_locale_iso_code();
-    HoverHighlightView* item =
-        AddScrollListCheckableItem(entry->display_name, checked);
+    LocaleItem* item =
+        new LocaleItem(this, entry->iso_code, entry->display_name, checked);
+    scroll_content()->AddChildView(item);
     item->set_id(id);
     id_to_locale_[id] = entry->iso_code;
     ++id;
   }
-  scroll_content()->SizeToPreferredSize();
-  scroller()->Layout();
-  // TODO(wzang): Show locale ISO codes at the beginning of each row, most
-  // likely by reusing the code in |ImeListItemView|.
+  Layout();
 }
 
 void LocaleDetailedView::HandleViewClicked(views::View* view) {
diff --git a/ash/system/locale/locale_detailed_view.h b/ash/system/locale/locale_detailed_view.h
index b6115b33..49fb7751 100644
--- a/ash/system/locale/locale_detailed_view.h
+++ b/ash/system/locale/locale_detailed_view.h
@@ -20,12 +20,12 @@
   explicit LocaleDetailedView(DetailedViewDelegate* delegate);
   ~LocaleDetailedView() override;
 
- private:
-  void CreateItems();
-
   // TrayDetailedView:
   void HandleViewClicked(views::View* view) override;
 
+ private:
+  void CreateItems();
+
   // The map between the id of the view and the locale it corresponds to.
   base::flat_map<int, std::string> id_to_locale_;
 
diff --git a/ash/system/locale/locale_feature_pod_controller.cc b/ash/system/locale/locale_feature_pod_controller.cc
index 9e4331bf..4f4bb97 100644
--- a/ash/system/locale/locale_feature_pod_controller.cc
+++ b/ash/system/locale/locale_feature_pod_controller.cc
@@ -10,6 +10,7 @@
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
+#include "base/i18n/case_conversion.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -33,8 +34,9 @@
     button->SetLabel(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_LOCALE));
     button->ShowDetailedViewArrow();
     button->DisableLabelButtonFocus();
-    button->SetSubLabel(base::UTF8ToUTF16(
-        Shell::Get()->system_tray_model()->current_locale_iso_code()));
+    button->SetSubLabel(
+        base::i18n::ToUpper(base::UTF8ToUTF16(l10n_util::GetLanguage(
+            Shell::Get()->system_tray_model()->current_locale_iso_code()))));
   }
   return button;
 }
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index 0a5f5aad..5dbea29 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -485,6 +485,11 @@
       DeleteRemovedNotifications();
       UpdateBounds();
       start_height_ = ideal_height_;
+      for (int i = 0; i < child_count(); ++i) {
+        auto* view = GetContainer(i);
+        view->set_start_bounds(view->ideal_bounds());
+      }
+
       PreferredSizeChanged();
 
       state_ = State::CLEAR_ALL_STACKED;
diff --git a/ash/system/message_center/unified_message_list_view_unittest.cc b/ash/system/message_center/unified_message_list_view_unittest.cc
index edf5131..7b2950c6 100644
--- a/ash/system/message_center/unified_message_list_view_unittest.cc
+++ b/ash/system/message_center/unified_message_list_view_unittest.cc
@@ -443,8 +443,10 @@
 
   message_list_view()->set_stacked_notification_count(1);
   int previous_height = message_list_view()->GetPreferredSize().height();
+  gfx::Rect previous_bounds = GetMessageViewBounds(1);
   AnimateToMiddle();
   EXPECT_EQ(previous_height, message_list_view()->GetPreferredSize().height());
+  EXPECT_EQ(previous_bounds, GetMessageViewBounds(1));
   AnimateToEnd();
   EXPECT_EQ(1, message_list_view()->child_count());
 
@@ -455,7 +457,7 @@
   AnimateToEnd();
   EXPECT_EQ(1, message_list_view()->child_count());
 
-  gfx::Rect previous_bounds = GetMessageViewBounds(0);
+  previous_bounds = GetMessageViewBounds(0);
   AnimateToMiddle();
   EXPECT_LT(previous_bounds.x(), GetMessageViewBounds(0).x());
   AnimateToEnd();
diff --git a/ash/system/session/logout_button_tray.cc b/ash/system/session/logout_button_tray.cc
index eaca905..3f6fe02 100644
--- a/ash/system/session/logout_button_tray.cc
+++ b/ash/system/session/logout_button_tray.cc
@@ -17,6 +17,7 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
 #include "ash/system/user/login_status.h"
+#include "base/metrics/user_metrics.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -72,11 +73,14 @@
   DCHECK_EQ(button_, sender);
 
   if (dialog_duration_ <= base::TimeDelta()) {
+    if (Shell::Get()->session_controller()->IsDemoSession())
+      base::RecordAction(base::UserMetricsAction("DemoMode.ExitFromShelf"));
     // Sign out immediately if |dialog_duration_| is non-positive.
     Shell::Get()->session_controller()->RequestSignOut();
   } else if (Shell::Get()->logout_confirmation_controller()) {
     Shell::Get()->logout_confirmation_controller()->ConfirmLogout(
-        base::TimeTicks::Now() + dialog_duration_);
+        base::TimeTicks::Now() + dialog_duration_,
+        LogoutConfirmationController::Source::kShelfExitButton);
   }
 }
 
diff --git a/ash/system/session/logout_button_tray.h b/ash/system/session/logout_button_tray.h
index 56f9155..7601a50 100644
--- a/ash/system/session/logout_button_tray.h
+++ b/ash/system/session/logout_button_tray.h
@@ -48,6 +48,8 @@
   // SessionObserver:
   void OnActiveUserPrefServiceChanged(PrefService* prefs) override;
 
+  views::MdTextButton* button_for_test() const { return button_; }
+
  private:
   void UpdateShowLogoutButtonInTray();
   void UpdateLogoutDialogDuration();
diff --git a/ash/system/session/logout_button_tray_unittest.cc b/ash/system/session/logout_button_tray_unittest.cc
index d5fb2f5..5387399 100644
--- a/ash/system/session/logout_button_tray_unittest.cc
+++ b/ash/system/session/logout_button_tray_unittest.cc
@@ -9,12 +9,16 @@
 #include "ash/session/session_controller.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
+#include "ash/system/session/logout_confirmation_controller.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test_shell_delegate.h"
 #include "base/macros.h"
+#include "base/test/metrics/user_action_tester.h"
 #include "components/prefs/pref_service.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/views/controls/button/md_text_button.h"
 
 namespace ash {
 namespace {
@@ -70,5 +74,66 @@
   EXPECT_FALSE(button->visible());
 }
 
+TEST_F(LogoutButtonTrayTest, ButtonPressed) {
+  constexpr char kUserEmail[] = "user1@test.com";
+  constexpr char kUserAction[] = "DemoMode.ExitFromShelf";
+
+  LogoutButtonTray* const tray = Shell::GetPrimaryRootWindowController()
+                                     ->GetStatusAreaWidget()
+                                     ->logout_button_tray_for_testing();
+  ASSERT_TRUE(tray);
+  views::MdTextButton* const button = tray->button_for_test();
+  SessionController* const session_controller =
+      Shell::Get()->session_controller();
+  TestSessionControllerClient* const session_client =
+      GetSessionControllerClient();
+  base::UserActionTester user_action_tester;
+  const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), 0, 0);
+  PrefService* const pref_service =
+      Shell::Get()->session_controller()->GetUserPrefServiceForUser(
+          AccountId::FromUserEmail(kUserEmail));
+
+  SimulateUserLogin(kUserEmail);
+  EXPECT_EQ(0, session_client->request_sign_out_count());
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction));
+  EXPECT_EQ(0, Shell::Get()
+                   ->logout_confirmation_controller()
+                   ->confirm_logout_count_for_test());
+
+  // Sign out immediately when duration is zero.
+  pref_service->SetInteger(prefs::kLogoutDialogDurationMs, 0);
+  tray->ButtonPressed(button, event);
+  session_controller->FlushMojoForTest();
+  EXPECT_EQ(1, session_client->request_sign_out_count());
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction));
+  EXPECT_EQ(0, Shell::Get()
+                   ->logout_confirmation_controller()
+                   ->confirm_logout_count_for_test());
+
+  // Call |LogoutConfirmationController::ConfirmLogout| when duration is
+  // non-zero.
+  pref_service->SetInteger(prefs::kLogoutDialogDurationMs, 1000);
+  tray->ButtonPressed(button, event);
+  session_controller->FlushMojoForTest();
+  EXPECT_EQ(1, session_client->request_sign_out_count());
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kUserAction));
+  EXPECT_EQ(1, Shell::Get()
+                   ->logout_confirmation_controller()
+                   ->confirm_logout_count_for_test());
+
+  // Sign out immediately and record user action when duration is zero and it is
+  // demo session.
+  pref_service->SetInteger(prefs::kLogoutDialogDurationMs, 0);
+  session_client->SetIsDemoSession();
+  tray->ButtonPressed(button, event);
+  session_controller->FlushMojoForTest();
+  EXPECT_EQ(2, session_client->request_sign_out_count());
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kUserAction));
+  EXPECT_EQ(1, Shell::Get()
+                   ->logout_confirmation_controller()
+                   ->confirm_logout_count_for_test());
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/system/session/logout_confirmation_controller.cc b/ash/system/session/logout_confirmation_controller.cc
index dc9fa3f..9d7bde1 100644
--- a/ash/system/session/logout_confirmation_controller.cc
+++ b/ash/system/session/logout_confirmation_controller.cc
@@ -15,6 +15,7 @@
 #include "ash/system/session/logout_confirmation_dialog.h"
 #include "base/callback.h"
 #include "base/location.h"
+#include "base/metrics/user_metrics.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
 #include "ui/aura/window.h"
@@ -29,7 +30,11 @@
 const int kLastWindowClosedContainerIds[] = {
     kShellWindowId_DefaultContainer, kShellWindowId_AlwaysOnTopContainer};
 
-void SignOut() {
+void SignOut(LogoutConfirmationController::Source source) {
+  if (Shell::Get()->session_controller()->IsDemoSession() &&
+      source == LogoutConfirmationController::Source::kShelfExitButton) {
+    base::RecordAction(base::UserMetricsAction("DemoMode.ExitFromShelf"));
+  }
   Shell::Get()->session_controller()->RequestSignOut();
 }
 
@@ -94,7 +99,8 @@
     // No more windows except currently removing. Show logout time.
     Shell::Get()->logout_confirmation_controller()->ConfirmLogout(
         base::TimeTicks::Now() +
-        base::TimeDelta::FromSeconds(kLogoutConfirmationDelayInSeconds));
+            base::TimeDelta::FromSeconds(kLogoutConfirmationDelayInSeconds),
+        Source::kCloseAllWindows);
   }
 
   // ShellObserver:
@@ -120,7 +126,7 @@
 
 LogoutConfirmationController::LogoutConfirmationController()
     : clock_(base::DefaultTickClock::GetInstance()),
-      logout_closure_(base::Bind(&SignOut)) {
+      logout_callback_(base::BindRepeating(&SignOut)) {
   if (Shell::HasInstance())  // Null in testing::Test.
     Shell::Get()->session_controller()->AddObserver(this);
 }
@@ -133,7 +139,8 @@
     Shell::Get()->session_controller()->RemoveObserver(this);
 }
 
-void LogoutConfirmationController::ConfirmLogout(base::TimeTicks logout_time) {
+void LogoutConfirmationController::ConfirmLogout(base::TimeTicks logout_time,
+                                                 Source source) {
   if (!logout_time_.is_null() && logout_time >= logout_time_) {
     // If a confirmation dialog is already being shown and its countdown expires
     // no later than the |logout_time| requested now, keep the current dialog
@@ -150,8 +157,10 @@
     dialog_->Update(logout_time_);
   }
 
+  source_ = source;
   logout_timer_.Start(FROM_HERE, logout_time_ - clock_->NowTicks(),
-                      logout_closure_);
+                      base::BindOnce(logout_callback_, source));
+  ++confirm_logout_count_for_test_;
 }
 
 void LogoutConfirmationController::OnLoginStatusChanged(
@@ -178,7 +187,7 @@
 
 void LogoutConfirmationController::OnLogoutConfirmed() {
   logout_timer_.Stop();
-  logout_closure_.Run();
+  logout_callback_.Run(source_);
 }
 
 void LogoutConfirmationController::OnDialogClosed() {
@@ -192,9 +201,9 @@
   clock_ = clock;
 }
 
-void LogoutConfirmationController::SetLogoutClosureForTesting(
-    const base::Closure& logout_closure) {
-  logout_closure_ = logout_closure;
+void LogoutConfirmationController::SetLogoutCallbackForTesting(
+    const base::RepeatingCallback<void(Source)>& logout_callback) {
+  logout_callback_ = logout_callback;
 }
 
 }  // namespace ash
diff --git a/ash/system/session/logout_confirmation_controller.h b/ash/system/session/logout_confirmation_controller.h
index 27b78bd..b28570a2 100644
--- a/ash/system/session/logout_confirmation_controller.h
+++ b/ash/system/session/logout_confirmation_controller.h
@@ -34,6 +34,8 @@
 // closed.
 class ASH_EXPORT LogoutConfirmationController : public SessionObserver {
  public:
+  enum class Source { kShelfExitButton, kCloseAllWindows };
+
   LogoutConfirmationController();
   ~LogoutConfirmationController() override;
 
@@ -42,7 +44,7 @@
   // Shows a LogoutConfirmationDialog. If a confirmation dialog is already being
   // shown, it is closed and a new one opened if |logout_time| is earlier than
   // the current dialog's |logout_time_|.
-  void ConfirmLogout(base::TimeTicks logout_time);
+  void ConfirmLogout(base::TimeTicks logout_time, Source source);
 
   // SessionObserver:
   void OnLoginStatusChanged(LoginStatus login_status) override;
@@ -58,20 +60,29 @@
   // of the clock. |clock| must outlive the LogoutConfirmationController
   // instance.
   void SetClockForTesting(const base::TickClock* clock);
-  void SetLogoutClosureForTesting(const base::Closure& logout_closure);
+  void SetLogoutCallbackForTesting(
+      const base::RepeatingCallback<void(Source)>& logout_callback);
   LogoutConfirmationDialog* dialog_for_testing() const { return dialog_; }
 
+  int confirm_logout_count_for_test() const {
+    return confirm_logout_count_for_test_;
+  }
+
  private:
   class LastWindowClosedObserver;
   std::unique_ptr<LastWindowClosedObserver> last_window_closed_observer_;
 
   const base::TickClock* clock_;
-  base::Closure logout_closure_;
+
+  base::RepeatingCallback<void(Source)> logout_callback_;
+  Source source_;
 
   base::TimeTicks logout_time_;
   LogoutConfirmationDialog* dialog_ = nullptr;  // Owned by the Views hierarchy.
   base::OneShotTimer logout_timer_;
 
+  int confirm_logout_count_for_test_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(LogoutConfirmationController);
 };
 
diff --git a/ash/system/session/logout_confirmation_controller_unittest.cc b/ash/system/session/logout_confirmation_controller_unittest.cc
index 3943568..fe2bd540 100644
--- a/ash/system/session/logout_confirmation_controller_unittest.cc
+++ b/ash/system/session/logout_confirmation_controller_unittest.cc
@@ -26,7 +26,7 @@
   LogoutConfirmationControllerTest();
   ~LogoutConfirmationControllerTest() override;
 
-  void LogOut();
+  void LogOut(LogoutConfirmationController::Source source);
 
   bool log_out_called_;
 
@@ -44,20 +44,23 @@
       runner_(new base::TestMockTimeTaskRunner),
       runner_handle_(runner_) {
   controller_.SetClockForTesting(runner_->GetMockTickClock());
-  controller_.SetLogoutClosureForTesting(base::Bind(
+  controller_.SetLogoutCallbackForTesting(base::BindRepeating(
       &LogoutConfirmationControllerTest::LogOut, base::Unretained(this)));
 }
 
 LogoutConfirmationControllerTest::~LogoutConfirmationControllerTest() = default;
 
-void LogoutConfirmationControllerTest::LogOut() {
+void LogoutConfirmationControllerTest::LogOut(
+    LogoutConfirmationController::Source source) {
   log_out_called_ = true;
 }
 
 // Verifies that the user is logged out immediately if logout confirmation with
 // a zero-length countdown is requested.
 TEST_F(LogoutConfirmationControllerTest, ZeroDuration) {
-  controller_.ConfirmLogout(runner_->NowTicks());
+  controller_.ConfirmLogout(
+      runner_->NowTicks(),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   runner_->FastForwardBy(base::TimeDelta());
   EXPECT_TRUE(log_out_called_);
@@ -65,8 +68,9 @@
 
 // Verifies that the user is logged out when the countdown expires.
 TEST_F(LogoutConfirmationControllerTest, DurationExpired) {
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
   EXPECT_FALSE(log_out_called_);
@@ -78,13 +82,15 @@
 // request's countdown ends before the original request's, the user is logged
 // out when the new countdown expires.
 TEST_F(LogoutConfirmationControllerTest, DurationShortened) {
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(30));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(30),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
   EXPECT_FALSE(log_out_called_);
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
   EXPECT_FALSE(log_out_called_);
   runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
@@ -95,13 +101,15 @@
 // request's countdown ends after the original request's, the user is logged
 // out when the original countdown expires.
 TEST_F(LogoutConfirmationControllerTest, DurationExtended) {
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
   EXPECT_FALSE(log_out_called_);
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
   EXPECT_TRUE(log_out_called_);
 }
@@ -109,8 +117,9 @@
 // Verifies that when the screen is locked while the countdown is running, the
 // user is not logged out, even when the original countdown expires.
 TEST_F(LogoutConfirmationControllerTest, Lock) {
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   controller_.OnLockStateChanged(true);
   runner_->FastForwardUntilNoTasksRemain();
@@ -120,8 +129,9 @@
 // Verifies that when the user confirms the logout request, the user is logged
 // out immediately.
 TEST_F(LogoutConfirmationControllerTest, UserAccepted) {
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   controller_.OnLogoutConfirmed();
   EXPECT_TRUE(log_out_called_);
@@ -130,8 +140,9 @@
 // Verifies that when the user denies the logout request, the user is not logged
 // out, even when the original countdown expires.
 TEST_F(LogoutConfirmationControllerTest, UserDenied) {
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   controller_.OnDialogClosed();
   runner_->FastForwardUntilNoTasksRemain();
@@ -142,15 +153,17 @@
 // request is handled correctly and the user is logged out when the countdown
 // expires.
 TEST_F(LogoutConfirmationControllerTest, DurationExpiredAfterDeniedRequest) {
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   controller_.OnDialogClosed();
   runner_->FastForwardUntilNoTasksRemain();
   EXPECT_FALSE(log_out_called_);
 
-  controller_.ConfirmLogout(runner_->NowTicks() +
-                            base::TimeDelta::FromSeconds(10));
+  controller_.ConfirmLogout(
+      runner_->NowTicks() + base::TimeDelta::FromSeconds(10),
+      LogoutConfirmationController::Source::kShelfExitButton);
   EXPECT_FALSE(log_out_called_);
   runner_->FastForwardBy(base::TimeDelta::FromSeconds(9));
   EXPECT_FALSE(log_out_called_);
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index 1bb9a42..388538eb 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -119,6 +119,8 @@
 
 void UnifiedSystemTrayController::HandleSignOutAction() {
   Shell::Get()->metrics()->RecordUserMetricsAction(UMA_STATUS_AREA_SIGN_OUT);
+  if (Shell::Get()->session_controller()->IsDemoSession())
+    base::RecordAction(base::UserMetricsAction("DemoMode.ExitFromSystemTray"));
   Shell::Get()->session_controller()->RequestSignOut();
 }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 64f6fdbd..902e44c 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -100,16 +100,6 @@
   }
 }
 
-if (is_nacl_nonsfi) {
-  # Must be in a config because of how GN orders flags (otherwise -Wall will
-  # appear after this, and turn it back on).
-  config("nacl_nonsfi_warnings") {
-    # file_util_posix.cc contains a function which is not
-    # being used by nacl_helper_nonsfi.
-    cflags = [ "-Wno-unused-function" ]
-  }
-}
-
 if (is_android) {
   config("android_system_libs") {
     libs = [
@@ -132,14 +122,6 @@
 # test code (test support and anything in the test directory) which should use
 # source_set as is recommended for GN targets).
 jumbo_component("base") {
-  if (is_nacl_nonsfi) {
-    # TODO(phosek) bug 570839: If field_trial.cc is in a static library,
-    # nacl_helper_nonsfi doesn't link properly on Linux in debug builds. The
-    # reasons for this seem to involve obscure toolchain bugs. This should be
-    # fixed and this target should always be a static_library in the
-    # non-component case.
-    static_component_type = "source_set"
-  }
   if (is_nacl || is_ios) {
     # Link errors related to malloc functions if libbase for nacl is
     # compiled with jumbo: https://crbug.com/775959.
@@ -1553,7 +1535,6 @@
 
     if (is_nacl_nonsfi) {
       sources -= [ "rand_util_nacl.cc" ]
-      configs += [ ":nacl_nonsfi_warnings" ]
     } else {
       sources -= [
         "files/file_descriptor_watcher_posix.cc",
@@ -2073,6 +2054,7 @@
     "message_loop/message_loop_task_runner_perftest.cc",
     "message_loop/message_pump_perftest.cc",
     "observer_list_perftest.cc",
+    "strings/string_util_perftest.cc",
     "task/sequence_manager/sequence_manager_perftest.cc",
     "task/task_scheduler/task_scheduler_perftest.cc",
 
diff --git a/base/files/file_util_posix.cc b/base/files/file_util_posix.cc
index 94e80c5..eabdaf3 100644
--- a/base/files/file_util_posix.cc
+++ b/base/files/file_util_posix.cc
@@ -74,10 +74,12 @@
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
   return stat(path, sb);
 }
+#if !defined(OS_NACL_NONSFI)
 int CallLstat(const char* path, stat_wrapper_t* sb) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
   return lstat(path, sb);
 }
+#endif
 #else
 int CallStat(const char* path, stat_wrapper_t* sb) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
diff --git a/base/pickle_fuzzer.cc b/base/pickle_fuzzer.cc
index 26c5dbf..74abaadb 100644
--- a/base/pickle_fuzzer.cc
+++ b/base/pickle_fuzzer.cc
@@ -102,7 +102,8 @@
       }
       case 14: {
         const char* data_result = nullptr;
-        int read_length = data_provider.ConsumeInt32InRange(0, kMaxReadLength);
+        int read_length =
+            data_provider.ConsumeIntegralInRange(0, kMaxReadLength);
         ignore_result(iter.ReadBytes(&data_result, read_length));
         break;
       }
@@ -113,7 +114,7 @@
       }
       case 16: {
         ignore_result(iter.SkipBytes(
-            data_provider.ConsumeInt32InRange(0, kMaxSkipBytes)));
+            data_provider.ConsumeIntegralInRange(0, kMaxSkipBytes)));
         break;
       }
     }
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index f0c2197..5e2d0ee 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -68,37 +68,32 @@
 
 // Assuming that a pointer is the size of a "machine word", then
 // uintptr_t is an integer type that is also a machine word.
-typedef uintptr_t MachineWord;
-const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
+using MachineWord = uintptr_t;
 
-inline bool IsAlignedToMachineWord(const void* pointer) {
-  return !(reinterpret_cast<MachineWord>(pointer) & kMachineWordAlignmentMask);
+inline bool IsMachineWordAligned(const void* pointer) {
+  return !(reinterpret_cast<MachineWord>(pointer) & (sizeof(MachineWord) - 1));
 }
 
-template<typename T> inline T* AlignToMachineWord(T* pointer) {
-  return reinterpret_cast<T*>(reinterpret_cast<MachineWord>(pointer) &
-                              ~kMachineWordAlignmentMask);
-}
-
-template<size_t size, typename CharacterType> struct NonASCIIMask;
-template<> struct NonASCIIMask<4, char16> {
-    static inline uint32_t value() { return 0xFF80FF80U; }
+template <typename CharacterType>
+struct NonASCIIMask;
+template <>
+struct NonASCIIMask<char> {
+  static constexpr MachineWord value() {
+    return static_cast<MachineWord>(0x8080808080808080ULL);
+  }
 };
-template<> struct NonASCIIMask<4, char> {
-    static inline uint32_t value() { return 0x80808080U; }
-};
-template<> struct NonASCIIMask<8, char16> {
-    static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; }
-};
-template<> struct NonASCIIMask<8, char> {
-    static inline uint64_t value() { return 0x8080808080808080ULL; }
+template <>
+struct NonASCIIMask<char16> {
+  static constexpr MachineWord value() {
+    return static_cast<MachineWord>(0xFF80FF80FF80FF80ULL);
+  }
 };
 #if defined(WCHAR_T_IS_UTF32)
-template<> struct NonASCIIMask<4, wchar_t> {
-    static inline uint32_t value() { return 0xFFFFFF80U; }
-};
-template<> struct NonASCIIMask<8, wchar_t> {
-    static inline uint64_t value() { return 0xFFFFFF80FFFFFF80ULL; }
+template <>
+struct NonASCIIMask<wchar_t> {
+  static constexpr MachineWord value() {
+    return static_cast<MachineWord>(0xFFFFFF80FFFFFF80ULL);
+  }
 };
 #endif  // WCHAR_T_IS_UTF32
 
@@ -458,31 +453,42 @@
 
 template <class Char>
 inline bool DoIsStringASCII(const Char* characters, size_t length) {
+  if (!length)
+    return true;
+  constexpr MachineWord non_ascii_bit_mask = NonASCIIMask<Char>::value();
   MachineWord all_char_bits = 0;
   const Char* end = characters + length;
 
   // Prologue: align the input.
-  while (!IsAlignedToMachineWord(characters) && characters != end) {
-    all_char_bits |= *characters;
-    ++characters;
-  }
+  while (!IsMachineWordAligned(characters) && characters < end)
+    all_char_bits |= *characters++;
+  if (all_char_bits & non_ascii_bit_mask)
+    return false;
 
   // Compare the values of CPU word size.
-  const Char* word_end = AlignToMachineWord(end);
-  const size_t loop_increment = sizeof(MachineWord) / sizeof(Char);
-  while (characters < word_end) {
+  constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char);
+  constexpr int batch_count = 16;
+  while (characters <= end - batch_count * chars_per_word) {
+    all_char_bits = 0;
+    for (int i = 0; i < batch_count; ++i) {
+      all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
+      characters += chars_per_word;
+    }
+    if (all_char_bits & non_ascii_bit_mask)
+      return false;
+  }
+
+  // Process the remaining words.
+  all_char_bits = 0;
+  while (characters <= end - chars_per_word) {
     all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
-    characters += loop_increment;
+    characters += chars_per_word;
   }
 
   // Process the remaining bytes.
-  while (characters != end) {
-    all_char_bits |= *characters;
-    ++characters;
-  }
+  while (characters < end)
+    all_char_bits |= *characters++;
 
-  MachineWord non_ascii_bit_mask =
-      NonASCIIMask<sizeof(MachineWord), Char>::value();
   return !(all_char_bits & non_ascii_bit_mask);
 }
 
diff --git a/base/strings/string_util_perftest.cc b/base/strings/string_util_perftest.cc
new file mode 100644
index 0000000..17cdb05
--- /dev/null
+++ b/base/strings/string_util_perftest.cc
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_util.h"
+
+#include <cinttypes>
+
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+template <typename String>
+void MeasureIsStringASCII(size_t str_length, size_t non_ascii_pos) {
+  String str(str_length, 'A');
+  if (non_ascii_pos < str_length)
+    str[non_ascii_pos] = '\xAF';
+
+  TimeTicks t0 = TimeTicks::Now();
+  for (size_t i = 0; i < 10000000; ++i)
+    IsStringASCII(str);
+  TimeDelta time = TimeTicks::Now() - t0;
+  printf(
+      "char-size:\t%zu\tlength:\t%zu\tnon-ascii-pos:\t%zu\ttime-ms:\t%" PRIu64
+      "\n",
+      sizeof(typename String::value_type), str_length, non_ascii_pos,
+      time.InMilliseconds());
+}
+
+TEST(StringUtilTest, DISABLED_IsStringASCIIPerf) {
+  for (size_t str_length = 4; str_length <= 1024; str_length *= 2) {
+    for (size_t non_ascii_loc = 0; non_ascii_loc < 3; ++non_ascii_loc) {
+      size_t non_ascii_pos = str_length * non_ascii_loc / 2 + 2;
+      MeasureIsStringASCII<std::string>(str_length, non_ascii_pos);
+      MeasureIsStringASCII<string16>(str_length, non_ascii_pos);
+#if defined(WCHAR_T_IS_UTF32)
+      MeasureIsStringASCII<std::basic_string<wchar_t>>(str_length,
+                                                       non_ascii_pos);
+#endif
+    }
+  }
+}
+
+}  // namespace base
diff --git a/base/test/fuzzed_data_provider.h b/base/test/fuzzed_data_provider.h
index b847a2d9..0e3da80 100644
--- a/base/test/fuzzed_data_provider.h
+++ b/base/test/fuzzed_data_provider.h
@@ -151,24 +151,11 @@
     return ConsumeBytesAsString(remaining_bytes_);
   }
 
-  // TODO(mmoroz): consider deprecating these methods.
-  uint32_t ConsumeUint32InRange(uint32_t min, uint32_t max) {
-    return ConsumeIntegralInRange(min, max);
-  }
-
-  int32_t ConsumeInt32InRange(int32_t min, int32_t max) {
-    return ConsumeIntegralInRange(min, max);
-  }
-
-  int ConsumeIntInRange(int min, int max) {
-    return ConsumeIntegralInRange(min, max);
-  }
-
   // Reads one byte and returns a bool, or false when no data remains.
   bool ConsumeBool() { return 1 & ConsumeUint8(); }
 
   // Returns a uint8_t from the input or 0 if nothing remains. This is
-  // equivalent to ConsumeUint32InRange(0, 0xFF).
+  // equivalent to ConsumeIntegralInRange<uint8_t>(0, 0xFF).
   uint8_t ConsumeUint8() {
     return ConsumeIntegralInRange(std::numeric_limits<uint8_t>::min(),
                                   std::numeric_limits<uint8_t>::max());
@@ -176,16 +163,32 @@
 
   // Returns a uint16_t from the input. If fewer than 2 bytes of data remain
   // will fill the most significant bytes with 0. This is equivalent to
-  // ConsumeUint32InRange(0, 0xFFFF).
+  // ConsumeIntegralInRange<uint16_t>(0, 0xFFFF).
   uint16_t ConsumeUint16() {
     return ConsumeIntegralInRange(std::numeric_limits<uint16_t>::min(),
                                   std::numeric_limits<uint16_t>::max());
   }
 
+  // Returns a uint32_t from the input. If fewer than 4 bytes of data remain
+  // will fill the most significant bytes with 0. This is equivalent to
+  // ConsumeIntegralInRange<uint32_t>(0, 0xFFFFFFFF).
+  uint16_t ConsumeUint32() {
+    return ConsumeIntegralInRange(std::numeric_limits<uint32_t>::min(),
+                                  std::numeric_limits<uint32_t>::max());
+  }
+
+  // Returns a uint64_t from the input. If fewer than 8 bytes of data remain
+  // will fill the most significant bytes with 0. This is equivalent to
+  // ConsumeIntegralInRange<uint64_t>(0, 0xFFFFFFFFFFFFFFFF).
+  uint16_t ConsumeUint64() {
+    return ConsumeIntegralInRange(std::numeric_limits<uint64_t>::min(),
+                                  std::numeric_limits<uint64_t>::max());
+  }
+
   // Returns a value from |array|, consuming as many bytes as needed to do so.
   // |array| must be a fixed-size array.
-  template <typename Type, size_t size>
-  Type PickValueInArray(Type (&array)[size]) {
+  template <typename T, size_t size>
+  T PickValueInArray(T (&array)[size]) {
     return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
   }
 
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 65db622..06c3238 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -40,6 +40,12 @@
       const viz::BeginFrameArgs& args) = 0;
   virtual DrawResult ScheduledActionDrawIfPossible() = 0;
   virtual DrawResult ScheduledActionDrawForced() = 0;
+
+  // The Commit step occurs when the client received the BeginFrame from the
+  // source and we perform at most one commit per BeginFrame. In this step the
+  // main thread collects all updates then blocks and gives control to the
+  // compositor thread, which allows Compositor thread to update its layer tree
+  // to match the state of the layer tree on the main thread.
   virtual void ScheduledActionCommit() = 0;
   virtual void ScheduledActionActivateSyncTree() = 0;
   virtual void ScheduledActionBeginLayerTreeFrameSinkCreation() = 0;
@@ -90,10 +96,19 @@
   void SetVisible(bool visible);
   bool visible() { return state_machine_.visible(); }
   void SetCanDraw(bool can_draw);
+
+  // We have 2 copies of the layer trees on the compositor thread: pending_tree
+  // and active_tree. When we finish asynchronously rastering all tiles on
+  // pending_tree, call this method to notify that this pending tree is ready to
+  // be activated, that is to be copied to the active tree.
   void NotifyReadyToActivate();
   void NotifyReadyToDraw();
   void SetBeginFrameSource(viz::BeginFrameSource* source);
 
+  // Set |needs_begin_main_frame_| to true, which will cause the BeginFrame
+  // source to be told to send BeginFrames to this client so that this client
+  // can send a CompositorFrame to the display compositor with appropriate
+  // timing.
   void SetNeedsBeginMainFrame();
   // Requests a single impl frame (after the current frame if there is one
   // active).
@@ -125,12 +140,24 @@
   void SetTreePrioritiesAndScrollState(TreePriority tree_priority,
                                        ScrollHandlerState scroll_handler_state);
 
+  // Commit step happens after the main thread has completed updating for a
+  // BeginMainFrame request from the compositor, and blocks the main thread
+  // to copy the layer tree to the compositor thread. Call this method when the
+  // main thread updates are completed to signal it is ready for the commmit.
   void NotifyReadyToCommit();
   void BeginMainFrameAborted(CommitEarlyOutReason reason);
   void DidCommit();
 
+  // In the PrepareTiles step, compositor thread divides the layers into tiles
+  // to reduce cost of raster large layers. Then, each tile is rastered by a
+  // dedicated thread.
+  // |WillPrepareTiles| is called before PrepareTiles step to have the scheduler
+  // track when PrepareTiles starts.
   void WillPrepareTiles();
+  // |DidPrepareTiles| is called after PrepareTiles step to have the scheduler
+  // track how long PrepareTiles takes.
   void DidPrepareTiles();
+
   void DidLoseLayerTreeFrameSink();
   void DidCreateAndInitializeLayerTreeFrameSink();
 
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index 87fabafd..ac58ccf 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -139,6 +139,9 @@
   ~TileManager() override;
 
   // Assigns tile memory and schedules work to prepare tiles for drawing.
+  // This step occurs after Commit and at most once per BeginFrame. It can be
+  // called on its own, that is, outside of Commit.
+  //
   // - Runs client_->NotifyReadyToActivate() when all tiles required for
   // activation are prepared, or failed to prepare due to OOM.
   // - Runs client_->NotifyReadyToDraw() when all tiles required draw are
diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h
index 703d503e..b9bb0318 100644
--- a/cc/trees/proxy.h
+++ b/cc/trees/proxy.h
@@ -36,6 +36,9 @@
   virtual ~Proxy() {}
 
   virtual bool IsStarted() const = 0;
+
+  // This function retruns true if the commits go directly to active tree by
+  // skipping commit to pending tree.
   virtual bool CommitToActiveTree() const = 0;
 
   virtual void SetLayerTreeFrameSink(
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d2f7ca86..8ea43b1 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -423,7 +423,6 @@
 
 java_cpp_enum("chrome_android_java_enums_srcjar") {
   sources = [
-    "//chrome/browser/android/crash/crash_keys_android.h",
     "//chrome/browser/android/customtabs/detached_resource_request.h",
     "//chrome/browser/android/digital_asset_links/digital_asset_links_handler.h",
     "//chrome/browser/android/explore_sites/explore_sites_feature.h",
@@ -646,6 +645,7 @@
     "//components/background_task_scheduler:background_task_scheduler_javatests",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/bookmarks/common/android:bookmarks_java",
+    "//components/crash/android:java",
     "//components/crash/android:javatests",
     "//components/dom_distiller/core/android:dom_distiller_core_java",
     "//components/download/internal/background_service:internal_java",
@@ -1099,6 +1099,7 @@
     "//base/test:test_support",
     "//chrome:chrome_android_core",
     "//chrome/browser/android/metrics:ukm_utils_for_test",
+    "//components/crash/android:crash_android",
     "//components/heap_profiling:test_support",
     "//components/minidump_uploader",
     "//components/sync",
diff --git a/chrome/android/java/res/layout/bottom_toolbar_browsing.xml b/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
index 70e4b116..432e7d9 100644
--- a/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
+++ b/chrome/android/java/res/layout/bottom_toolbar_browsing.xml
@@ -37,7 +37,7 @@
             android:paddingStart="@dimen/bottom_toolbar_start_padding"
             android:paddingEnd="@dimen/bottom_toolbar_end_padding" >
 
-            <org.chromium.chrome.browser.toolbar.bottom.HomeButton
+            <org.chromium.chrome.browser.toolbar.HomeButton
                 android:id="@+id/home_button"
                 style="@style/BottomToolbarButton"
                 android:contentDescription="@string/accessibility_toolbar_btn_home" />
diff --git a/chrome/android/java/res/layout/toolbar_phone.xml b/chrome/android/java/res/layout/toolbar_phone.xml
index 104b864..f85c0c6d 100644
--- a/chrome/android/java/res/layout/toolbar_phone.xml
+++ b/chrome/android/java/res/layout/toolbar_phone.xml
@@ -23,7 +23,7 @@
         android:visibility="invisible"
         android:contentDescription="@string/accessibility_toolbar_btn_new_tab" />
 
-    <org.chromium.chrome.browser.toolbar.HomePageButton
+    <org.chromium.chrome.browser.toolbar.HomeButton
         android:id="@+id/home_button"
         style="@style/ToolbarButton"
         android:src="@drawable/btn_toolbar_home"
diff --git a/chrome/android/java/res/layout/toolbar_tablet.xml b/chrome/android/java/res/layout/toolbar_tablet.xml
index 6567426..87c5d19 100644
--- a/chrome/android/java/res/layout/toolbar_tablet.xml
+++ b/chrome/android/java/res/layout/toolbar_tablet.xml
@@ -22,7 +22,7 @@
         android:layout_height="match_parent"
         android:orientation="horizontal" >
 
-        <org.chromium.chrome.browser.toolbar.HomePageButton
+        <org.chromium.chrome.browser.toolbar.HomeButton
             android:id="@+id/home_button"
             style="@style/ToolbarButton"
             android:src="@drawable/btn_toolbar_home"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index a8f1f27..5cbfd62 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -40,15 +40,11 @@
 
         AutofillAssistantUiController controller =
                 new AutofillAssistantUiController(activity, parameters);
-        AutofillAssistantUiDelegate uiDelegate =
-                new AutofillAssistantUiDelegate(activity, controller);
-        UiDelegateHolder delegateHolder = new UiDelegateHolder(controller, uiDelegate);
-        controller.setUiDelegateHolder(delegateHolder);
+        UiDelegateHolder delegateHolder = new UiDelegateHolder(
+                controller, new AutofillAssistantUiDelegate(activity, controller));
         initTabObservers(activity, delegateHolder);
 
-        controller.maybeUpdateDetails(Details.makeFromParameters(parameters));
-
-        uiDelegate.startOrSkipInitScreen();
+        controller.start(delegateHolder, Details.makeFromParameters(parameters));
     }
 
     private static void initTabObservers(ChromeActivity activity, UiDelegateHolder delegateHolder) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index d219e2d..f382071ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -89,13 +89,17 @@
                 parameters.get(PARAMETER_USER_EMAIL), activity.getInitialIntent().getExtras());
     }
 
-    void setUiDelegateHolder(UiDelegateHolder uiDelegateHolder) {
+    void start(UiDelegateHolder uiDelegateHolder, Details details) {
         mUiDelegateHolder = uiDelegateHolder;
+        // Do not show details until 'onInitOk'.
+        mCurrentDetails = details;
+        mUiDelegateHolder.startOrSkipInitScreen();
     }
 
     @Override
     public void onInitOk() {
         assert mUiDelegateHolder != null;
+        mUiDelegateHolder.performUiOperation(uiDelegate -> uiDelegate.showDetails(mCurrentDetails));
         nativeStart(mUiControllerAndroid, mInitialUrl);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
index cabdfc0..0683980 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
@@ -315,6 +315,7 @@
     public void showStatusMessage(@Nullable String message) {
         show();
         mStatusMessageView.setText(message);
+        mStatusMessageView.announceForAccessibility(message);
     }
 
     /**
@@ -435,6 +436,10 @@
                     mActivity.getActivityTab().getWebContents());
             mFullContainer.setVisibility(View.VISIBLE);
 
+            // Announce Autofill Assistant is available for accessibility.
+            mBottomBar.announceForAccessibility(
+                    mActivity.getString(R.string.autofill_assistant_available_accessibility));
+
             // Set the initial progress. It is OK to make multiple calls to this method as it will
             // have an effect only on the first one.
             mProgressBar.maybeIncreaseProgress(PROGRESS_BAR_INITIAL_PROGRESS);
@@ -775,6 +780,8 @@
         View initView = LayoutInflater.from(mActivity)
                                 .inflate(R.layout.init_screen, mCoordinatorView)
                                 .findViewById(R.id.init_screen);
+        // Set focusable for accessibility.
+        initView.findViewById(R.id.init).setFocusable(true);
 
         // Set default state to checked.
         ((CheckBox) initView.findViewById(R.id.checkbox_dont_show_init_again)).setChecked(true);
@@ -782,6 +789,8 @@
                 .setOnClickListener(unusedView -> onInitClicked(true, initView));
         initView.findViewById(R.id.button_init_not_ok)
                 .setOnClickListener(unusedView -> onInitClicked(false, initView));
+        initView.announceForAccessibility(
+                mActivity.getString(R.string.autofill_assistant_first_run_accessibility));
     }
 
     private void onInitClicked(boolean accept, View initView) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/Details.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/Details.java
index 2b0062e..f678f27 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/Details.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/Details.java
@@ -4,10 +4,13 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 
 import org.json.JSONObject;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Collections;
@@ -22,7 +25,17 @@
  * Java side equivalent of autofill_assistant::DetailsProto.
  */
 class Details {
-    enum DetailsField { TITLE, URL, DATE, DESCRIPTION, MID, IS_FINAL }
+    @IntDef({DetailsField.TITLE, DetailsField.URL, DetailsField.DATE, DetailsField.DESCRIPTION,
+            DetailsField.MID, DetailsField.IS_FINAL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DetailsField {
+        int TITLE = 0;
+        int URL = 1;
+        int DATE = 2;
+        int DESCRIPTION = 3;
+        int MID = 4;
+        int IS_FINAL = 5;
+    }
 
     private final String mTitle;
     private final String mUrl;
@@ -32,7 +45,7 @@
     private final String mMid;
     private final boolean mIsFinal;
     /** Contains the fields that have changed when merging with other Details object. */
-    private final Set<DetailsField> mFieldsChanged;
+    private final Set<Integer> mFieldsChanged;
     // NOTE: When adding a new field, update the isEmpty and toJSONObject methods.
 
     static final Details EMPTY_DETAILS =
@@ -41,7 +54,7 @@
     private static final String RFC_3339_FORMAT_WITHOUT_TIMEZONE = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
 
     Details(String title, String url, @Nullable Date date, String description, String mId,
-            boolean isFinal, Set<DetailsField> fieldsChanged) {
+            boolean isFinal, Set<Integer> fieldsChanged) {
         this.mTitle = title;
         this.mUrl = url;
         this.mDate = date;
@@ -91,7 +104,7 @@
         return mIsFinal;
     }
 
-    Set<DetailsField> getFieldsChanged() {
+    Set<Integer> getFieldsChanged() {
         return mFieldsChanged;
     }
 
@@ -154,7 +167,7 @@
                     Collections.emptySet());
         }
 
-        Set<DetailsField> changedFields = new HashSet<>();
+        Set<Integer> changedFields = new HashSet<>();
 
         String title = oldDetails.getTitle();
         String mId = oldDetails.getMid();
@@ -186,4 +199,4 @@
         boolean isFinal = newDetails.isFinal();
         return new Details(title, url, date, description, mId, isFinal, changedFields);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java
index 37bfea44..4ce86ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/UiDelegateHolder.java
@@ -36,6 +36,13 @@
     }
 
     /**
+     * Starts the init screen unless it has been marked to be skipped.
+     */
+    public void startOrSkipInitScreen() {
+        mUiDelegate.startOrSkipInitScreen();
+    }
+
+    /**
      * Perform a UI operation:
      *  - directly if we are not in a pause state.
      *  - later if the shutdown is cancelled.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/ApplicationStatusTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/ApplicationStatusTracker.java
index 29ea88a..00b5fdc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/ApplicationStatusTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/ApplicationStatusTracker.java
@@ -7,6 +7,8 @@
 import org.chromium.base.ApplicationState;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ThreadUtils;
+import org.chromium.components.crash.CrashKeyIndex;
+import org.chromium.components.crash.CrashKeys;
 
 /**
  * This class updates crash keys when the application state changes.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java
index 5cac3e6..75f710d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.components.crash.CrashKeys;
 
 /**
  * This UncaughtExceptionHandler will upload the stacktrace when there is an uncaught exception.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java
index 87b0e07a..66a0a9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporter.java
@@ -20,6 +20,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.MainDex;
 import org.chromium.chrome.browser.ChromeVersionInfo;
+import org.chromium.components.crash.CrashKeys;
 
 import java.io.File;
 import java.io.FileNotFoundException;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
index b993b40..285e821 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
@@ -22,9 +22,9 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.crash.CrashKeyIndex;
-import org.chromium.chrome.browser.crash.CrashKeys;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics.DestructionReason;
+import org.chromium.components.crash.CrashKeyIndex;
+import org.chromium.components.crash.CrashKeys;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteChooserDialogManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteChooserDialogManager.java
index 9d6f7d0..25d826d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteChooserDialogManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/MediaRouteChooserDialogManager.java
@@ -13,6 +13,11 @@
 import android.support.v7.app.MediaRouteChooserDialog;
 import android.support.v7.app.MediaRouteChooserDialogFragment;
 import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
+import android.support.v7.mediarouter.R;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
 
 /**
  * Manages the dialog responsible for selecting a {@link MediaSink}.
@@ -34,7 +39,7 @@
         private final Handler mHandler = new Handler();
         private final SystemVisibilitySaver mVisibilitySaver = new SystemVisibilitySaver();
         private BaseMediaRouteDialogManager mManager;
-        private boolean mCancelled;
+        private boolean mIsSinkSelected;
 
         public Fragment() {
             mHandler.post(new Runnable() {
@@ -52,8 +57,7 @@
         @Override
         public MediaRouteChooserDialog onCreateChooserDialog(
                 Context context, Bundle savedInstanceState) {
-            MediaRouteChooserDialog dialog =
-                    super.onCreateChooserDialog(context, savedInstanceState);
+            MediaRouteChooserDialog dialog = new DelayedSelectionDialog(context, getTheme());
             dialog.setCanceledOnTouchOutside(true);
             return dialog;
         }
@@ -71,26 +75,45 @@
         }
 
         @Override
-        public void onCancel(DialogInterface dialog) {
-            mCancelled = true;
-
-            mManager.delegate().onDialogCancelled();
-
-            super.onCancel(dialog);
-        }
-
-        @Override
         public void onDismiss(DialogInterface dialog) {
             super.onDismiss(dialog);
-            if (mManager == null) return;
 
-            mManager.mDialogFragment = null;
+            if (!mIsSinkSelected) mManager.delegate().onDialogCancelled();
+        }
 
-            if (mCancelled) return;
+        private class DelayedSelectionDialog extends MediaRouteChooserDialog {
+            public DelayedSelectionDialog(Context context) {
+                super(context);
+            }
 
-            MediaSink newSink =
-                    MediaSink.fromRoute(mManager.androidMediaRouter().getSelectedRoute());
-            mManager.delegate().onSinkSelected(mManager.sourceId(), newSink);
+            public DelayedSelectionDialog(Context context, int theme) {
+                super(context, theme);
+            }
+
+            @Override
+            public void onCreate(Bundle savedInstanceState) {
+                super.onCreate(savedInstanceState);
+
+                ListView listView = (ListView) findViewById(R.id.mr_chooser_list);
+                if (listView != null) {
+                    listView.setOnItemClickListener(Fragment.this::onItemClick);
+                }
+            }
+        }
+
+        private void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            MediaRouter.RouteInfo routeInfo =
+                    (MediaRouter.RouteInfo) parent.getItemAtPosition(position);
+            if (routeInfo != null && routeInfo.isEnabled()) {
+                MediaSink newSink = MediaSink.fromRoute(routeInfo);
+
+                // When a item is clicked, the route is not selected right away. Instead, the route
+                // selection is postponed to the actual session launch.
+                mManager.delegate().onSinkSelected(mManager.sourceId(), newSink);
+                mIsSinkSelected = true;
+
+                dismiss();
+            }
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java
index 1250ffa8..e57a99f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java
@@ -6,7 +6,6 @@
 
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.support.v7.media.MediaRouter;
 
 import com.google.android.gms.cast.CastDevice;
 import com.google.android.gms.cast.framework.CastSession;
@@ -31,7 +30,6 @@
 
     private CastSession mCastSession;
     private final CafBaseMediaRouteProvider mProvider;
-    private final MediaRouter.Callback mMediaRouterCallbackForSessionLaunch;
     private CreateRouteRequestInfo mRouteCreationInfo;
     @VisibleForTesting
     CafNotificationController mNotificationController;
@@ -39,7 +37,6 @@
 
     public BaseSessionController(CafBaseMediaRouteProvider provider) {
         mProvider = provider;
-        mMediaRouterCallbackForSessionLaunch = new MediaRouterCallbackForSessionLaunch();
         mNotificationController = new CafNotificationController(this);
         mRemoteMediaClientCallback = new RemoteMediaClientCallback();
     }
@@ -49,21 +46,10 @@
         CastUtils.getCastContext().setReceiverApplicationId(
                 mRouteCreationInfo.source.getApplicationId());
 
-        if (mRouteCreationInfo.routeInfo.isSelected()) {
-            // If a route has just been selected, CAF might not be ready yet before setting the app
-            // ID. So unselect and select the route will let CAF be aware that the route has been
-            // selected thus it can start the session.
-            //
-            // An issue of this workaround is that if a route is unselected and selected in a very
-            // short time, the selection might be ignored by MediaRouter, so put the reselection in
-            // a callback.
-            mProvider.getAndroidMediaRouter().addCallback(
-                    mRouteCreationInfo.source.buildRouteSelector(),
-                    mMediaRouterCallbackForSessionLaunch);
-            mProvider.getAndroidMediaRouter().unselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
-        } else {
-            mRouteCreationInfo.routeInfo.select();
-        }
+        // When the user clicks a route on the MediaRouteChooserDialog, we intercept the click event
+        // and do not select the route. Instead the route selection is postponed to here. This will
+        // trigger CAF to launch the session.
+        mRouteCreationInfo.routeInfo.select();
     }
 
     public MediaSource getSource() {
@@ -173,20 +159,6 @@
         }
     }
 
-    private class MediaRouterCallbackForSessionLaunch extends MediaRouter.Callback {
-        @Override
-        public void onRouteUnselected(MediaRouter mediaRouter, MediaRouter.RouteInfo routeInfo) {
-            if (mProvider.getPendingCreateRouteRequestInfo() == null) return;
-
-            if (routeInfo.getId().equals(
-                        mProvider.getPendingCreateRouteRequestInfo().routeInfo.getId())) {
-                routeInfo.select();
-                mProvider.getAndroidMediaRouter().removeCallback(
-                        mMediaRouterCallbackForSessionLaunch);
-            }
-        }
-    }
-
     private class RemoteMediaClientCallback extends RemoteMediaClient.Callback {
         @Override
         public void onStatusUpdated() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java
index cb598d90..ae9aea6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java
@@ -173,18 +173,32 @@
             return;
         }
 
-        MediaSink sink = MediaSink.fromSinkId(sinkId, mAndroidMediaRouter);
-        if (sink == null) {
+        MediaRouter.RouteInfo targetRouteInfo = null;
+        for (MediaRouter.RouteInfo routeInfo : mAndroidMediaRouter.getRoutes()) {
+            if (routeInfo.getId().equals(sinkId)) {
+                targetRouteInfo = routeInfo;
+                break;
+            }
+        }
+
+        if (targetRouteInfo == null) {
             mManager.onRouteRequestError("No sink", nativeRequestId);
             return;
         }
 
+        MediaSink sink = MediaSink.fromRoute(targetRouteInfo);
+
         MediaSource source = getSourceFromId(sourceId);
         if (source == null) {
             mManager.onRouteRequestError("Unsupported source URL", nativeRequestId);
             return;
         }
 
+        // When the user clicks a route on the MediaRouteChooserDialog, we intercept the click event
+        // and do not select the route. Instead the route selection is postponed to here. This will
+        // make sure the MediaRouteControllerDialog show up properly.
+        targetRouteInfo.select();
+
         ChromeCastSessionManager.CastSessionLaunchRequest request = createSessionLaunchRequest(
                 source, sink, presentationId, origin, tabId, isIncognito, nativeRequestId);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/OWNERS
index 845f1d7b..be748d62 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/OWNERS
@@ -1 +1,3 @@
-bengr@chromium.org
+file://components/data_reduction_proxy/OWNERS
+
+# COMPONENT: Internals>Network>DataProxy
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
new file mode 100644
index 0000000..ff726ad0
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomeButton.java
@@ -0,0 +1,77 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.support.v4.content.ContextCompat;
+import android.util.AttributeSet;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnCreateContextMenuListener;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
+import org.chromium.chrome.browser.toolbar.ThemeColorProvider.ThemeColorObserver;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.ui.widget.ChromeImageButton;
+
+/**
+ * The home button.
+ */
+public class HomeButton extends ChromeImageButton implements ThemeColorObserver,
+                                                             OnCreateContextMenuListener,
+                                                             MenuItem.OnMenuItemClickListener {
+    private static final int ID_REMOVE = 0;
+
+    /** A provider that notifies components when the theme color changes.*/
+    private ThemeColorProvider mThemeColorProvider;
+
+    public HomeButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        final int homeButtonIcon = FeatureUtilities.isNewTabPageButtonEnabled()
+                ? R.drawable.ic_home
+                : R.drawable.btn_toolbar_home;
+        setImageDrawable(ContextCompat.getDrawable(context, homeButtonIcon));
+        if (!FeatureUtilities.isNewTabPageButtonEnabled()
+                && !FeatureUtilities.isBottomToolbarEnabled()) {
+            setOnCreateContextMenuListener(this);
+        }
+    }
+
+    public void destroy() {
+        if (mThemeColorProvider != null) {
+            mThemeColorProvider.removeObserver(this);
+            mThemeColorProvider = null;
+        }
+    }
+
+    public void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
+        mThemeColorProvider = themeColorProvider;
+        mThemeColorProvider.addObserver(this);
+    }
+
+    @Override
+    public void onThemeColorChanged(ColorStateList tint, int primaryColor) {
+        ApiCompatibilityUtils.setImageTintList(this, tint);
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+        menu.add(Menu.NONE, ID_REMOVE, Menu.NONE, R.string.remove).setOnMenuItemClickListener(this);
+    }
+
+    @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        assert item.getItemId() == ID_REMOVE;
+        HomepageManager.getInstance().setPrefHomepageEnabled(false);
+        return true;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomePageButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomePageButton.java
deleted file mode 100644
index e03afdd..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/HomePageButton.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnCreateContextMenuListener;
-
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
-import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.ui.widget.ChromeImageButton;
-
-/**
- * View that displays the home page button.
- */
-public class HomePageButton extends ChromeImageButton
-        implements OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener {
-    private static final int ID_REMOVE = 0;
-
-    /** Constructor inflating from XML. */
-    public HomePageButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        if (!FeatureUtilities.isNewTabPageButtonEnabled()) setOnCreateContextMenuListener(this);
-    }
-
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
-        menu.add(Menu.NONE, ID_REMOVE, Menu.NONE, R.string.remove)
-                .setOnMenuItemClickListener(this);
-    }
-
-    @Override
-    public boolean onMenuItemClick(MenuItem item) {
-        assert item.getItemId() == ID_REMOVE;
-        HomepageManager.getInstance().setPrefHomepageEnabled(false);
-        return true;
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
index 0f79804..b339876 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarCoordinator.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.toolbar.HomeButton;
 import org.chromium.chrome.browser.toolbar.MenuButton;
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
 import org.chromium.chrome.browser.toolbar.TabSwitcherButtonCoordinator;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/HomeButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/HomeButton.java
deleted file mode 100644
index fb84ab90..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/HomeButton.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.toolbar.bottom;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.support.v4.content.ContextCompat;
-import android.util.AttributeSet;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.toolbar.ThemeColorProvider;
-import org.chromium.chrome.browser.toolbar.ThemeColorProvider.ThemeColorObserver;
-import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.ui.widget.ChromeImageButton;
-
-/**
- * The home button.
- */
-class HomeButton extends ChromeImageButton implements ThemeColorObserver {
-    /** A provider that notifies components when the theme color changes.*/
-    private ThemeColorProvider mThemeColorProvider;
-
-    public HomeButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        final int homeButtonIcon = FeatureUtilities.isNewTabPageButtonEnabled()
-                ? R.drawable.ic_home
-                : R.drawable.btn_toolbar_home;
-        setImageDrawable(ContextCompat.getDrawable(context, homeButtonIcon));
-    }
-
-    void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
-        mThemeColorProvider = themeColorProvider;
-        mThemeColorProvider.addObserver(this);
-    }
-
-    void destroy() {
-        if (mThemeColorProvider != null) {
-            mThemeColorProvider.removeObserver(this);
-            mThemeColorProvider = null;
-        }
-    }
-
-    @Override
-    public void onThemeColorChanged(ColorStateList tint, int primaryColor) {
-        ApiCompatibilityUtils.setImageTintList(this, tint);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 5a680118..abe2582 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -45,7 +45,6 @@
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarTabController;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.ViewUtils;
 import org.chromium.chrome.browser.widget.PulseDrawable;
 import org.chromium.chrome.browser.widget.ToolbarProgressBar;
@@ -958,15 +957,4 @@
      * it.
      */
     void setTabModelSelector(TabModelSelector selector) {}
-
-    /**
-     * Sets the icon drawable for the ntp button if the ntp button feature is enabled.
-     * Note: This method is called twice in ToolbarLayout's children - once in
-     * #onNativeLibraryReady() & once in #onFinishInflate() (see https://crbug.com/862887).
-     * @param ntpButton The button that needs to be changed.
-     */
-    void changeIconToNTPIcon(ImageButton ntpButton) {
-        if (FeatureUtilities.isNewTabPageButtonEnabled())
-            ntpButton.setImageResource(R.drawable.ic_home);
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 90f20e90..63c2632f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -382,7 +382,6 @@
             mToolbarButtonsContainer = (ViewGroup) findViewById(R.id.toolbar_buttons);
 
             mHomeButton = findViewById(R.id.home_button);
-            changeIconToNTPIcon(mHomeButton);
             if (FeatureUtilities.isBottomToolbarEnabled()) {
                 disableMenuButton();
                 if (mHomeButton != null) {
@@ -510,7 +509,6 @@
         if (!FeatureUtilities.isBottomToolbarEnabled()) enableTabSwitchingResources();
 
         if (mHomeButton != null) {
-            changeIconToNTPIcon(mHomeButton);
             mHomeButton.setOnClickListener(this);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index eabd696..b0dc941 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -105,7 +105,6 @@
         mLocationBar = (LocationBarTablet) findViewById(R.id.location_bar);
 
         mHomeButton = findViewById(R.id.home_button);
-        changeIconToNTPIcon(mHomeButton);
         mBackButton = findViewById(R.id.back_button);
         mForwardButton = findViewById(R.id.forward_button);
         mReloadButton = findViewById(R.id.refresh_button);
@@ -163,7 +162,6 @@
     public void onNativeLibraryReady() {
         super.onNativeLibraryReady();
         mLocationBar.onNativeLibraryReady();
-        changeIconToNTPIcon(mHomeButton);
         mHomeButton.setOnClickListener(this);
         mHomeButton.setOnKeyListener(new KeyboardNavigationListener() {
             @Override
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index c04433c0..04084836 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -404,7 +404,6 @@
   "java/src/org/chromium/chrome/browser/crash/ApplicationStatusTracker.java",
   "java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploadJobService.java",
   "java/src/org/chromium/chrome/browser/crash/ChromeMinidumpUploaderDelegate.java",
-  "java/src/org/chromium/chrome/browser/crash/CrashKeys.java",
   "java/src/org/chromium/chrome/browser/crash/LogcatExtractionRunnable.java",
   "java/src/org/chromium/chrome/browser/crash/MinidumpLogcatPrepender.java",
   "java/src/org/chromium/chrome/browser/crash/MinidumpUploadRetry.java",
@@ -1577,7 +1576,7 @@
   "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/tasks/TasksUma.java",
-  "java/src/org/chromium/chrome/browser/toolbar/HomePageButton.java",
+  "java/src/org/chromium/chrome/browser/toolbar/HomeButton.java",
   "java/src/org/chromium/chrome/browser/toolbar/IncognitoStateProvider.java",
   "java/src/org/chromium/chrome/browser/toolbar/IncognitoToggleTabLayout.java",
   "java/src/org/chromium/chrome/browser/toolbar/KeyboardNavigationListener.java",
@@ -1600,7 +1599,6 @@
   "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarModel.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarViewBinder.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/HomeButton.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/SearchAccelerator.java",
   "java/src/org/chromium/chrome/browser/toolbar/bottom/ShareButton.java",
diff --git a/chrome/android/javatests/DEPS b/chrome/android/javatests/DEPS
index f98022e6..4c30088 100644
--- a/chrome/android/javatests/DEPS
+++ b/chrome/android/javatests/DEPS
@@ -3,6 +3,7 @@
   "+components/autofill/android/java/src/org/chromium/components/autofill",
   "+components/background_task_scheduler/android/java",
   "+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks",
+  "+components/crash/android",
   "+components/embedder_support/android",
   "+components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement",
   "+components/dom_distiller/core/android/java/src/org/chromium/components/dom_distiller/core",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporterTest.java
index 5aadd64..b6b5a513 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/PureJavaExceptionReporterTest.java
@@ -13,6 +13,8 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.components.crash.CrashKeyIndex;
+import org.chromium.components.crash.CrashKeys;
 import org.chromium.components.minidump_uploader.CrashTestRule;
 
 import java.io.BufferedReader;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java
index 0de56d9..2382567 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java
@@ -17,6 +17,7 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -44,6 +45,7 @@
      * Test the pause button on the notification.
      */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java
index 2b2232c..e7cc0efa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java
@@ -19,6 +19,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -83,6 +84,7 @@
 
     /** Test for crbug.com/428409 */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872
@@ -114,6 +116,7 @@
 
     /** Test for crbug.com/428409 */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872
@@ -163,6 +166,7 @@
 
     /** Test for crbug.com/425105 */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/593840, crbug.com/652872
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java
index 440a7f8..4c854b1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java
@@ -18,6 +18,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
@@ -45,6 +46,7 @@
      * Test that we can cast a video, and that we get the ExpandedControllerActivity when we do.
      */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure
@@ -58,6 +60,7 @@
      * Test that we can disconnect a cast session from the expanded controller activity overlay.
      */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure
@@ -76,6 +79,7 @@
      * Test that we can stop a cast session from the notification.
      */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java
index cefd2dc..f3cd818f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java
@@ -20,6 +20,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -44,6 +45,7 @@
     private static final String VIDEO_ELEMENT_2 = "video2";
 
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure // crbug.com/623526
@@ -63,6 +65,7 @@
     }
 
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure // crbug.com/623526
@@ -81,6 +84,7 @@
     }
 
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure // crbug.com/623526
@@ -98,6 +102,7 @@
     }
 
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure // crbug.com/623526
@@ -117,6 +122,7 @@
     }
 
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure // crbug.com/623526
@@ -135,6 +141,7 @@
     }
 
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure // crbug.com/623526
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java
index 8ffb20740..113223b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java
@@ -17,6 +17,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -43,6 +44,7 @@
      * Test the pause button.
      */
     @Test
+    @DisabledTest // crbug.com/907307
     @Feature({"VideoFling"})
     @LargeTest
     @RetryOnFailure
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/BaseSessionControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/BaseSessionControllerTest.java
index 28d649d..b44cf9a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/BaseSessionControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/BaseSessionControllerTest.java
@@ -13,7 +13,6 @@
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -115,51 +114,14 @@
         assertNull(mController.getRouteCreationInfo());
     }
 
-    /**
-     * Test the case of requesting a session launch while the requested route is not selected. The
-     * controller should set the receiver app ID and then select the route so CAF will start the
-     * session once observed the route selection.
-     */
     @Test
-    public void testRequestSessionLaunch_routeNotSelected() {
+    public void testRequestSessionLaunch() {
         mController.requestSessionLaunch();
         verify(mCastContext).setReceiverApplicationId(APP_ID);
         verify(mMediaRouterHelper.getCastRoute()).select();
         assertSame(mRequestInfo, mController.getRouteCreationInfo());
     }
 
-    /**
-     * Test the case of requesting a session launch while the requested route is already selected.
-     * The controller should first unselect the route, set the application ID and then select the
-     * route again to notify CAF to start the session.
-     */
-    @Test
-    public void testRequestSessionLaunch_routeSelected() {
-        mMediaRouterHelper.selectRoute(mMediaRouterHelper.getCastRoute());
-
-        ArgumentCaptor<MediaRouter.Callback> callbackCaptor =
-                ArgumentCaptor.forClass(MediaRouter.Callback.class);
-
-        mController.requestSessionLaunch();
-        verify(mCastContext).setReceiverApplicationId(APP_ID);
-        verify(mMediaRouterHelper.getShadowImpl())
-                .addCallback(eq(mMediaRouteSelector), callbackCaptor.capture());
-        assertSame(mRequestInfo, mController.getRouteCreationInfo());
-        verify(mMediaRouterHelper.getShadowImpl()).unselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
-
-        // Simulate another route has been unselected.
-        callbackCaptor.getValue().onRouteUnselected(
-                MediaRouter.getInstance(mContext), mMediaRouterHelper.getOtherCastRoute());
-
-        assertFalse(mMediaRouterHelper.getCastRoute().isSelected());
-
-        // Simulate the cast route has been unselected.
-        callbackCaptor.getValue().onRouteUnselected(
-                MediaRouter.getInstance(mContext), mMediaRouterHelper.getCastRoute());
-
-        assertTrue(mMediaRouterHelper.getCastRoute().isSelected());
-    }
-
     @Test
     public void testEndSession() {
         mController.endSession();
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ca836d2f..28f5780 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1473,8 +1473,9 @@
     "speech/tts_mac.mm",
     "speech/tts_message_filter.cc",
     "speech/tts_message_filter.h",
-    "speech/tts_platform.cc",
     "speech/tts_platform.h",
+    "speech/tts_platform_impl.cc",
+    "speech/tts_platform_impl.h",
     "speech/tts_win.cc",
     "ssl/bad_clock_blocking_page.cc",
     "ssl/bad_clock_blocking_page.h",
@@ -2114,8 +2115,6 @@
       "android/contextualsearch/unhandled_tap_web_contents_observer.cc",
       "android/contextualsearch/unhandled_tap_web_contents_observer.h",
       "android/cookies/cookies_fetcher_util.cc",
-      "android/crash/crash_keys_android.cc",
-      "android/crash/crash_keys_android.h",
       "android/crash/pure_java_exception_handler.cc",
       "android/crash/pure_java_exception_handler.h",
       "android/customtabs/detached_resource_request.cc",
@@ -2557,6 +2556,7 @@
       "//chrome/services/media_gallery_util/public/cpp",
       "//components/autofill_assistant/browser",
       "//components/cdm/browser",
+      "//components/crash/android:crash_android",
       "//components/embedder_support/android:web_contents_delegate",
       "//components/feed:buildflags",
       "//components/feed:feature_list",
@@ -2599,6 +2599,8 @@
       "apps/app_service/app_service_proxy_factory.h",
       "background/background_contents.cc",
       "background/background_contents.h",
+      "badging/badge_service_impl.cc",
+      "badging/badge_service_impl.h",
       "banners/app_banner_infobar_delegate_desktop.cc",
       "banners/app_banner_infobar_delegate_desktop.h",
       "banners/app_banner_manager_desktop.cc",
@@ -3111,8 +3113,6 @@
       "apps/app_service/built_in_chromeos_apps.h",
       "ash_service_registry.cc",
       "ash_service_registry.h",
-      "badging/badge_service_impl.cc",
-      "badging/badge_service_impl.h",
       "component_updater/cros_component_installer_chromeos.cc",
       "component_updater/cros_component_installer_chromeos.h",
       "component_updater/cros_component_manager.h",
@@ -4687,7 +4687,6 @@
       "../android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java",
       "../android/java/src/org/chromium/chrome/browser/contextualsearch/CtrSuppression.java",
       "../android/java/src/org/chromium/chrome/browser/cookies/CookiesFetcher.java",
-      "../android/java/src/org/chromium/chrome/browser/crash/CrashKeys.java",
       "../android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java",
       "../android/java/src/org/chromium/chrome/browser/crash/PureJavaExceptionHandler.java",
       "../android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f75e3f7..f5e7150 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -335,7 +335,6 @@
 #elif defined(OS_CHROMEOS)
 #include "ash/public/interfaces/constants.mojom.h"
 #include "chrome/browser/ash_service_registry.h"
-#include "chrome/browser/badging/badge_service_impl.h"
 #include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_content_file_system_backend_delegate.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_backend_delegate.h"
@@ -400,6 +399,7 @@
 #endif
 
 #if !defined(OS_ANDROID)
+#include "chrome/browser/badging/badge_service_impl.h"
 #include "chrome/browser/devtools/chrome_devtools_manager_delegate.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/media/unified_autoplay_config.h"
@@ -4340,7 +4340,7 @@
   frame_interfaces_->AddInterface(base::Bind(&ShareServiceImpl::Create));
 #endif
 
-#if defined(OS_CHROMEOS)
+#if !defined(OS_ANDROID)
   frame_interfaces_->AddInterface(
       base::BindRepeating(&BadgeServiceImpl::Create));
 #endif
diff --git a/chrome/browser/chrome_resource_bundle_helper.cc b/chrome/browser/chrome_resource_bundle_helper.cc
index cfe253d..4b1e3731 100644
--- a/chrome/browser/chrome_resource_bundle_helper.cc
+++ b/chrome/browser/chrome_resource_bundle_helper.cc
@@ -42,19 +42,6 @@
   // locale dll to load. This also causes local state prefs to be registered.
   PrefService* local_state = chrome_feature_list_creator->local_state();
   DCHECK(local_state);
-#if defined(OS_WIN)
-  if (first_run::IsChromeFirstRun()) {
-    // During first run we read the google_update registry key to find what
-    // language the user selected when downloading the installer. This
-    // becomes our default language in the prefs.
-    // Other platforms obey the system locale.
-    base::string16 install_lang;
-    if (GoogleUpdateSettings::GetLanguage(&install_lang)) {
-      local_state->SetString(language::prefs::kApplicationLocale,
-                             base::UTF16ToASCII(install_lang));
-    }
-  }
-#endif  // defined(OS_WIN)
 
   // If the local state file for the current profile doesn't exist and the
   // parent profile command line flag is present, then we should inherit some
diff --git a/chrome/browser/chromeos/accessibility/speech_monitor.cc b/chrome/browser/chromeos/accessibility/speech_monitor.cc
index 4c0f3fb..b9a208c 100644
--- a/chrome/browser/chromeos/accessibility/speech_monitor.cc
+++ b/chrome/browser/chromeos/accessibility/speech_monitor.cc
@@ -16,12 +16,12 @@
 }  // namespace
 
 SpeechMonitor::SpeechMonitor() {
-  TtsControllerDelegateImpl::GetInstance()->SetPlatformImpl(this);
+  TtsControllerDelegateImpl::GetInstance()->SetTtsPlatform(this);
 }
 
 SpeechMonitor::~SpeechMonitor() {
-  TtsControllerDelegateImpl::GetInstance()->SetPlatformImpl(
-      TtsPlatformImpl::GetInstance());
+  TtsControllerDelegateImpl::GetInstance()->SetTtsPlatform(
+      TtsPlatform::GetInstance());
 }
 
 std::string SpeechMonitor::GetNextUtterance() {
@@ -99,10 +99,6 @@
   voice.events.insert(content::TTS_EVENT_END);
 }
 
-std::string SpeechMonitor::error() {
-  return "";
-}
-
 void SpeechMonitor::WillSpeakUtteranceWithVoice(
     const content::Utterance* utterance,
     const content::VoiceData& voice_data) {
@@ -120,4 +116,21 @@
     loop_runner_->Quit();
 }
 
+bool SpeechMonitor::LoadBuiltInTtsExtension(
+    content::BrowserContext* browser_context) {
+  return false;
+}
+
+std::string SpeechMonitor::GetError() {
+  return error_;
+}
+
+void SpeechMonitor::ClearError() {
+  error_ = std::string();
+}
+
+void SpeechMonitor::SetError(const std::string& error) {
+  error_ = error;
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/speech_monitor.h b/chrome/browser/chromeos/accessibility/speech_monitor.h
index f434500507..9c31fe8 100644
--- a/chrome/browser/chromeos/accessibility/speech_monitor.h
+++ b/chrome/browser/chromeos/accessibility/speech_monitor.h
@@ -18,10 +18,10 @@
 // For testing purpose installs itself as the platform speech synthesis engine,
 // allowing it to intercept all speech calls, and then provides a method to
 // block until the next utterance is spoken.
-class SpeechMonitor : public TtsPlatformImpl {
+class SpeechMonitor : public TtsPlatform {
  public:
   SpeechMonitor();
-  ~SpeechMonitor() override;
+  virtual ~SpeechMonitor();
 
   // Blocks until the next utterance is spoken, and returns its text.
   std::string GetNextUtterance();
@@ -38,7 +38,7 @@
   void BlockUntilStop();
 
  private:
-  // TtsPlatformImpl implementation.
+  // TtsPlatform implementation.
   bool PlatformImplAvailable() override;
   bool Speak(int utterance_id,
              const std::string& utterance,
@@ -50,16 +50,19 @@
   void GetVoices(std::vector<content::VoiceData>* out_voices) override;
   void Pause() override {}
   void Resume() override {}
-  std::string error() override;
-  void clear_error() override {}
-  void set_error(const std::string& error) override {}
   void WillSpeakUtteranceWithVoice(
       const content::Utterance* utterance,
       const content::VoiceData& voice_data) override;
+  bool LoadBuiltInTtsExtension(
+      content::BrowserContext* browser_context) override;
+  std::string GetError() override;
+  void ClearError() override;
+  void SetError(const std::string& error) override;
 
   scoped_refptr<content::MessageLoopRunner> loop_runner_;
   base::circular_deque<std::string> utterance_queue_;
   bool did_stop_ = false;
+  std::string error_;
 
   DISALLOW_COPY_AND_ASSIGN(SpeechMonitor);
 };
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index b73fa03..893b7c66a 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -580,6 +580,7 @@
                       TestCase("shareFileTeamDrive"),
                       TestCase("shareDirectoryTeamDrive"),
                       TestCase("shareHostedFileTeamDrive"),
+                      TestCase("shareTeamDrive"),
                       TestCase("manageHostedFileTeamDrive"),
                       TestCase("manageFileTeamDrive"),
                       TestCase("manageDirectoryTeamDrive"),
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
index 700d596..d8ffb088 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -39,8 +39,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FileOperationManagerTest) {
-  RunTest(base::FilePath(
-      FILE_PATH_LITERAL("background/js/file_operation_manager_unittest.html")));
+  RunGeneratedTest("/background/js/file_operation_manager_unittest.html");
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DriveSyncHandlerTest) {
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc
index fb890cf..11100c6 100644
--- a/chrome/browser/chromeos/file_manager/path_util.cc
+++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -115,8 +115,12 @@
   storage::ExternalMountPoints* const mount_points =
       storage::ExternalMountPoints::GetSystemInstance();
   base::FilePath path;
-  if (mount_points->GetRegisteredPath(mount_point_name, &path))
+  if (mount_points->GetRegisteredPath(mount_point_name, &path)) {
+    if (base::FeatureList::IsEnabled(chromeos::features::kMyFilesVolume))
+      return path.AppendASCII(kFolderNameDownloads);
+
     return path;
+  }
 
   // Return $HOME/Downloads as Download folder.
   if (ShouldMountPrimaryUserDownloads(profile))
diff --git a/chrome/browser/chromeos/file_manager/path_util_unittest.cc b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
index c19028a..bf53948 100644
--- a/chrome/browser/chromeos/file_manager/path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
@@ -87,10 +87,6 @@
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndEnableFeature(chromeos::features::kMyFilesVolume);
 
-    base::FilePath myfiles_path = profile_path.AppendASCII("MyFiles");
-    base::FilePath myfiles_downloads_path =
-        myfiles_path.AppendASCII("Downloads");
-
     EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles",
               GetMyFilesFolderForProfile(&profile).value());
     EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles/Downloads",
@@ -102,10 +98,39 @@
         storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
         profile_path.Append("MyFiles"));
 
+    // When returning from the mount_point Downloads should still point to
+    // MyFiles/Downloads.
+    EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles/Downloads",
+              GetDownloadsFolderForProfile(&profile).value());
+
     // Still the same: /home/u-{hash}/MyFiles.
     EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles",
               GetMyFilesFolderForProfile(&profile).value());
   }
+  {
+    // Remove mount configured to MyFiles in the previous test.
+    storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+        GetDownloadsMountPointName(&profile));
+
+    // Add mount point for Downloads instead of MyFiles.
+    storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
+        GetDownloadsMountPointName(&profile),
+        storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(),
+        profile_path.Append("Downloads"));
+
+    // Disable MyFilesVolume again to test returning from the mount_point.
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndDisableFeature(chromeos::features::kMyFilesVolume);
+    // When MyFilesVolume feature is disabled it will return the same as
+    // Downloads.
+    EXPECT_EQ(GetDownloadsFolderForProfile(&profile),
+              GetMyFilesFolderForProfile(&profile));
+    EXPECT_EQ("/home/chronos/u-0123456789abcdef/Downloads",
+              GetDownloadsFolderForProfile(&profile).value());
+    // Unmount Downloads because it was interfering with other tests.
+    storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
+        GetDownloadsMountPointName(&profile));
+  }
 }
 
 TEST(FileManagerPathUtilTest, GetPathDisplayTextForSettings) {
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index b1922d8..19b9479 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
@@ -32,7 +32,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/signin_manager_base.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_observer.h"
@@ -41,6 +40,7 @@
 #include "content/public/common/content_switches.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace chromeos {
@@ -127,11 +127,11 @@
 
     // Whether the account is supported for voice interaction.
     bool account_supported = false;
-    SigninManagerBase* signin_manager =
-        SigninManagerFactory::GetForProfileIfExists(profile_);
-    if (signin_manager) {
+    auto* identity_manager =
+        IdentityManagerFactory::GetForProfileIfExists(profile_);
+    if (identity_manager) {
       std::string hosted_domain =
-          signin_manager->GetAuthenticatedAccountInfo().hosted_domain;
+          identity_manager->GetPrimaryAccountInfo().hosted_domain;
       if (hosted_domain == AccountTrackerService::kNoHostedDomainFound ||
           hosted_domain == "google.com") {
         account_supported = true;
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index b6267431..b1b3d38b 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1448,7 +1448,7 @@
   BootTimesRecorder::Get()->AddLoginTimeMarker("TPMOwn-Start", false);
 
   if (!tpm_util::TpmIsEnabled() || tpm_util::TpmIsBeingOwned()) {
-    FinalizePrepareProfile(profile);
+    ClearSigninProfileAndFinalizePrepareProfile(profile);
     return;
   }
 
@@ -1475,7 +1475,13 @@
 void UserSessionManager::OnCryptohomeOperationCompleted(Profile* profile,
                                                         bool result) {
   DCHECK(result);
-  FinalizePrepareProfile(profile);
+  ClearSigninProfileAndFinalizePrepareProfile(profile);
+}
+
+void UserSessionManager::ClearSigninProfileAndFinalizePrepareProfile(
+    Profile* profile) {
+  ProfileHelper::Get()->ClearSigninProfile(base::BindRepeating(
+      &UserSessionManager::FinalizePrepareProfile, AsWeakPtr(), profile));
 }
 
 void UserSessionManager::FinalizePrepareProfile(Profile* profile) {
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h
index 2e8e3d29..de89481 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.h
+++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -389,6 +389,9 @@
   // Called on UI thread once Cryptohome operation completes.
   void OnCryptohomeOperationCompleted(Profile* profile, bool result);
 
+  // Clears the sign-in profile, and initiates the user profile finalization.
+  void ClearSigninProfileAndFinalizePrepareProfile(Profile* profile);
+
   // Finalized profile preparation.
   void FinalizePrepareProfile(Profile* profile);
 
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h
index c1a7f8b..9da989c 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h
@@ -48,13 +48,13 @@
     kMaxValue = kLatest
   };
 
-  // TODO(jiameng): we currently use past 2 seconds of ambient values to
+  // TODO(jiameng): we currently use past 5 seconds of ambient values to
   // calculate average ambient when we predict optimal brightness. This is
   // shorter than the duration used for training data (10 seconds), because it's
   // important that we can quickly adjust the brightness when ambient value
   // changes. This duration should be revised.
   static constexpr base::TimeDelta kAmbientLightShortHorizon =
-      base::TimeDelta::FromSeconds(2);
+      base::TimeDelta::FromSeconds(5);
 
   // Size of |ambient_light_values_|.
   static constexpr int kNumberAmbientValuesToTrack =
@@ -69,16 +69,16 @@
     // from the current value before brightness could be changed: brightness
     // will actually be changed if |min_time_between_brightness_changes| has
     // passed from the previous change.
-    double brightening_lux_threshold_ratio = 0.1;
-    double darkening_lux_threshold_ratio = 0.2;
+    double brightening_lux_threshold_ratio = 0.3;
+    double darkening_lux_threshold_ratio = 0.4;
 
     // If average ambient value changes by more than the "immediate" thresholds
     // then brightness transition will happen immediately, without waiting for
     // |min_time_between_brightness_changes| to elapse. This value should be
     // greater than or equal to the max of brightening/darkening thresholds
     // above.
-    double immediate_brightening_lux_threshold_ratio = 0.3;
-    double immediate_darkening_lux_threshold_ratio = 0.4;
+    double immediate_brightening_lux_threshold_ratio = 0.4;
+    double immediate_darkening_lux_threshold_ratio = 0.5;
 
     // Whether brightness should be set to the predicted value when the first
     // ambient reading comes in. If false, we'll wait for
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc
index 8e7d3eb..d9d3f39 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/adapter_unittest.cc
@@ -427,6 +427,7 @@
 TEST_F(AdapterTest, BrightnessLuxThresholds) {
   Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess,
        global_curve_, personal_curve_, default_params_);
+  EXPECT_EQ(Adapter::kNumberAmbientValuesToTrack, 5);
 
   EXPECT_EQ(adapter_->GetStatusForTesting(), Adapter::Status::kSuccess);
   EXPECT_TRUE(adapter_->GetGlobalCurveForTesting());
@@ -457,7 +458,8 @@
   // A 3rd ALS comes in, but still not enough to trigger brightness change.
   fake_als_reader_.ReportAmbientLightUpdate(15);
   EXPECT_EQ(test_observer_.num_changes(), 1);
-  EXPECT_DOUBLE_EQ(adapter_->GetAverageAmbientForTesting(), (21 + 15) / 2.0);
+  EXPECT_DOUBLE_EQ(adapter_->GetAverageAmbientForTesting(),
+                   (20 + 21 + 15) / 3.0);
   EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(), 22);
   EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(), 16);
 
@@ -467,15 +469,26 @@
   scoped_task_environment_.RunUntilIdle();
 
   EXPECT_EQ(test_observer_.num_changes(), 2);
-  EXPECT_EQ(Adapter::kNumberAmbientValuesToTrack, 2);
-  const double expected_average_ambient =
-      (15.0 + 7.0) / Adapter::kNumberAmbientValuesToTrack;
+  const double expected_average_ambient = (20 + 21 + 15 + 7) / 4.0;
   EXPECT_DOUBLE_EQ(adapter_->GetAverageAmbientForTesting(),
                    expected_average_ambient);
   EXPECT_DOUBLE_EQ(adapter_->GetBrighteningThresholdForTesting(),
                    expected_average_ambient * 1.1);
   EXPECT_DOUBLE_EQ(adapter_->GetDarkeningThresholdForTesting(),
                    expected_average_ambient * 0.8);
+
+  // Next check |ambient_light_values_| has capacity
+  // |Adapter::kNumberAmbientValuesToTrack|.
+  fake_als_reader_.ReportAmbientLightUpdate(8);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_DOUBLE_EQ(adapter_->GetAverageAmbientForTesting(),
+                   (20 + 21 + 15 + 7 + 8) / 5.0);
+
+  fake_als_reader_.ReportAmbientLightUpdate(9);
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_DOUBLE_EQ(adapter_->GetAverageAmbientForTesting(),
+                   (21 + 15 + 7 + 8 + 9) / 5.0);
 }
 
 TEST_F(AdapterTest, ImmediateBrightnessTransitionThresholds) {
@@ -530,7 +543,7 @@
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(test_observer_.num_changes(), 0);
 
-  scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(2));
+  scoped_task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5));
   EXPECT_EQ(test_observer_.num_changes(), 0);
 
   // 2nd ALS comes in so that we have |kAmbientLightShortHorizonSeconds| of
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
index 221309b..e2d1e371 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
@@ -31,7 +31,8 @@
 namespace {
 
 // Creates a global/default brightness curve.
-// TODO(crbug.com/881215): add actual default curve and then add unit test too.
+// TODO(crbug.com/881215): default curve may be revised, if so, need to update
+// unit tests as well.
 MonotoneCubicSpline CreateGlobalCurve() {
   const std::string global_curve = GetFieldTrialParamValueByFeature(
       features::kAutoScreenBrightness, "global_curve");
@@ -43,8 +44,14 @@
     // TODO(jiameng): log error to UMA.
   }
 
-  const std::vector<double> default_log_lux = {0, 100};
-  const std::vector<double> default_brightness = {50, 100};
+  const std::vector<double> default_log_lux = {
+      3.69, 4.83, 6.54, 7.68, 8.25, 8.82,
+  };
+
+  const std::vector<double> default_brightness = {
+      36.14, 47.62, 85.83, 93.27, 93.27, 100,
+  };
+
   return MonotoneCubicSpline(default_log_lux, default_brightness);
 }
 
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
index c809a22..d71dea1 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl_unittest.cc
@@ -561,7 +561,13 @@
       features::kAutoScreenBrightness, {{"global_curve", global_curve_spec}});
 
   // Defined by default values.
-  const MonotoneCubicSpline expected_global_curve({0, 100}, {50, 100});
+  const MonotoneCubicSpline expected_global_curve(
+      {
+          3.69, 4.83, 6.54, 7.68, 8.25, 8.82,
+      },
+      {
+          36.14, 47.62, 85.83, 93.27, 93.27, 100,
+      });
 
   Init(AlsReader::AlsInitStatus::kSuccess, BrightnessMonitor::Status::kSuccess);
   EXPECT_EQ(modeller_->GetGlobalCurveForTesting(), expected_global_curve);
diff --git a/chrome/browser/chromeos/profiles/profile_helper.cc b/chrome/browser/chromeos/profiles/profile_helper.cc
index b5588626..e74d0a2 100644
--- a/chrome/browser/chromeos/profiles/profile_helper.cc
+++ b/chrome/browser/chromeos/profiles/profile_helper.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/chromeos_constants.h"
@@ -52,6 +53,11 @@
       user_id_hash != chrome::kTestUserProfileDir;
 }
 
+void WrapAsBrowsersCloseCallback(const base::RepeatingClosure& callback,
+                                 const base::FilePath& path) {
+  callback.Run();
+}
+
 class UsernameHashMatcher {
  public:
   explicit UsernameHashMatcher(const std::string& h) : username_hash(h) {}
@@ -286,7 +292,7 @@
     return;
   }
   on_clear_profile_stage_finished_ =
-      base::BarrierClosure(2, base::Bind(&ProfileHelper::OnSigninProfileCleared,
+      base::BarrierClosure(3, base::Bind(&ProfileHelper::OnSigninProfileCleared,
                                          weak_factory_.GetWeakPtr()));
   LOG_ASSERT(!browsing_data_remover_);
   browsing_data_remover_ =
@@ -302,6 +308,16 @@
   login::SigninPartitionManager::Factory::GetForBrowserContext(
       GetSigninProfile())
       ->CloseCurrentSigninSession(on_clear_profile_stage_finished_);
+
+  BrowserList::CloseAllBrowsersWithProfile(
+      GetSigninProfile(),
+      base::BindRepeating(
+          &WrapAsBrowsersCloseCallback,
+          on_clear_profile_stage_finished_) /* on_close_success */,
+      base::BindRepeating(
+          &WrapAsBrowsersCloseCallback,
+          on_clear_profile_stage_finished_) /* on_close_aborted */,
+      true /* skip_beforeunload */);
 }
 
 Profile* ProfileHelper::GetProfileByAccountId(const AccountId& account_id) {
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
index 77d452ba..6021e64 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
@@ -439,6 +439,7 @@
     personal_data->UpdateCreditCard(credit_card);
   } else {
     personal_data->AddCreditCard(credit_card);
+    base::RecordAction(base::UserMetricsAction("AutofillCreditCardsAdded"));
   }
 
   return RespondNow(NoArguments());
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e644e53..878fa2a3 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -463,8 +463,8 @@
   },
   {
     "name": "dcheck-is-fatal",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "wez" ],
+    "expiry_milestone": -1
   },
   {
     "name": "debug-packed-apps",
diff --git a/chrome/browser/metrics/chrome_feature_list_creator.cc b/chrome/browser/metrics/chrome_feature_list_creator.cc
index 3c6dd66..0bae50e 100644
--- a/chrome/browser/metrics/chrome_feature_list_creator.cc
+++ b/chrome/browser/metrics/chrome_feature_list_creator.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
@@ -26,6 +27,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/installer/util/google_update_settings.h"
 #include "components/flags_ui/flags_ui_pref_names.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
 #include "components/language/core/browser/pref_names.h"
@@ -147,6 +149,23 @@
       local_state_file, browser_policy_connector_->GetPolicyService(),
       std::move(pref_registry), false, std::move(delegate),
       browser_policy_connector_.get());
+
+// TODO(asvitkine): This is done here so that the pref is set before
+// VariationsService queries the locale. This should potentially be moved to
+// somewhere better, e.g. as a helper in first_run namespace.
+#if defined(OS_WIN)
+  if (first_run::IsChromeFirstRun()) {
+    // During first run we read the google_update registry key to find what
+    // language the user selected when downloading the installer. This
+    // becomes our default language in the prefs.
+    // Other platforms obey the system locale.
+    base::string16 install_lang;
+    if (GoogleUpdateSettings::GetLanguage(&install_lang)) {
+      local_state_->SetString(language::prefs::kApplicationLocale,
+                              base::UTF16ToASCII(install_lang));
+    }
+  }
+#endif  // defined(OS_WIN)
 }
 
 void ChromeFeatureListCreator::ConvertFlagsToSwitches() {
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc
index 24d98a9..e8ae0a9 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor.cc
@@ -108,9 +108,14 @@
                   is_same_host_scale_ + contains_image_scale_ +
                   is_url_incremented_scale_ + source_engagement_score_scale_ +
                   target_engagement_score_scale_ + area_rank_scale_),
-      is_low_end_device_(base::SysInfo::IsLowEndDevice()) {
+      is_low_end_device_(base::SysInfo::IsLowEndDevice()),
+      prefetch_url_score_threshold_(base::GetFieldTrialParamByFeatureAsInt(
+          blink::features::kRecordAnchorMetricsVisible,
+          "prefetch_url_score_threshold",
+          0)) {
   DCHECK(browser_context_);
   DETACH_FROM_SEQUENCE(sequence_checker_);
+  DCHECK_LE(0, prefetch_url_score_threshold_);
 
   if (render_frame_host != GetMainFrame(render_frame_host))
     return;
@@ -702,15 +707,22 @@
   if (source_is_default_search_engine_page_)
     return base::nullopt;
 
-  // Return the same-origin URL that has the highest navigation score.
-  for (const auto& navigation_score : sorted_navigation_scores) {
-    // Currently, only same origin URLs can be prefetched.
-    if (url::Origin::Create(navigation_score->url) != document_origin)
-      continue;
-    return navigation_score->url;
+  if (sorted_navigation_scores.empty())
+    return base::nullopt;
+
+  // Only the same origin URLs are eligible for prefetching. If the URL with
+  // the highest score is from a different origin, then we skip prefetching
+  // since same origin URLs are not likely to be clicked.
+  if (url::Origin::Create(sorted_navigation_scores[0]->url) !=
+      document_origin) {
+    return base::nullopt;
   }
 
-  return base::nullopt;
+  // If the prediction score of the highest scoring URL is less than the
+  // threshold, then return.
+  if (sorted_navigation_scores[0]->score < prefetch_url_score_threshold_)
+    return base::nullopt;
+  return sorted_navigation_scores[0]->url;
 }
 
 void NavigationPredictor::RecordMetricsOnLoad(
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.h b/chrome/browser/navigation_predictor/navigation_predictor.h
index a1a9927..bfacf2d 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.h
+++ b/chrome/browser/navigation_predictor/navigation_predictor.h
@@ -82,6 +82,7 @@
   };
 
  protected:
+  // URL that we decided to prefetch.
   base::Optional<GURL> prefetch_url_;
 
  private:
@@ -183,6 +184,9 @@
   // True if device is a low end device.
   const bool is_low_end_device_;
 
+  // Minimum score that a URL should have for it to be prefetched.
+  const int prefetch_url_score_threshold_;
+
   // Timing of document loaded and last click.
   base::TimeTicks document_loaded_timing_;
   base::TimeTicks last_click_timing_;
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
index 6eecddc..0a08c2e 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
@@ -5,8 +5,10 @@
 #include "chrome/browser/navigation_predictor/navigation_predictor.h"
 
 #include <map>
+#include <string>
 
 #include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -64,6 +66,7 @@
   ~NavigationPredictorTest() override = default;
 
   void SetUp() override {
+    SetupFieldTrial(base::nullopt /* prefetch_url_score_threshold */);
     ChromeRenderViewHostTestHarness::SetUp();
     predictor_service_helper_ = std::make_unique<TestNavigationPredictor>(
         mojo::MakeRequest(&predictor_service_), main_rfh());
@@ -93,9 +96,25 @@
     return predictor_service_helper_->prefetch_url();
   }
 
- private:
+ protected:
+  void SetupFieldTrial(base::Optional<int> prefetch_url_score_threshold) {
+    const std::string kTrialName = "TrialFoo2";
+    const std::string kGroupName = "GroupFoo2";  // Value not used
+
+    std::map<std::string, std::string> params;
+    if (prefetch_url_score_threshold.has_value()) {
+      params["prefetch_url_score_threshold"] =
+          base::IntToString(prefetch_url_score_threshold.value());
+    }
+    scoped_feature_list.InitAndEnableFeatureWithParameters(
+        blink::features::kRecordAnchorMetricsVisible, params);
+  }
+
   blink::mojom::AnchorElementMetricsHostPtr predictor_service_;
   std::unique_ptr<TestNavigationPredictor> predictor_service_helper_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list;
 };
 
 }  // namespace
@@ -265,16 +284,17 @@
   EXPECT_FALSE(prefetch_url().has_value());
 }
 
+// URL with highest prefetch score is from the same origin. Prefetch is done.
 TEST_F(NavigationPredictorTest, ActionTaken_SameOrigin_Prefetch) {
   const std::string source = "https://example.com";
   const std::string same_origin_href_small = "https://example.com/small";
   const std::string same_origin_href_large = "https://example.com/large";
-  const std::string diff_origin_href_xlarge = "https://example2.com/xlarge";
+  const std::string diff_origin_href_xsmall = "https://example2.com/xsmall";
 
   std::vector<blink::mojom::AnchorElementMetricsPtr> metrics;
   metrics.push_back(CreateMetricsPtr(source, same_origin_href_large, 1));
   metrics.push_back(CreateMetricsPtr(source, same_origin_href_small, 0.01));
-  metrics.push_back(CreateMetricsPtr(source, diff_origin_href_xlarge, 1));
+  metrics.push_back(CreateMetricsPtr(source, diff_origin_href_xsmall, 0.01));
 
   base::HistogramTester histogram_tester;
   predictor_service()->ReportAnchorElementMetricsOnLoad(std::move(metrics));
@@ -297,6 +317,38 @@
       NavigationPredictor::ActionAccuracy::kPrefetchActionClickToSameOrigin, 1);
 }
 
+// URL with highest prefetch score is from a different origin. So, prefetch is
+// not done.
+TEST_F(NavigationPredictorTest, ActionTaken_SameOrigin_Prefetch_NotSameOrigin) {
+  const std::string source = "https://example.com";
+  const std::string same_origin_href_small = "https://example.com/small";
+  const std::string same_origin_href_large = "https://example.com/large";
+  const std::string diff_origin_href_xlarge = "https://example2.com/xlarge";
+
+  std::vector<blink::mojom::AnchorElementMetricsPtr> metrics;
+  metrics.push_back(CreateMetricsPtr(source, same_origin_href_large, 1));
+  metrics.push_back(CreateMetricsPtr(source, same_origin_href_small, 0.01));
+  metrics.push_back(CreateMetricsPtr(source, diff_origin_href_xlarge, 10));
+
+  base::HistogramTester histogram_tester;
+  predictor_service()->ReportAnchorElementMetricsOnLoad(std::move(metrics));
+  base::RunLoop().RunUntilIdle();
+
+  histogram_tester.ExpectUniqueSample(
+      "NavigationPredictor.OnNonDSE.ActionTaken",
+      NavigationPredictor::Action::kNone, 1);
+  EXPECT_FALSE(prefetch_url().has_value());
+
+  auto metrics_clicked = CreateMetricsPtr(source, same_origin_href_small, 0.01);
+  predictor_service()->ReportAnchorElementMetricsOnClick(
+      std::move(metrics_clicked));
+  base::RunLoop().RunUntilIdle();
+
+  histogram_tester.ExpectUniqueSample(
+      "NavigationPredictor.OnNonDSE.AccuracyActionTaken",
+      NavigationPredictor::ActionAccuracy::kNoActionTakenClickHappened, 1);
+}
+
 TEST_F(NavigationPredictorTest,
        ActionTaken_SameOrigin_DifferentScheme_Prefetch) {
   const std::string source = "https://example.com";
@@ -316,3 +368,49 @@
       NavigationPredictor::Action::kNone, 1);
   EXPECT_FALSE(prefetch_url().has_value());
 }
+
+// Framework for testing cases where prefetch is effectively
+// disabled by setting |prefetch_url_score_threshold| to too high.
+class NavigationPredictorPrefetchDisabledTest : public NavigationPredictorTest {
+ public:
+  void SetUp() override {
+    SetupFieldTrial(101 /* prefetch_url_score_threshold */);
+
+    ChromeRenderViewHostTestHarness::SetUp();
+    predictor_service_helper_ = std::make_unique<TestNavigationPredictor>(
+        mojo::MakeRequest(&predictor_service_), main_rfh());
+  }
+};
+
+TEST_F(NavigationPredictorPrefetchDisabledTest,
+       ActionTaken_SameOrigin_Prefetch_BelowThreshold) {
+  const std::string source = "https://example.com";
+  const std::string same_origin_href_small = "https://example.com/small";
+  const std::string same_origin_href_large = "https://example.com/large";
+  const std::string diff_origin_href_xsmall = "https://example2.com/xsmall";
+
+  std::vector<blink::mojom::AnchorElementMetricsPtr> metrics;
+  metrics.push_back(CreateMetricsPtr(source, same_origin_href_large, 1));
+  metrics.push_back(CreateMetricsPtr(source, same_origin_href_small, 0.01));
+  metrics.push_back(CreateMetricsPtr(source, diff_origin_href_xsmall, 0.0001));
+
+  base::HistogramTester histogram_tester;
+  predictor_service()->ReportAnchorElementMetricsOnLoad(std::move(metrics));
+  base::RunLoop().RunUntilIdle();
+
+  histogram_tester.ExpectUniqueSample(
+      "NavigationPredictor.OnNonDSE.ActionTaken",
+      NavigationPredictor::Action::kNone, 1);
+  EXPECT_FALSE(prefetch_url().has_value());
+
+  auto metrics_clicked = CreateMetricsPtr(source, same_origin_href_small, 0.01);
+  predictor_service()->ReportAnchorElementMetricsOnClick(
+      std::move(metrics_clicked));
+  base::RunLoop().RunUntilIdle();
+
+  histogram_tester.ExpectTotalCount(
+      "AnchorElementMetrics.Clicked.HrefEngagementScore2", 1);
+  histogram_tester.ExpectUniqueSample(
+      "NavigationPredictor.OnNonDSE.AccuracyActionTaken",
+      NavigationPredictor::ActionAccuracy::kNoActionTakenClickHappened, 1);
+}
diff --git a/chrome/browser/policy/browser_dm_token_storage_mac.mm b/chrome/browser/policy/browser_dm_token_storage_mac.mm
index 56fd4f97..9cf4945 100644
--- a/chrome/browser/policy/browser_dm_token_storage_mac.mm
+++ b/chrome/browser/policy/browser_dm_token_storage_mac.mm
@@ -17,6 +17,7 @@
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_ioobject.h"
 #include "base/no_destructor.h"
+#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/sha1.h"
 #include "base/strings/string16.h"
@@ -47,6 +48,18 @@
 const char kEnrollmentTokenOldFilePath[] = FILE_PATH_LITERAL(
     "/Library/Google/Chrome/MachineLevelUserCloudPolicyEnrollmentToken");
 
+// Enrollment Mandatory Option
+const CFStringRef kEnrollmentMandatoryOptionPolicyName =
+    CFSTR("CloudManagementEnrollmentMandatory");
+const char kEnrollmentOptionsFilePath[] = FILE_PATH_LITERAL(
+    "/Library/Google/Chrome/CloudManagementEnrollmentOptions");
+const char kEnrollmentMandatoryOption[] = "Mandatory";
+
+// Explicitly access the "com.google.Chrome" bundle ID, no matter what this
+// app's bundle ID actually is. All channels of Chrome should obey the same
+// policies.
+const CFStringRef kBundleId = CFSTR("com.google.Chrome");
+
 bool GetDmTokenFilePath(base::FilePath* token_file_path,
                         const std::string& client_id,
                         bool create_dir) {
@@ -80,31 +93,21 @@
 // Get the enrollment token from policy file: /Library/com.google.Chrome.plist.
 // Return true if policy is set, otherwise false.
 bool GetEnrollmentTokenFromPolicy(std::string* enrollment_token) {
-// Since the configuration management infrastructure is not initialized when
-// this code runs, read the policy preference directly.
-#if defined(GOOGLE_CHROME_BUILD)
-  // Explicitly access the "com.google.Chrome" bundle ID, no matter what this
-  // app's bundle ID actually is. All channels of Chrome should obey the same
-  // policies.
-  CFStringRef bundle_id = CFSTR("com.google.Chrome");
-#else
-  base::ScopedCFTypeRef<CFStringRef> bundle_id(
-      base::SysUTF8ToCFStringRef(base::mac::BaseBundleID()));
-#endif
-
+  // Since the configuration management infrastructure is not initialized when
+  // this code runs, read the policy preference directly.
   base::ScopedCFTypeRef<CFPropertyListRef> value(
-      CFPreferencesCopyAppValue(kEnrollmentTokenPolicyName, bundle_id));
+      CFPreferencesCopyAppValue(kEnrollmentTokenPolicyName, kBundleId));
 
   // Read the enrollment token from the new location. If that fails, try the old
   // location (which will be deprecated soon). If that also fails, bail as there
   // is no token set.
   if (!value ||
-      !CFPreferencesAppValueIsForced(kEnrollmentTokenPolicyName, bundle_id)) {
+      !CFPreferencesAppValueIsForced(kEnrollmentTokenPolicyName, kBundleId)) {
     // TODO(crbug.com/907589) : Remove once no longer in use.
     value.reset(
-        CFPreferencesCopyAppValue(kEnrollmentTokenOldPolicyName, bundle_id));
+        CFPreferencesCopyAppValue(kEnrollmentTokenOldPolicyName, kBundleId));
     if (!value || !CFPreferencesAppValueIsForced(kEnrollmentTokenOldPolicyName,
-                                                 bundle_id)) {
+                                                 kBundleId)) {
       return false;
     }
   }
@@ -133,6 +136,31 @@
   return true;
 }
 
+base::Optional<bool> IsEnrollmentMandatoryByPolicy() {
+  base::ScopedCFTypeRef<CFPropertyListRef> value(CFPreferencesCopyAppValue(
+      kEnrollmentMandatoryOptionPolicyName, kBundleId));
+
+  if (!value || !CFPreferencesAppValueIsForced(
+                    kEnrollmentMandatoryOptionPolicyName, kBundleId)) {
+    return base::Optional<bool>();
+  }
+
+  CFBooleanRef value_bool = base::mac::CFCast<CFBooleanRef>(value);
+  if (!value_bool)
+    return base::Optional<bool>();
+  return value_bool == kCFBooleanTrue;
+}
+
+base::Optional<bool> IsEnrollmentMandatoryByFile() {
+  std::string options;
+  if (!base::ReadFileToString(base::FilePath(kEnrollmentOptionsFilePath),
+                              &options)) {
+    return base::Optional<bool>();
+  }
+  return base::TrimWhitespaceASCII(options, base::TRIM_ALL).as_string() ==
+         kEnrollmentMandatoryOption;
+}
+
 }  // namespace
 
 // static
@@ -197,8 +225,11 @@
 }
 
 bool BrowserDMTokenStorageMac::InitEnrollmentErrorOption() {
-  // TODO(crbug/904983): Load the policy value for this option.
-  return true;
+  base::Optional<bool> is_mandatory = IsEnrollmentMandatoryByPolicy();
+  if (is_mandatory)
+    return is_mandatory.value();
+
+  return IsEnrollmentMandatoryByFile().value_or(false);
 }
 
 void BrowserDMTokenStorageMac::SaveDMToken(const std::string& token) {
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index e8a4ad01a..6ecd46e5 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -3466,20 +3466,20 @@
 
 // When instantiated, mocks out the global text-to-speech engine with something
 // that emulates speaking any phrase for the duration of 0ms.
-class TtsPlatformMock : public TtsPlatformImpl {
+class TtsPlatformMock : public TtsPlatform {
  public:
   TtsPlatformMock() : speaking_requested_(false) {
-    TtsControllerDelegateImpl::GetInstance()->SetPlatformImpl(this);
+    TtsControllerDelegateImpl::GetInstance()->SetTtsPlatform(this);
   }
 
-  ~TtsPlatformMock() override {
-    TtsControllerDelegateImpl::GetInstance()->SetPlatformImpl(
-        TtsPlatformImpl::GetInstance());
+  virtual ~TtsPlatformMock() {
+    TtsControllerDelegateImpl::GetInstance()->SetTtsPlatform(
+        TtsPlatform::GetInstance());
   }
 
   bool speaking_requested() { return speaking_requested_; }
 
-  // TtsPlatformImpl:
+  // TtsPlatform:
 
   bool PlatformImplAvailable() override { return true; }
 
@@ -3512,6 +3512,21 @@
 
   void Resume() override {}
 
+  bool LoadBuiltInTtsExtension(
+      content::BrowserContext* browser_context) override {
+    return false;
+  }
+
+  void WillSpeakUtteranceWithVoice(
+      const content::Utterance* utterance,
+      const content::VoiceData& voice_data) override {}
+
+  void SetError(const std::string& error) override {}
+
+  std::string GetError() override { return std::string(); }
+
+  void ClearError() override {}
+
  private:
   bool speaking_requested_;
 };
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index 3945233..660b312 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -1652,13 +1652,7 @@
   EXPECT_EQ("permission status - denied", script_result);
 }
 
-// TODO(peter): Flaky on Win buildbots. https://crbug.com/838759
-#if defined(OS_WIN)
-#define MAYBE_UnsubscribeSuccess DISABLED_UnsubscribeSuccess
-#else
-#define MAYBE_UnsubscribeSuccess UnsubscribeSucces
-#endif
-IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, MAYBE_UnsubscribeSuccess) {
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
   std::string script_result;
 
   std::string token1;
diff --git a/chrome/browser/resources/md_history/history_item.js b/chrome/browser/resources/md_history/history_item.js
index 78c71cd..f4af1cd 100644
--- a/chrome/browser/resources/md_history/history_item.js
+++ b/chrome/browser/resources/md_history/history_item.js
@@ -160,16 +160,26 @@
     },
 
     /**
+     * @param {!Event} e The focus event
      * @private
      */
-    onFocus_: function() {
+    onFocus_: function(e) {
       // Don't change the focus while the mouse is down, as it prevents text
       // selection. Not changing focus here is acceptable because the checkbox
       // will be focused in onItemClick_() anyway.
       if (this.mouseDown_)
         return;
 
-      if (this.lastFocused && !this.blurred_)
+      // If focus is being restored from outside the item and the event is fired
+      // by the list item itself, focus the first control so that the user can
+      // tab through all the controls. When the user shift-tabs back to the row,
+      // or focus is restored to the row from a dropdown on the last item, the
+      // last child item will be focused before the row itself. Since this is
+      // the desired behavior, do not shift focus to the first item in these
+      // cases.
+      const restoreFocusToFirst = this.blurred_ && e.composedPath()[0] === this;
+
+      if (this.lastFocused && !restoreFocusToFirst)
         this.row_.getEquivalentElement(this.lastFocused).focus();
       else
         this.row_.getFirstFocusable().focus();
diff --git a/chrome/browser/resources/omnibox/omnibox_output.js b/chrome/browser/resources/omnibox/omnibox_output.js
index d77b4266..9d6123c8 100644
--- a/chrome/browser/resources/omnibox/omnibox_output.js
+++ b/chrome/browser/resources/omnibox/omnibox_output.js
@@ -469,10 +469,15 @@
       OutputMatch.displayedProperties(showDetails)
           .map(property => {
             const value = this.properties[property.propertyName];
-            if (typeof value === 'object')
-              return OutputMatch.renderJsonProperty_(value);
             if (typeof value === 'boolean')
               return OutputMatch.renderBooleanProperty_(value);
+            if (typeof value === 'object') {
+              // We check if the first element has key and value properties.
+              if (value && value[0] && value[0].key && value[0].value)
+                return OutputMatch.renderKeyValueTuples_(value);
+              else
+                return OutputMatch.renderJsonProperty_(value);
+            }
             const LINK_REGEX = /^(http|https|ftp|chrome|file):\/\//;
             if (LINK_REGEX.test(value))
               return OutputMatch.renderLinkProperty_(value);
@@ -544,6 +549,21 @@
 
     /**
      * @private
+     * @param {Array<{key: string, value: string}>} propertyValue
+     * @return {Element}
+     */
+    static renderKeyValueTuples_(propertyValue) {
+      const cell = document.createElement('td');
+      const pre = document.createElement('pre');
+      const text = propertyValue.reduce(
+          (prev, current) => `${prev}${current.key}: ${current.value}\n`, '');
+      pre.textContent = text;
+      cell.appendChild(pre);
+      return cell;
+    }
+
+    /**
+     * @private
      * @param {boolean} showDetails
      * @param {boolean} showAdditionalHeader
      * @return {!Element}
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index 3c05f722..f2baeaf 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -1170,6 +1170,7 @@
           this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
       if (type === print_preview.PrinterType.EXTENSION_PRINTER)
         this.endExtensionPrinterSearch_();
+      this.sendNoPrinterEventIfNeeded_();
     }
 
     /**
@@ -1270,6 +1271,23 @@
       }
       cr.dispatchSimpleEvent(
           this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
+      this.sendNoPrinterEventIfNeeded_();
+    }
+
+    /**
+     * Checks if the search is done and no printers are found. If so, fires a
+     * DestinationStore.EventType.NO_DESTINATIONS_FOUND event.
+     * @private
+     */
+    sendNoPrinterEventIfNeeded_() {
+      if (this.isPrintDestinationSearchInProgress ||
+          !this.selectFirstDestination_) {
+        return;
+      }
+
+      this.selectFirstDestination_ = false;
+      cr.dispatchSimpleEvent(
+          this, DestinationStore.EventType.NO_DESTINATIONS_FOUND);
     }
 
     /**
@@ -1397,6 +1415,8 @@
         'print_preview.DestinationStore.PROVISIONAL_DESTINATION_RESOLVED',
     CACHED_SELECTED_DESTINATION_INFO_READY:
         'print_preview.DestinationStore.CACHED_SELECTED_DESTINATION_INFO_READY',
+    NO_DESTINATIONS_FOUND:
+        'print_preview.DestinationStore.NO_DESTINATIONS_FOUND',
     SELECTED_DESTINATION_CAPABILITIES_READY: 'print_preview.DestinationStore' +
         '.SELECTED_DESTINATION_CAPABILITIES_READY',
     SELECTED_DESTINATION_INVALID:
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js
index 1ba5afb..e8859d1 100644
--- a/chrome/browser/resources/print_preview/new/app.js
+++ b/chrome/browser/resources/print_preview/new/app.js
@@ -194,6 +194,12 @@
         this.destinationStore_,
         print_preview.DestinationStore.EventType.DESTINATION_SELECT,
         this.onDestinationSelect_.bind(this));
+    // <if expr="chromeos">
+    this.tracker_.add(
+        this.destinationStore_,
+        print_preview.DestinationStore.EventType.NO_DESTINATIONS_FOUND,
+        this.onNoDestinationsFound_.bind(this));
+    // </if>
     this.tracker_.add(
         this.destinationStore_,
         print_preview.DestinationStore.EventType
@@ -656,6 +662,15 @@
     return this.settingsExpandedByUser_ || !this.shouldShowMoreSettings_;
   },
 
+  // <if expr="chromeos">
+  /** @private */
+  onNoDestinationsFound_: function() {
+    this.$.state.transitTo(print_preview_new.State.INVALID_PRINTER);
+    this.$.previewArea.setNoDestinationsFound();
+    this.$.destinationSettings.noDestinationsFound = true;
+  },
+  // </if>
+
   /** @private */
   close_: function() {
     this.$.state.transitTo(print_preview_new.State.CLOSING);
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.html b/chrome/browser/resources/print_preview/new/destination_settings.html
index 8200b9db..f26075f9 100644
--- a/chrome/browser/resources/print_preview/new/destination_settings.html
+++ b/chrome/browser/resources/print_preview/new/destination_settings.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/event_tracker.html">
@@ -34,13 +35,15 @@
       }
 
       .destination-settings-box,
+      .no-destinations-display,
       .throbber-container {
         align-items: center;
         display: flex;
         overflow: hidden;
       }
 
-      .destination-settings-box iron-icon {
+      .destination-settings-box iron-icon,
+      .no-destinations-display iron-icon {
         fill: var(--google-grey-600);
         flex: 0;
         margin-inline-end: 8px;
@@ -63,7 +66,8 @@
       }
 
       .destination-info-wrapper > div,
-      .destination-throbber-name {
+      .destination-throbber-name,
+      .no-destinations-message {
         flex: 1;
         overflow: hidden;
         text-overflow: ellipsis;
@@ -77,10 +81,18 @@
     <print-preview-settings-section>
       <span slot="title">$i18n{destinationLabel}</span>
       <div slot="controls">
-        <div class="throbber-container" hidden="[[!loadingDestination_]]">
+        <div class="throbber-container"
+            hidden="[[!shouldShowSpinner_(
+                loadingDestination_, noDestinationsFound)]]">
           <div class="throbber"></div>
           <div class="destination-throbber-name"></div>
         </div>
+        <div class="no-destinations-display" hidden="[[!noDestinationsFound]]">
+          <iron-icon icon="cr:error"></iron-icon>
+          <div class="no-destinations-message">
+            $i18n{noDestinationsMessage}
+          </div>
+        </div>
         <div class="destination-settings-box" hidden="[[loadingDestination_]]">
           <iron-icon icon$="[[destination.icon]]" hidden="[[!destination]]">
           </iron-icon>
@@ -100,7 +112,7 @@
       <div slot="controls">
         <paper-button
           disabled$="[[shouldDisableButton_(destinationStore, disabled,
-                                            state)]]"
+                                            state, noDestinationsFound)]]"
           on-click="onChangeButtonClick_">
           $i18n{changeDestination}
         </paper-button>
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.js b/chrome/browser/resources/print_preview/new/destination_settings.js
index 0fb6bd5..25c2248 100644
--- a/chrome/browser/resources/print_preview/new/destination_settings.js
+++ b/chrome/browser/resources/print_preview/new/destination_settings.js
@@ -31,6 +31,11 @@
     /** @type {!print_preview_new.State} */
     state: Number,
 
+    noDestinationsFound: {
+      type: Boolean,
+      value: false,
+    },
+
     /** @private {boolean} */
     showCloudPrintPromo_: {
       type: Boolean,
@@ -58,8 +63,8 @@
    * @private
    */
   shouldDisableButton_: function() {
-    return !this.destinationStore ||
-        (this.disabled && this.state != print_preview_new.State.NOT_READY &&
+    return !this.destinationStore || this.noDestinationsFound ||
+        (this.disabled &&
          this.state != print_preview_new.State.INVALID_PRINTER);
   },
 
@@ -69,6 +74,14 @@
   },
 
   /**
+   * @return {boolean} Whether to show the spinner.
+   * @private
+   */
+  shouldShowSpinner_: function() {
+    return this.loadingDestination_ && !this.noDestinationsFound;
+  },
+
+  /**
    * @return {string} The connection status text to display.
    * @private
    */
diff --git a/chrome/browser/resources/print_preview/new/preview_area.js b/chrome/browser/resources/print_preview/new/preview_area.js
index 8a53460..9b9d0ec2 100644
--- a/chrome/browser/resources/print_preview/new/preview_area.js
+++ b/chrome/browser/resources/print_preview/new/preview_area.js
@@ -22,6 +22,9 @@
   INVALID_SETTINGS: 'invalid-settings',
   PREVIEW_FAILED: 'preview-failed',
   UNSUPPORTED_CLOUD_PRINTER: 'unsupported-cloud-printer',
+  // <if expr="chromeos">
+  NO_DESTINATIONS_FOUND: 'no-destinations-found',
+  // </if>
 };
 
 Polymer({
@@ -260,15 +263,15 @@
   currentMessage_: function() {
     switch (this.previewState) {
       case print_preview_new.PreviewAreaState.NO_PLUGIN:
-        return this.i18nAdvanced('noPlugin');
+        return this.i18n('noPlugin');
       case print_preview_new.PreviewAreaState.LOADING:
-        return this.i18nAdvanced('loading');
+        return this.i18n('loading');
       case print_preview_new.PreviewAreaState.DISPLAY_PREVIEW:
         return '';
       // <if expr="is_macosx">
       case print_preview_new.PreviewAreaState.OPEN_IN_PREVIEW_LOADING:
       case print_preview_new.PreviewAreaState.OPEN_IN_PREVIEW_LOADED:
-        return this.i18nAdvanced('openingPDFInPreview');
+        return this.i18n('openingPDFInPreview');
       // </if>
       case print_preview_new.PreviewAreaState.INVALID_SETTINGS:
         return this.i18nAdvanced('invalidPrinterSettings', {
@@ -276,12 +279,16 @@
           tags: ['BR'],
         });
       case print_preview_new.PreviewAreaState.PREVIEW_FAILED:
-        return this.i18nAdvanced('previewFailed');
+        return this.i18n('previewFailed');
       case print_preview_new.PreviewAreaState.UNSUPPORTED_CLOUD_PRINTER:
         return this.i18nAdvanced('unsupportedCloudPrinter', {
           substitutions: [],
           tags: ['BR'],
         });
+      // <if expr="chromeos">
+      case print_preview_new.PreviewAreaState.NO_DESTINATIONS_FOUND:
+        return this.i18n('noDestinationsMessage');
+      // </if>
       default:
         return '';
     }
@@ -317,6 +324,13 @@
     }
   },
 
+  // <if expr="chromeos">
+  setNoDestinationsFound: function() {
+    this.previewState =
+        print_preview_new.PreviewAreaState.NO_DESTINATIONS_FOUND;
+  },
+  // </if>
+
   // <if expr="is_macosx">
   /** Set the preview state to display the "opening in preview" message. */
   setOpeningPdfInPreview: function() {
diff --git a/chrome/browser/resources/settings/focus_row_behavior.js b/chrome/browser/resources/settings/focus_row_behavior.js
index 22afc7c..94d9d73 100644
--- a/chrome/browser/resources/settings/focus_row_behavior.js
+++ b/chrome/browser/resources/settings/focus_row_behavior.js
@@ -221,15 +221,24 @@
 
   /**
    * This function gets called when the row itself receives the focus event.
+   * @param {!Event} e The focus event
    * @private
    */
-  onFocus_: function() {
+  onFocus_: function(e) {
     if (this.mouseFocused_) {
       this.mouseFocused_ = false;  // Consume and reset flag.
       return;
     }
 
-    if (this.lastFocused && !this.blurred_) {
+    // If focus is being restored from outside the item and the event is fired
+    // by the list item itself, focus the first control so that the user can tab
+    // through all the controls. When the user shift-tabs back to the row, or
+    // focus is restored to the row from a dropdown on the last item, the last
+    // child item will be focused before the row itself. Since this is the
+    // desired behavior, do not shift focus to the first item in these cases.
+    const restoreFocusToFirst = this.blurred_ && e.composedPath()[0] === this;
+
+    if (this.lastFocused && !restoreFocusToFirst) {
       this.row_.getEquivalentElement(this.lastFocused).focus();
     } else {
       const firstFocusable = assert(this.firstControl_);
diff --git "a/chrome/browser/speech/\043tts_win.cc\043" "b/chrome/browser/speech/\043tts_win.cc\043"
new file mode 100644
index 0000000..2f5a820
--- /dev/null
+++ "b/chrome/browser/speech/\043tts_win.cc\043"
@@ -0,0 +1,335 @@
+// Copyright (c) 2012 The Chromium 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 <math.h>
+#include <objbase.h>
+#include <sapi.h>
+#include <stdint.h>
+#include <wrl/client.h>
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/sphelper.h"
+#include "chrome/browser/speech/tts_platform_impl.h"
+#include "content/public/browser/tts_controller.h"
+
+namespace {
+
+// ISpObjectToken key and value names.
+const wchar_t kAttributesKey[] = L"Attributes";
+const wchar_t kLanguageValue[] = L"Language";
+
+}  // anonymous namespace.
+
+// TODO(katie): Move to content/browser/speech.
+class TtsPlatformImplWin : public TtsPlatformImpl {
+ public:
+  bool PlatformImplAvailable() override {
+    return true;
+  }
+
+  bool Speak(int utterance_id,
+             const std::string& utterance,
+             const std::string& lang,
+             const content::VoiceData& voice,
+             const content::UtteranceContinuousParameters& params) override;
+
+  bool StopSpeaking() override;
+
+  void Pause() override;
+
+  void Resume() override;
+
+  bool IsSpeaking() override;
+
+  void GetVoices(std::vector<content::VoiceData>* out_voices) override;
+
+  // Get the single instance of this class.
+  static TtsPlatformImplWin* GetInstance();
+
+  static void __stdcall SpeechEventCallback(WPARAM w_param, LPARAM l_param);
+
+ private:
+  TtsPlatformImplWin();
+  ~TtsPlatformImplWin() override {}
+
+  void OnSpeechEvent();
+
+  void SetVoiceFromName(const std::string& name);
+
+  Microsoft::WRL::ComPtr<ISpVoice> speech_synthesizer_;
+
+  // These apply to the current utterance only.
+  std::wstring utterance_;
+  int utterance_id_;
+  int prefix_len_;
+  ULONG stream_number_;
+  int char_position_;
+  bool paused_;
+  std::string last_voice_name_;
+
+  friend struct base::DefaultSingletonTraits<TtsPlatformImplWin>;
+
+  DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin);
+};
+
+// static
+TtsPlatform* TtsPlatform::GetInstance() {
+  return TtsPlatformImplWin::GetInstance();
+}
+
+bool TtsPlatformImplWin::Speak(
+    int utterance_id,
+    const std::string& src_utterance,
+    const std::string& lang,
+    const content::VoiceData& voice,
+    const content::UtteranceContinuousParameters& params) {
+  std::wstring prefix;
+  std::wstring suffix;
+
+  if (!speech_synthesizer_.Get())
+    return false;
+
+  SetVoiceFromName(voice.name);
+
+  if (params.rate >= 0.0) {
+    // Map our multiplicative range of 0.1x to 10.0x onto Microsoft's
+    // linear range of -10 to 10:
+    //   0.1 -> -10
+    //   1.0 -> 0
+    //  10.0 -> 10
+    speech_synthesizer_->SetRate(static_cast<int32_t>(10 * log10(params.rate)));
+  }
+
+  if (params.pitch >= 0.0) {
+    // The TTS api allows a range of -10 to 10 for speech pitch.
+    // TODO(dtseng): cleanup if we ever use any other properties that
+    // require xml.
+    std::wstring pitch_value =
+        base::IntToString16(static_cast<int>(params.pitch * 10 - 10));
+    prefix = L"<pitch absmiddle=\"" + pitch_value + L"\">";
+    suffix = L"</pitch>";
+  }
+
+  if (params.volume >= 0.0) {
+    // The TTS api allows a range of 0 to 100 for speech volume.
+    speech_synthesizer_->SetVolume(static_cast<uint16_t>(params.volume * 100));
+  }
+
+  // TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072
+
+  utterance_ = base::UTF8ToWide(src_utterance);
+  utterance_id_ = utterance_id;
+  char_position_ = 0;
+  std::wstring merged_utterance = prefix + utterance_ + suffix;
+  prefix_len_ = prefix.size();
+
+  HRESULT result = speech_synthesizer_->Speak(
+      merged_utterance.c_str(),
+      SPF_ASYNC,
+      &stream_number_);
+  return (result == S_OK);
+}
+
+bool TtsPlatformImplWin::StopSpeaking() {
+  if (speech_synthesizer_.Get()) {
+    // Clear the stream number so that any further events relating to this
+    // utterance are ignored.
+    stream_number_ = 0;
+
+    if (IsSpeaking()) {
+      // Stop speech by speaking the empty string with the purge flag.
+      speech_synthesizer_->Speak(L"", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
+    }
+    if (paused_) {
+      speech_synthesizer_->Resume();
+      paused_ = false;
+    }
+  }
+  return true;
+}
+
+void TtsPlatformImplWin::Pause() {
+  if (speech_synthesizer_.Get() && utterance_id_ && !paused_) {
+    speech_synthesizer_->Pause();
+    paused_ = true;
+    content::TtsController::GetInstance()->OnTtsEvent(
+        utterance_id_, content::TTS_EVENT_PAUSE, char_position_, "");
+  }
+}
+
+void TtsPlatformImplWin::Resume() {
+  if (speech_synthesizer_.Get() && utterance_id_ && paused_) {
+    speech_synthesizer_->Resume();
+    paused_ = false;
+    content::TtsController::GetInstance()->OnTtsEvent(
+        utterance_id_, content::TTS_EVENT_RESUME, char_position_, "");
+  }
+}
+
+bool TtsPlatformImplWin::IsSpeaking() {
+  if (speech_synthesizer_.Get()) {
+    SPVOICESTATUS status;
+    HRESULT result = speech_synthesizer_->GetStatus(&status, NULL);
+    if (result == S_OK) {
+      if (status.dwRunningState == 0 ||  // 0 == waiting to speak
+          status.dwRunningState == SPRS_IS_SPEAKING) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+void TtsPlatformImplWin::GetVoices(
+    std::vector<content::VoiceData>* out_voices) {
+  Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
+  unsigned long voice_count;
+  if (S_OK !=
+      SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
+    return;
+  if (S_OK != voice_tokens->GetCount(&voice_count))
+    return;
+
+  for (unsigned i = 0; i < voice_count; i++) {
+    content::VoiceData voice;
+
+    Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
+    if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
+      return;
+
+    base::win::ScopedCoMem<WCHAR> description;
+    if (S_OK != SpGetDescription(voice_token.Get(), &description))
+      continue;
+    voice.name = base::WideToUTF8(description.get());
+
+    Microsoft::WRL::ComPtr<ISpDataKey> attributes;
+    if (S_OK != voice_token->OpenKey(kAttributesKey, attributes.GetAddressOf()))
+      continue;
+
+    base::win::ScopedCoMem<WCHAR> language;
+    if (S_OK == attributes->GetStringValue(kLanguageValue, &language)) {
+      int lcid_value;
+      base::HexStringToInt(base::WideToUTF8(language.get()), &lcid_value);
+      LCID lcid = MAKELCID(lcid_value, SORT_DEFAULT);
+      WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0};
+      LCIDToLocaleName(lcid, locale_name, LOCALE_NAME_MAX_LENGTH, 0);
+      voice.lang = base::WideToUTF8(locale_name);
+    }
+
+    voice.native = true;
+    voice.events.insert(content::TTS_EVENT_START);
+    voice.events.insert(content::TTS_EVENT_END);
+    voice.events.insert(content::TTS_EVENT_MARKER);
+    voice.events.insert(content::TTS_EVENT_WORD);
+    voice.events.insert(content::TTS_EVENT_SENTENCE);
+    voice.events.insert(content::TTS_EVENT_PAUSE);
+    voice.events.insert(content::TTS_EVENT_RESUME);
+    out_voices->push_back(voice);
+  }
+}
+
+void TtsPlatformImplWin::OnSpeechEvent() {
+  content::TtsController* controller = content::TtsController::GetInstance();
+  SPEVENT event;
+  while (S_OK == speech_synthesizer_->GetEvents(1, &event, NULL)) {
+    if (event.ulStreamNum != stream_number_)
+      continue;
+
+    switch (event.eEventId) {
+    case SPEI_START_INPUT_STREAM:
+      controller->OnTtsEvent(utterance_id_, content::TTS_EVENT_START, 0,
+                             std::string());
+      break;
+    case SPEI_END_INPUT_STREAM:
+      char_position_ = utterance_.size();
+      controller->OnTtsEvent(utterance_id_, content::TTS_EVENT_END,
+                             char_position_, std::string());
+      break;
+    case SPEI_TTS_BOOKMARK:
+      controller->OnTtsEvent(utterance_id_, content::TTS_EVENT_MARKER,
+                             char_position_, std::string());
+      break;
+    case SPEI_WORD_BOUNDARY:
+      char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
+      controller->OnTtsEvent(utterance_id_, content::TTS_EVENT_WORD,
+                             char_position_, std::string());
+      break;
+    case SPEI_SENTENCE_BOUNDARY:
+      char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
+      controller->OnTtsEvent(utterance_id_, content::TTS_EVENT_SENTENCE,
+                             char_position_, std::string());
+      break;
+    default:
+      break;
+    }
+  }
+}
+
+void TtsPlatformImplWin::SetVoiceFromName(const std::string& name) {
+  if (name.empty() || name == last_voice_name_)
+    return;
+
+  last_voice_name_ = name;
+
+  Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
+  unsigned long voice_count;
+  if (S_OK !=
+      SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
+    return;
+  if (S_OK != voice_tokens->GetCount(&voice_count))
+    return;
+
+  for (unsigned i = 0; i < voice_count; i++) {
+    Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
+    if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
+      return;
+
+    base::win::ScopedCoMem<WCHAR> description;
+    if (S_OK != SpGetDescription(voice_token.Get(), &description))
+      continue;
+    if (name == base::WideToUTF8(description.get())) {
+      speech_synthesizer_->SetVoice(voice_token.Get());
+      break;
+    }
+  }
+}
+
+TtsPlatformImplWin::TtsPlatformImplWin()
+  : utterance_id_(0),
+    prefix_len_(0),
+    stream_number_(0),
+    char_position_(0),
+    paused_(false) {
+  ::CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL,
+                     IID_PPV_ARGS(&speech_synthesizer_));
+  if (speech_synthesizer_.Get()) {
+    ULONGLONG event_mask =
+        SPFEI(SPEI_START_INPUT_STREAM) |
+        SPFEI(SPEI_TTS_BOOKMARK) |
+        SPFEI(SPEI_WORD_BOUNDARY) |
+        SPFEI(SPEI_SENTENCE_BOUNDARY) |
+        SPFEI(SPEI_END_INPUT_STREAM);
+    speech_synthesizer_->SetInterest(event_mask, event_mask);
+    speech_synthesizer_->SetNotifyCallbackFunction(
+        TtsPlatformImplWin::SpeechEventCallback, 0, 0);
+  }
+}
+
+// static
+TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() {
+  return base::Singleton<TtsPlatformImplWin,
+                         base::LeakySingletonTraits<TtsPlatformImplWin>>::get();
+}
+
+// static
+void TtsPlatformImplWin::SpeechEventCallback(
+    WPARAM w_param, LPARAM l_param) {
+  GetInstance()->OnSpeechEvent();
+}
diff --git a/chrome/browser/speech/extension_api/tts_extension_apitest.cc b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
index ecbf97d..9caf91c 100644
--- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
@@ -48,7 +48,7 @@
 
 namespace extensions {
 
-class MockTtsPlatformImpl : public TtsPlatformImpl {
+class MockTtsPlatformImpl : public TtsPlatform {
  public:
   MockTtsPlatformImpl()
       : should_fake_get_voices_(false),
@@ -56,6 +56,21 @@
 
   bool PlatformImplAvailable() override { return true; }
 
+  void WillSpeakUtteranceWithVoice(
+      const content::Utterance* utterance,
+      const content::VoiceData& voice_data) override {}
+
+  bool LoadBuiltInTtsExtension(
+      content::BrowserContext* browser_context) override {
+    return false;
+  }
+
+  void ClearError() override { error_ = ""; }
+
+  void SetError(const std::string& error) override { error_ = error; }
+
+  std::string GetError() override { return error_; }
+
   MOCK_METHOD5(Speak,
                bool(int utterance_id,
                     const std::string& utterance,
@@ -87,9 +102,7 @@
 
   void set_should_fake_get_voices(bool val) { should_fake_get_voices_ = val; }
 
-  void SetErrorToEpicFail() {
-    set_error("epic fail");
-  }
+  void SetErrorToEpicFail() { SetError("epic fail"); }
 
   void SendEndEventOnSavedUtteranceId() {
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
@@ -166,6 +179,7 @@
 
  private:
   bool should_fake_get_voices_;
+  std::string error_;
   base::WeakPtrFactory<MockTtsPlatformImpl> ptr_factory_;
 };
 
@@ -215,7 +229,7 @@
  public:
   void SetUpInProcessBrowserTestFixture() override {
     ExtensionApiTest::SetUpInProcessBrowserTestFixture();
-    TtsControllerDelegateImpl::GetInstance()->SetPlatformImpl(
+    TtsControllerDelegateImpl::GetInstance()->SetTtsPlatform(
         &mock_platform_impl_);
   }
 
diff --git a/chrome/browser/speech/tts_android.cc b/chrome/browser/speech/tts_android.cc
index e801e90a..04ffb344 100644
--- a/chrome/browser/speech/tts_android.cc
+++ b/chrome/browser/speech/tts_android.cc
@@ -17,7 +17,7 @@
 using base::android::JavaParamRef;
 
 // static
-TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
+TtsPlatform* TtsPlatform::GetInstance() {
   return TtsPlatformImplAndroid::GetInstance();
 }
 
diff --git a/chrome/browser/speech/tts_android.h b/chrome/browser/speech/tts_android.h
index cec3838..81e9368 100644
--- a/chrome/browser/speech/tts_android.h
+++ b/chrome/browser/speech/tts_android.h
@@ -7,8 +7,9 @@
 
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
-#include "chrome/browser/speech/tts_platform.h"
+#include "chrome/browser/speech/tts_platform_impl.h"
 
+// TODO(katie): Move to content/browser/speech.
 class TtsPlatformImplAndroid : public TtsPlatformImpl {
  public:
   // TtsPlatformImpl implementation.
diff --git a/chrome/browser/speech/tts_chromeos.cc b/chrome/browser/speech/tts_chromeos.cc
index e1819e20..3e1469e 100644
--- a/chrome/browser/speech/tts_chromeos.cc
+++ b/chrome/browser/speech/tts_chromeos.cc
@@ -11,9 +11,9 @@
 
 // This class includes extension-based tts through LoadBuiltInTtsExtension and
 // native tts through ARC.
-class TtsPlatformImplChromeOs : public TtsPlatformImpl {
+class TtsPlatformImplChromeOs : public TtsPlatform {
  public:
-  // TtsPlatformImpl overrides:
+  // TtsPlatform overrides:
   bool PlatformImplAvailable() override {
     return arc::ArcServiceManager::Get() && arc::ArcServiceManager::Get()
                                                 ->arc_bridge_service()
@@ -76,25 +76,36 @@
     voice.events.insert(content::TTS_EVENT_END);
   }
 
+  std::string GetError() override { return error_; }
+
+  void ClearError() override { error_ = std::string(); }
+
+  void SetError(const std::string& error) override { error_ = error; }
+
   // Unimplemented.
   void Pause() override {}
   void Resume() override {}
   bool IsSpeaking() override { return false; }
+  void WillSpeakUtteranceWithVoice(
+      const content::Utterance* utterance,
+      const content::VoiceData& voice_data) override {}
 
   // Get the single instance of this class.
   static TtsPlatformImplChromeOs* GetInstance();
 
  private:
   TtsPlatformImplChromeOs() {}
-  ~TtsPlatformImplChromeOs() override {}
+  virtual ~TtsPlatformImplChromeOs() {}
 
   friend struct base::DefaultSingletonTraits<TtsPlatformImplChromeOs>;
 
+  std::string error_;
+
   DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplChromeOs);
 };
 
 // static
-TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
+TtsPlatform* TtsPlatform::GetInstance() {
   return TtsPlatformImplChromeOs::GetInstance();
 }
 
diff --git a/chrome/browser/speech/tts_controller_delegate_impl.cc b/chrome/browser/speech/tts_controller_delegate_impl.cc
index 488266f4..b7463965 100644
--- a/chrome/browser/speech/tts_controller_delegate_impl.cc
+++ b/chrome/browser/speech/tts_controller_delegate_impl.cc
@@ -80,7 +80,7 @@
 TtsControllerDelegateImpl::TtsControllerDelegateImpl()
     : current_utterance_(nullptr),
       paused_(false),
-      platform_impl_(nullptr),
+      tts_platform_(nullptr),
       tts_engine_delegate_(nullptr) {}
 
 TtsControllerDelegateImpl::~TtsControllerDelegateImpl() {
@@ -115,7 +115,7 @@
   // Ensure we have all built-in voices loaded. This is a no-op if already
   // loaded.
   bool loaded_built_in =
-      GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context());
+      GetTtsPlatform()->LoadBuiltInTtsExtension(utterance->browser_context());
 
   // Get all available voices and try to find a matching voice.
   std::vector<content::VoiceData> voices;
@@ -132,7 +132,7 @@
 
   UpdateUtteranceDefaults(utterance);
 
-  GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice);
+  GetTtsPlatform()->WillSpeakUtteranceWithVoice(utterance, voice);
 
   base::RecordAction(base::UserMetricsAction("TextToSpeech.Speak"));
   UMA_HISTOGRAM_COUNTS_100000("TextToSpeech.Utterance.TextLength",
@@ -171,10 +171,10 @@
     // It's possible for certain platforms to send start events immediately
     // during |speak|.
     current_utterance_ = utterance;
-    GetPlatformImpl()->clear_error();
-    bool success = GetPlatformImpl()->Speak(utterance->id(), utterance->text(),
-                                            utterance->lang(), voice,
-                                            utterance->continuous_parameters());
+    GetTtsPlatform()->ClearError();
+    bool success = GetTtsPlatform()->Speak(utterance->id(), utterance->text(),
+                                           utterance->lang(), voice,
+                                           utterance->continuous_parameters());
     if (!success)
       current_utterance_ = nullptr;
 
@@ -187,7 +187,7 @@
 
     if (!success) {
       utterance->OnTtsEvent(content::TTS_EVENT_ERROR, kInvalidCharIndex,
-                            GetPlatformImpl()->error());
+                            GetTtsPlatform()->GetError());
       delete utterance;
       return;
     }
@@ -202,8 +202,8 @@
     if (tts_engine_delegate_)
       tts_engine_delegate_->Stop(current_utterance_);
   } else {
-    GetPlatformImpl()->clear_error();
-    GetPlatformImpl()->StopSpeaking();
+    GetTtsPlatform()->ClearError();
+    GetTtsPlatform()->StopSpeaking();
   }
 
   if (current_utterance_)
@@ -221,8 +221,8 @@
     if (tts_engine_delegate_)
       tts_engine_delegate_->Pause(current_utterance_);
   } else if (current_utterance_) {
-    GetPlatformImpl()->clear_error();
-    GetPlatformImpl()->Pause();
+    GetTtsPlatform()->ClearError();
+    GetTtsPlatform()->Pause();
   }
 }
 
@@ -234,8 +234,8 @@
     if (tts_engine_delegate_)
       tts_engine_delegate_->Resume(current_utterance_);
   } else if (current_utterance_) {
-    GetPlatformImpl()->clear_error();
-    GetPlatformImpl()->Resume();
+    GetTtsPlatform()->ClearError();
+    GetTtsPlatform()->Resume();
   } else {
     SpeakNextUtterance();
   }
@@ -302,13 +302,13 @@
 void TtsControllerDelegateImpl::GetVoices(
     content::BrowserContext* browser_context,
     std::vector<content::VoiceData>* out_voices) {
-  TtsPlatformImpl* platform_impl = GetPlatformImpl();
-  if (platform_impl) {
+  TtsPlatform* tts_platform = GetTtsPlatform();
+  if (tts_platform) {
     // Ensure we have all built-in voices loaded. This is a no-op if already
     // loaded.
-    platform_impl->LoadBuiltInTtsExtension(browser_context);
-    if (platform_impl->PlatformImplAvailable())
-      platform_impl->GetVoices(out_voices);
+    tts_platform->LoadBuiltInTtsExtension(browser_context);
+    if (tts_platform->PlatformImplAvailable())
+      tts_platform->GetVoices(out_voices);
   }
 
   if (browser_context && tts_engine_delegate_)
@@ -316,7 +316,7 @@
 }
 
 bool TtsControllerDelegateImpl::IsSpeaking() {
-  return current_utterance_ != nullptr || GetPlatformImpl()->IsSpeaking();
+  return current_utterance_ != nullptr || GetTtsPlatform()->IsSpeaking();
 }
 
 void TtsControllerDelegateImpl::FinishCurrentUtterance() {
@@ -355,19 +355,18 @@
   }
 }
 
-void TtsControllerDelegateImpl::SetPlatformImpl(
-    TtsPlatformImpl* platform_impl) {
-  platform_impl_ = platform_impl;
+void TtsControllerDelegateImpl::SetTtsPlatform(TtsPlatform* tts_platform) {
+  tts_platform_ = tts_platform;
 }
 
 int TtsControllerDelegateImpl::QueueSize() {
   return static_cast<int>(utterance_queue_.size());
 }
 
-TtsPlatformImpl* TtsControllerDelegateImpl::GetPlatformImpl() {
-  if (!platform_impl_)
-    platform_impl_ = TtsPlatformImpl::GetInstance();
-  return platform_impl_;
+TtsPlatform* TtsControllerDelegateImpl::GetTtsPlatform() {
+  if (!tts_platform_)
+    tts_platform_ = TtsPlatform::GetInstance();
+  return tts_platform_;
 }
 
 int TtsControllerDelegateImpl::GetMatchingVoice(
@@ -546,7 +545,7 @@
 void TtsControllerDelegateImpl::VoicesChanged() {
   // Existence of platform tts indicates explicit requests to tts. Since
   // |VoicesChanged| can occur implicitly, only send if needed.
-  if (!GetPlatformImpl())
+  if (!GetTtsPlatform())
     return;
 
   for (auto& delegate : voices_changed_delegates_)
@@ -583,8 +582,8 @@
       if (tts_engine_delegate_)
         tts_engine_delegate_->Stop(current_utterance_);
     } else {
-      GetPlatformImpl()->clear_error();
-      GetPlatformImpl()->StopSpeaking();
+      GetTtsPlatform()->ClearError();
+      GetTtsPlatform()->StopSpeaking();
     }
 
     FinishCurrentUtterance();
diff --git a/chrome/browser/speech/tts_controller_delegate_impl.h b/chrome/browser/speech/tts_controller_delegate_impl.h
index 676ec8e2..8ea03d9 100644
--- a/chrome/browser/speech/tts_controller_delegate_impl.h
+++ b/chrome/browser/speech/tts_controller_delegate_impl.h
@@ -49,7 +49,7 @@
   void SetTtsEngineDelegate(content::TtsEngineDelegate* delegate) override;
   content::TtsEngineDelegate* GetTtsEngineDelegate() override;
 
-  void SetPlatformImpl(TtsPlatformImpl* platform_impl);
+  void SetTtsPlatform(TtsPlatform* tts_platform);
   int QueueSize();
 
  protected:
@@ -63,7 +63,7 @@
                            TestTtsControllerUtteranceDefaults);
 
   // Get the platform TTS implementation (or injected mock).
-  TtsPlatformImpl* GetPlatformImpl();
+  TtsPlatform* GetTtsPlatform();
 
   // Start speaking the given utterance. Will either take ownership of
   // |utterance| or delete it if there's an error. Returns true on success.
@@ -108,7 +108,7 @@
 
   // A pointer to the platform implementation of text-to-speech, for
   // dependency injection.
-  TtsPlatformImpl* platform_impl_;
+  TtsPlatform* tts_platform_;
 
   // The delegate that processes TTS requests with user-installed extensions.
   content::TtsEngineDelegate* tts_engine_delegate_;
diff --git a/chrome/browser/speech/tts_controller_unittest.cc b/chrome/browser/speech/tts_controller_unittest.cc
index 2a09982..f09c8f1 100644
--- a/chrome/browser/speech/tts_controller_unittest.cc
+++ b/chrome/browser/speech/tts_controller_unittest.cc
@@ -19,10 +19,10 @@
 };
 
 // Platform Tts implementation that does nothing.
-class DummyTtsPlatformImpl : public TtsPlatformImpl {
+class DummyTtsPlatformImpl : public TtsPlatform {
  public:
   DummyTtsPlatformImpl() {}
-  ~DummyTtsPlatformImpl() override {}
+  virtual ~DummyTtsPlatformImpl() {}
   bool PlatformImplAvailable() override { return true; }
   bool Speak(int utterance_id,
              const std::string& utterance,
@@ -36,9 +36,16 @@
   void Pause() override {}
   void Resume() override {}
   void GetVoices(std::vector<content::VoiceData>* out_voices) override {}
-  std::string error() override { return std::string(); }
-  void clear_error() override {}
-  void set_error(const std::string& error) override {}
+  bool LoadBuiltInTtsExtension(
+      content::BrowserContext* browser_context) override {
+    return false;
+  }
+  void WillSpeakUtteranceWithVoice(
+      const content::Utterance* utterance,
+      const content::VoiceData& voice_data) override {}
+  void SetError(const std::string& error) override {}
+  std::string GetError() override { return std::string(); }
+  void ClearError() override {}
 };
 
 // Subclass of TtsController with a public ctor and dtor.
@@ -61,7 +68,7 @@
   TestableTtsController* controller =
       new TestableTtsController();
 
-  controller->SetPlatformImpl(&platform_impl);
+  controller->SetTtsPlatform(&platform_impl);
 
   content::Utterance* utterance1 = new content::Utterance(nullptr);
   utterance1->set_can_enqueue(true);
diff --git a/chrome/browser/speech/tts_linux.cc b/chrome/browser/speech/tts_linux.cc
index cfe920f..f35e974 100644
--- a/chrome/browser/speech/tts_linux.cc
+++ b/chrome/browser/speech/tts_linux.cc
@@ -14,7 +14,7 @@
 #include "base/memory/singleton.h"
 #include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
-#include "chrome/browser/speech/tts_platform.h"
+#include "chrome/browser/speech/tts_platform_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
@@ -34,6 +34,7 @@
 
 }  // namespace
 
+// TODO(katie): Move to content/browser/speech.
 class TtsPlatformImplLinux : public TtsPlatformImpl {
  public:
   bool PlatformImplAvailable() override;
@@ -355,6 +356,6 @@
 }
 
 // static
-TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
+TtsPlatform* TtsPlatform::GetInstance() {
   return TtsPlatformImplLinux::GetInstance();
 }
diff --git a/chrome/browser/speech/tts_mac.mm b/chrome/browser/speech/tts_mac.mm
index c26cc77..9859905d 100644
--- a/chrome/browser/speech/tts_mac.mm
+++ b/chrome/browser/speech/tts_mac.mm
@@ -9,7 +9,7 @@
 #include "base/memory/singleton.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
-#include "chrome/browser/speech/tts_platform.h"
+#include "chrome/browser/speech/tts_platform_impl.h"
 #include "content/public/browser/tts_controller.h"
 
 #import <Cocoa/Cocoa.h>
@@ -48,6 +48,7 @@
 
 @end
 
+// TODO(katie): Move to content/browser/speech.
 class TtsPlatformImplMac : public TtsPlatformImpl {
  public:
   bool PlatformImplAvailable() override { return true; }
@@ -95,7 +96,7 @@
 };
 
 // static
-TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
+TtsPlatform* TtsPlatform::GetInstance() {
   return TtsPlatformImplMac::GetInstance();
 }
 
diff --git a/chrome/browser/speech/tts_platform.h b/chrome/browser/speech/tts_platform.h
index 341026ab..2c86f5d 100644
--- a/chrome/browser/speech/tts_platform.h
+++ b/chrome/browser/speech/tts_platform.h
@@ -15,9 +15,9 @@
 // TODO(katie): Move to content/public/browser and most implementations into
 // content/browser/speech. The tts_chromeos.cc implementation may need to remain
 // in chrome/ due to ARC++ dependencies.
-class TtsPlatformImpl {
+class TtsPlatform {
  public:
-  static TtsPlatformImpl* GetInstance();
+  static TtsPlatform* GetInstance();
 
   // Returns true if this platform implementation is supported and available.
   virtual bool PlatformImplAvailable() = 0;
@@ -28,7 +28,7 @@
   // Will call TtsController::RetrySpeakingQueuedUtterances when
   // the extension finishes loading.
   virtual bool LoadBuiltInTtsExtension(
-      content::BrowserContext* browser_context);
+      content::BrowserContext* browser_context) = 0;
 
   // Speak the given utterance with the given parameters if possible,
   // and return true on success. Utterance will always be nonempty.
@@ -64,22 +64,11 @@
   // for each one.
   virtual void WillSpeakUtteranceWithVoice(
       const content::Utterance* utterance,
-      const content::VoiceData& voice_data);
+      const content::VoiceData& voice_data) = 0;
 
-  virtual std::string error();
-  virtual void clear_error();
-  virtual void set_error(const std::string& error);
-
- protected:
-  TtsPlatformImpl() {}
-
-  // On some platforms this may be a leaky singleton - do not rely on the
-  // destructor being called!  http://crbug.com/122026
-  virtual ~TtsPlatformImpl() {}
-
-  std::string error_;
-
-  DISALLOW_COPY_AND_ASSIGN(TtsPlatformImpl);
+  virtual std::string GetError() = 0;
+  virtual void ClearError() = 0;
+  virtual void SetError(const std::string& error) = 0;
 };
 
 #endif  // CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
diff --git a/chrome/browser/speech/tts_platform_fuzzer.cc b/chrome/browser/speech/tts_platform_fuzzer.cc
index 71f0dca..38ec9e5 100644
--- a/chrome/browser/speech/tts_platform_fuzzer.cc
+++ b/chrome/browser/speech/tts_platform_fuzzer.cc
@@ -88,7 +88,7 @@
   while (i < size)
     utterance.append(1, data[i++]);
 
-  TtsPlatformImpl* tts = TtsPlatformImpl::GetInstance();
+  TtsPlatform* tts = TtsPlatform::GetInstance();
   CHECK(tts->PlatformImplAvailable());
 
   VLOG(1) << "id=" << utterance_id << " lang='" << lang << "'"
diff --git a/chrome/browser/speech/tts_platform.cc b/chrome/browser/speech/tts_platform_impl.cc
similarity index 63%
rename from chrome/browser/speech/tts_platform.cc
rename to chrome/browser/speech/tts_platform_impl.cc
index c2b67007..725e55a 100644
--- a/chrome/browser/speech/tts_platform.cc
+++ b/chrome/browser/speech/tts_platform_impl.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/speech/tts_platform.h"
+#include "chrome/browser/speech/tts_platform_impl.h"
 
 #include <string>
 
@@ -11,18 +11,18 @@
   return false;
 }
 
-std::string TtsPlatformImpl::error() {
-  return error_;
-}
-
-void TtsPlatformImpl::clear_error() {
-  error_ = std::string();
-}
-
-void TtsPlatformImpl::set_error(const std::string& error) {
-  error_ = error;
-}
-
 void TtsPlatformImpl::WillSpeakUtteranceWithVoice(
     const content::Utterance* utterance,
     const content::VoiceData& voice_data) {}
+
+std::string TtsPlatformImpl::GetError() {
+  return error_;
+}
+
+void TtsPlatformImpl::ClearError() {
+  error_ = std::string();
+}
+
+void TtsPlatformImpl::SetError(const std::string& error) {
+  error_ = error;
+}
diff --git a/chrome/browser/speech/tts_platform_impl.h b/chrome/browser/speech/tts_platform_impl.h
new file mode 100644
index 0000000..f63e026
--- /dev/null
+++ b/chrome/browser/speech/tts_platform_impl.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPEECH_TTS_PLATFORM_IMPL_H_
+#define CHROME_BROWSER_SPEECH_TTS_PLATFORM_IMPL_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/speech/tts_platform.h"
+#include "content/public/browser/tts_controller.h"
+
+// Abstract platform implementation.
+// TODO(katie): Move to content/browser/speech.
+class TtsPlatformImpl : public TtsPlatform {
+ public:
+  // TtsPlatform overrides.
+  bool LoadBuiltInTtsExtension(
+      content::BrowserContext* browser_context) override;
+  void WillSpeakUtteranceWithVoice(
+      const content::Utterance* utterance,
+      const content::VoiceData& voice_data) override;
+  std::string GetError() override;
+  void ClearError() override;
+  void SetError(const std::string& error) override;
+
+ protected:
+  TtsPlatformImpl() {}
+
+  // On some platforms this may be a leaky singleton - do not rely on the
+  // destructor being called!  http://crbug.com/122026
+  virtual ~TtsPlatformImpl() {}
+
+  std::string error_;
+
+  DISALLOW_COPY_AND_ASSIGN(TtsPlatformImpl);
+};
+
+#endif  // CHROME_BROWSER_SPEECH_TTS_PLATFORM_IMPL_H_
diff --git a/chrome/browser/speech/tts_win.cc b/chrome/browser/speech/tts_win.cc
index 36efc695..2f5a820 100644
--- a/chrome/browser/speech/tts_win.cc
+++ b/chrome/browser/speech/tts_win.cc
@@ -16,7 +16,7 @@
 #include "base/values.h"
 #include "base/win/scoped_co_mem.h"
 #include "base/win/sphelper.h"
-#include "chrome/browser/speech/tts_platform.h"
+#include "chrome/browser/speech/tts_platform_impl.h"
 #include "content/public/browser/tts_controller.h"
 
 namespace {
@@ -27,6 +27,7 @@
 
 }  // anonymous namespace.
 
+// TODO(katie): Move to content/browser/speech.
 class TtsPlatformImplWin : public TtsPlatformImpl {
  public:
   bool PlatformImplAvailable() override {
@@ -79,7 +80,7 @@
 };
 
 // static
-TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
+TtsPlatform* TtsPlatform::GetInstance() {
   return TtsPlatformImplWin::GetInstance();
 }
 
diff --git a/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript_test.mm b/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript_test.mm
index a11054a1..d276898 100644
--- a/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript_test.mm
+++ b/chrome/browser/ui/cocoa/applescript/browsercrapplication+applescript_test.mm
@@ -99,9 +99,7 @@
 }
 
 // Bookmark folders at the root level.
-// http://code.google.com/p/chromium/issues/detail?id=84299
-IN_PROC_BROWSER_TEST_F(BrowserCrApplicationAppleScriptTest,
-                       DISABLED_BookmarkFolders) {
+IN_PROC_BROWSER_TEST_F(BrowserCrApplicationAppleScriptTest, BookmarkFolders) {
   NSArray* bookmarkFolders = [NSApp bookmarkFolders];
   EXPECT_EQ(2U, [bookmarkFolders count]);
 
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.cc b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.cc
index 83f316eb..75f8cda4 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.cc
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.cc
@@ -50,10 +50,6 @@
   trigger_.NewTabOpened();
 }
 
-void ReopenTabInProductHelp::OmniboxFocused() {
-  trigger_.OmniboxFocused();
-}
-
 void ReopenTabInProductHelp::TabReopened() {
   GetTracker()->NotifyEvent(feature_engagement::events::kTabReopened);
 }
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h
index a18da10..473ae620 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h
@@ -35,12 +35,9 @@
   ReopenTabInProductHelp(Profile* profile, const base::TickClock* clock);
   ~ReopenTabInProductHelp() override;
 
-  // Should be called when the user opens a blank new tab.
-  void NewTabOpened();
-
-  // Should be called when the user focuses on the omnibox. Possibly triggers
+  // Should be called when the user opens a blank new tab. Possibly triggers
   // IPH.
-  void OmniboxFocused();
+  void NewTabOpened();
 
   // Should be called when the user reopens a previously closed tab, either
   // through CTRL+SHIFT+T or through the recent tabs menu.
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.cc b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.cc
index 48d9437..aad9b39c 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.cc
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.cc
@@ -16,9 +16,6 @@
 // static
 const base::TimeDelta ReopenTabInProductHelpTrigger::kNewTabOpenedTimeout =
     base::TimeDelta::FromSeconds(10);
-// static
-const base::TimeDelta ReopenTabInProductHelpTrigger::kOmniboxFocusedTimeout =
-    base::TimeDelta::FromSeconds(10);
 
 ReopenTabInProductHelpTrigger::ReopenTabInProductHelpTrigger(
     feature_engagement::Tracker* tracker,
@@ -27,9 +24,8 @@
   DCHECK(tracker);
   DCHECK(clock);
 
-  // Timeouts must be non-zero.
+  // Timeout must be non-zero.
   DCHECK(!kNewTabOpenedTimeout.is_zero());
-  DCHECK(!kOmniboxFocusedTimeout.is_zero());
 }
 
 ReopenTabInProductHelpTrigger::~ReopenTabInProductHelpTrigger() = default;
@@ -61,26 +57,14 @@
   const base::TimeDelta elapsed_time = clock_->NowTicks() - time_of_last_step_;
 
   if (elapsed_time < kNewTabOpenedTimeout) {
-    trigger_state_ = NEW_TAB_OPENED;
-    time_of_last_step_ = clock_->NowTicks();
-  } else {
-    ResetTriggerState();
-  }
-}
-
-void ReopenTabInProductHelpTrigger::OmniboxFocused() {
-  if (trigger_state_ != NEW_TAB_OPENED)
-    return;
-
-  const base::TimeDelta elapsed_time = clock_->NowTicks() - time_of_last_step_;
-
-  if (elapsed_time < kOmniboxFocusedTimeout) {
     tracker_->NotifyEvent(feature_engagement::events::kReopenTabConditionsMet);
     if (tracker_->ShouldTriggerHelpUI(
             feature_engagement::kIPHReopenTabFeature)) {
       DCHECK(cb_);
       cb_.Run();
     }
+  } else {
+    ResetTriggerState();
   }
 }
 
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h
index d971d8a..40ee56f 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger.h
@@ -37,13 +37,10 @@
   // Should be called when an active tab is closed.
   void ActiveTabClosed(base::TimeDelta active_duration);
 
-  // Should be called when a blank new tab is opened by user action.
+  // Should be called when a blank new tab is opened by user action. Possibly
+  // triggers IPH.
   void NewTabOpened();
 
-  // Should be called when the user focuses on the omnibox. Possibly triggers
-  // IPH.
-  void OmniboxFocused();
-
   // Must be called once after IPH finishes. Must only be called after the
   // callback is called.
   void HelpDismissed();
@@ -51,7 +48,6 @@
   // Timeout constants. Exposed for unit testing.
   static const base::TimeDelta kTabMinimumActiveDuration;
   static const base::TimeDelta kNewTabOpenedTimeout;
-  static const base::TimeDelta kOmniboxFocusedTimeout;
 
  private:
   // Sets state as if user has not performed any actions.
@@ -65,7 +61,6 @@
   enum TriggerState {
     NO_ACTIONS_SEEN,
     ACTIVE_TAB_CLOSED,
-    NEW_TAB_OPENED,
   };
 
   TriggerState trigger_state_;
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc
index a6cbdcb..33ca80c 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc
@@ -57,7 +57,6 @@
   reopen_tab_iph.ActiveTabClosed(
       ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.NewTabOpened();
-  reopen_tab_iph.OmniboxFocused();
 }
 
 TEST(ReopenTabInProductHelpTriggerTest, RespectsBackendShouldTrigger) {
@@ -77,7 +76,6 @@
   reopen_tab_iph.ActiveTabClosed(
       ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.NewTabOpened();
-  reopen_tab_iph.OmniboxFocused();
 }
 
 TEST(ReopenTabInProductHelpTriggerTest, TabNotActiveLongEnough) {
@@ -92,10 +90,9 @@
   reopen_tab_iph.ActiveTabClosed(
       ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration / 2);
   reopen_tab_iph.NewTabOpened();
-  reopen_tab_iph.OmniboxFocused();
 }
 
-TEST(ReopenTabInProductHelpTriggerTest, RespectsTimeouts) {
+TEST(ReopenTabInProductHelpTriggerTest, RespectsTimeout) {
   NiceMock<MockTracker> mock_tracker;
 
   EXPECT_CALL(mock_tracker, NotifyEvent(_)).Times(0);
@@ -111,13 +108,6 @@
       ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   clock.Advance(ReopenTabInProductHelpTrigger::kNewTabOpenedTimeout);
   reopen_tab_iph.NewTabOpened();
-  reopen_tab_iph.OmniboxFocused();
-
-  reopen_tab_iph.ActiveTabClosed(
-      ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
-  reopen_tab_iph.NewTabOpened();
-  clock.Advance(ReopenTabInProductHelpTrigger::kOmniboxFocusedTimeout);
-  reopen_tab_iph.OmniboxFocused();
 }
 
 TEST(ReopenTabInProductHelpTriggerTest, TriggersTwice) {
@@ -142,7 +132,6 @@
   reopen_tab_iph.ActiveTabClosed(
       ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.NewTabOpened();
-  reopen_tab_iph.OmniboxFocused();
 
   EXPECT_TRUE(triggered);
   triggered = false;
@@ -150,7 +139,6 @@
   reopen_tab_iph.ActiveTabClosed(
       ReopenTabInProductHelpTrigger::kTabMinimumActiveDuration);
   reopen_tab_iph.NewTabOpened();
-  reopen_tab_iph.OmniboxFocused();
 
   EXPECT_TRUE(triggered);
 }
diff --git a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_unittest.cc b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_unittest.cc
index 5d688df..528226b 100644
--- a/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_unittest.cc
+++ b/chrome/browser/ui/in_product_help/reopen_tab_in_product_help_unittest.cc
@@ -74,5 +74,4 @@
   tab_strip_model->CloseSelectedTabs();
 
   reopen_tab_iph.NewTabOpened();
-  reopen_tab_iph.OmniboxFocused();
 }
diff --git a/chrome/browser/ui/location_bar/location_bar.h b/chrome/browser/ui/location_bar/location_bar.h
index de39c740..cee5096 100644
--- a/chrome/browser/ui/location_bar/location_bar.h
+++ b/chrome/browser/ui/location_bar/location_bar.h
@@ -53,9 +53,6 @@
   // Updates the state of the images showing the content settings status.
   virtual void UpdateContentSettingsIcons() = 0;
 
-  // Updates the password icon and pops up a bubble from the icon if needed.
-  virtual void UpdateManagePasswordsIconAndBubble() = 0;
-
   // Updates the visibility and toggled state of the save credit card icon.
   virtual void UpdateSaveCreditCardIcon() = 0;
 
diff --git a/chrome/browser/ui/media_router/media_router_ui_base.cc b/chrome/browser/ui/media_router/media_router_ui_base.cc
index 791971f..301a438 100644
--- a/chrome/browser/ui/media_router/media_router_ui_base.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_base.cc
@@ -253,7 +253,7 @@
     params = GetRouteParameters(sink_id, cast_mode);
   }
   if (!params) {
-    SendIssueForUnableToCast(cast_mode);
+    SendIssueForUnableToCast(cast_mode, sink_id);
     return false;
   }
 
@@ -406,6 +406,10 @@
   }
 
   current_route_request_.reset();
+  if (result.result_code() == RouteRequestResult::TIMED_OUT) {
+    SendIssueForRouteTimeout(cast_mode, sink_id,
+                             presentation_request_source_name);
+  }
 }
 
 void MediaRouterUIBase::HandleCreateSessionRequestRouteResponse(
@@ -582,6 +586,7 @@
 
 void MediaRouterUIBase::SendIssueForRouteTimeout(
     MediaCastMode cast_mode,
+    const MediaSink::Id& sink_id,
     const base::string16& presentation_request_source_name) {
   std::string issue_title;
   switch (cast_mode) {
@@ -606,11 +611,14 @@
       break;
   }
 
-  AddIssue(IssueInfo(issue_title, IssueInfo::Action::DISMISS,
-                     IssueInfo::Severity::NOTIFICATION));
+  IssueInfo issue_info(issue_title, IssueInfo::Action::DISMISS,
+                       IssueInfo::Severity::NOTIFICATION);
+  issue_info.sink_id = sink_id;
+  AddIssue(issue_info);
 }
 
-void MediaRouterUIBase::SendIssueForUnableToCast(MediaCastMode cast_mode) {
+void MediaRouterUIBase::SendIssueForUnableToCast(MediaCastMode cast_mode,
+                                                 const MediaSink::Id& sink_id) {
   // For a generic error, claim a tab error unless it was specifically desktop
   // mirroring.
   std::string issue_title =
@@ -619,8 +627,10 @@
                 IDS_MEDIA_ROUTER_ISSUE_UNABLE_TO_CAST_DESKTOP)
           : l10n_util::GetStringUTF8(
                 IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT_FOR_TAB);
-  AddIssue(IssueInfo(issue_title, IssueInfo::Action::DISMISS,
-                     IssueInfo::Severity::WARNING));
+  IssueInfo issue_info(issue_title, IssueInfo::Action::DISMISS,
+                       IssueInfo::Severity::WARNING);
+  issue_info.sink_id = sink_id;
+  AddIssue(issue_info);
 }
 
 IssueManager* MediaRouterUIBase::GetIssueManager() {
diff --git a/chrome/browser/ui/media_router/media_router_ui_base.h b/chrome/browser/ui/media_router/media_router_ui_base.h
index 954c46b..99e2dd8 100644
--- a/chrome/browser/ui/media_router/media_router_ui_base.h
+++ b/chrome/browser/ui/media_router/media_router_ui_base.h
@@ -197,10 +197,13 @@
   // Creates and sends an issue if route creation timed out.
   void SendIssueForRouteTimeout(
       MediaCastMode cast_mode,
+      const MediaSink::Id& sink_id,
       const base::string16& presentation_request_source_name);
 
-  // Creates and sends an issue if casting fails for any other reason.
-  void SendIssueForUnableToCast(MediaCastMode cast_mode);
+  // Creates and sends an issue if casting fails for any reason other than
+  // timeout.
+  void SendIssueForUnableToCast(MediaCastMode cast_mode,
+                                const MediaSink::Id& sink_id);
 
   // Returns the IssueManager associated with |router_|.
   IssueManager* GetIssueManager();
diff --git a/chrome/browser/ui/page_action/page_action_icon_container.h b/chrome/browser/ui/page_action/page_action_icon_container.h
index 6e9af2f..68b359e2 100644
--- a/chrome/browser/ui/page_action/page_action_icon_container.h
+++ b/chrome/browser/ui/page_action/page_action_icon_container.h
@@ -9,11 +9,14 @@
   // TODO(https://crbug.com/788051): Migrate page action icon update methods out
   // of LocationBar to this interface.
   kFind,
+  kManagePasswords,
   kZoom,
 };
 
 class PageActionIconContainer {
  public:
+  virtual ~PageActionIconContainer() {}
+
   // Signals a page action icon to update its visual state if it is present in
   // the browser window.
   virtual void UpdatePageActionIcon(PageActionIconType type) = 0;
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index dbc4329..5b5c0b3 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
+#include "chrome/browser/ui/page_action/page_action_icon_container.h"
 #include "chrome/browser/ui/passwords/manage_passwords_icon_view.h"
 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/browser/ui/passwords/password_dialog_controller_impl.h"
@@ -531,9 +532,8 @@
   if (!browser)
     return;
 
-  LocationBar* location_bar = browser->window()->GetLocationBar();
-  DCHECK(location_bar);
-  location_bar->UpdateManagePasswordsIconAndBubble();
+  browser->window()->GetPageActionIconContainer()->UpdatePageActionIcon(
+      PageActionIconType::kManagePasswords);
 }
 
 AccountChooserPrompt* ManagePasswordsUIController::CreateAccountChooser(
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 82b1703a..e5fcee3 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -45,6 +45,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
+#include "chrome/browser/ui/passwords/passwords_client_ui_delegate.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
@@ -66,6 +67,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/account_id/account_id.h"
+#include "components/autofill/core/common/password_form.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "content/public/browser/render_frame_host.h"
@@ -912,6 +914,31 @@
             hosted_app_button_container_);
 }
 
+// Test that the manage passwords icon appears in the title bar for hosted app
+// windows.
+IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest,
+                       ManagePasswordsIcon) {
+  SetUpHostedApp();
+  content::WebContents* web_contents =
+      app_browser_->tab_strip_model()->GetActiveWebContents();
+  PageActionIconView* manage_passwords_icon =
+      GetPageActionIcon(PageActionIconType::kManagePasswords);
+
+  EXPECT_TRUE(manage_passwords_icon);
+  EXPECT_FALSE(manage_passwords_icon->visible());
+
+  autofill::PasswordForm password_form;
+  password_form.username_value = base::ASCIIToUTF16("test");
+  password_form.origin = GetAppURL().GetOrigin();
+  PasswordsClientUIDelegateFromWebContents(web_contents)
+      ->OnPasswordAutofilled({{password_form.username_value, &password_form}},
+                             password_form.origin, nullptr);
+  chrome::ManagePasswordsForPage(app_browser_);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(manage_passwords_icon->visible());
+}
+
 // Test that the zoom icon appears in the title bar for hosted app windows.
 IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest, ZoomIcon) {
   SetUpHostedApp();
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 42034d9..b7123a1 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -2275,7 +2275,7 @@
 }
 
 gfx::Size BrowserView::GetMinimumSize() const {
-  return GetBrowserViewLayout()->GetMinimumSize();
+  return GetBrowserViewLayout()->GetMinimumSize(this);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc
index 3869270..baba07e 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/frame/browser_view_layout.h"
 
+#include <algorithm>
+
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "build/build_config.h"
@@ -168,7 +170,7 @@
   return dialog_host_.get();
 }
 
-gfx::Size BrowserViewLayout::GetMinimumSize() {
+gfx::Size BrowserViewLayout::GetMinimumSize(const views::View* host) const {
   gfx::Size tabstrip_size(
       browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ?
       tab_strip_->GetMinimumSize() : gfx::Size());
@@ -184,7 +186,7 @@
     bookmark_bar_size.Enlarge(0, -bookmark_bar_->GetToolbarOverlap());
   }
   gfx::Size infobar_container_size(infobar_container_->GetMinimumSize());
-  // TODO: Adjust the minimum height for the find bar.
+  // TODO(pkotwicz): Adjust the minimum height for the find bar.
 
   gfx::Size contents_size(contents_container_->GetMinimumSize());
   // Prevent having a 0x0 sized-contents as this can allow the window to be
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.h b/chrome/browser/ui/views/frame/browser_view_layout.h
index 4de4cfe..42792f70 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.h
+++ b/chrome/browser/ui/views/frame/browser_view_layout.h
@@ -28,6 +28,7 @@
 
 namespace views {
 class ClientView;
+class View;
 }
 
 namespace web_modal {
@@ -66,9 +67,6 @@
 
   web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost();
 
-  // Returns the minimum size of the browser view.
-  gfx::Size GetMinimumSize();
-
   // Returns the bounding box, in widget coordinates,  for the find bar.
   gfx::Rect GetFindBarBoundingBox() const;
 
@@ -80,6 +78,7 @@
 
   // views::LayoutManager overrides:
   void Layout(views::View* host) override;
+  gfx::Size GetMinimumSize(const views::View* host) const override;
   gfx::Size GetPreferredSize(const views::View* host) const override;
 
   // Returns true if an infobar is showing.
@@ -92,6 +91,7 @@
   class WebContentsModalDialogHostViews;
 
   Browser* browser() { return browser_; }
+  const Browser* browser() const { return browser_; }
 
   // Layout the following controls, starting at |top|, returns the coordinate
   // of the bottom of the control, for laying out the next control.
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.cc b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
index af0d37232..6cbeade 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
 #include "chrome/browser/ui/content_settings/content_setting_image_model.h"
 #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
@@ -184,10 +185,12 @@
       hosted_app_origin_text_(new HostedAppOriginText(browser_view->browser())),
       content_settings_container_(new ContentSettingsContainer(this)),
       page_action_icon_container_view_(new PageActionIconContainerView(
-          {PageActionIconType::kFind, PageActionIconType::kZoom},
+          {PageActionIconType::kManagePasswords, PageActionIconType::kFind,
+           PageActionIconType::kZoom},
           GetLayoutConstant(HOSTED_APP_PAGE_ACTION_ICON_SIZE),
           HorizontalPaddingBetweenItems(),
           browser_view->browser(),
+          browser_view->browser()->command_controller(),
           this,
           nullptr)),
       browser_actions_container_(
@@ -380,6 +383,10 @@
   return this;
 }
 
+views::View* HostedAppButtonContainer::GetAnchorView() {
+  return app_menu_button_;
+}
+
 void HostedAppButtonContainer::OnWidgetVisibilityChanged(views::Widget* widget,
                                                          bool visibility) {
   if (!visibility || !pending_widget_visibility_)
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.h b/chrome/browser/ui/views/frame/hosted_app_button_container.h
index d37b89d..a014011 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.h
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.h
@@ -116,6 +116,7 @@
   gfx::Rect GetFindBarBoundingBox(int contents_height) const override;
   void FocusToolbar() override;
   views::AccessiblePaneView* GetAsAccessiblePaneView() override;
+  views::View* GetAnchorView() override;
 
   // views::WidgetObserver:
   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index d6bf4a4..7b608159 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "build/build_config.h"
 #include "build/buildflag.h"
 #include "chrome/browser/themes/theme_properties.h"
@@ -213,7 +216,7 @@
 }
 
 gfx::Size OpaqueBrowserFrameView::GetMinimumSize() const {
-  return layout_->GetMinimumSize(width());
+  return layout_->GetMinimumSize(this);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
index f73f8183..aca0c67 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
 
+#include <algorithm>
+#include <string>
+#include <vector>
+
 #include "base/command_line.h"
 #include "base/containers/adapters.h"
 #include "base/stl_util.h"
@@ -93,7 +97,7 @@
 }
 
 gfx::Size OpaqueBrowserFrameViewLayout::GetMinimumSize(
-    int available_width) const {
+    const views::View* host) const {
   gfx::Size min_size = delegate_->GetBrowserViewMinimumSize();
   int border_thickness = FrameBorderThickness(false);
   min_size.Enlarge(2 * border_thickness,
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h
index 4a3c1fca..c1130eba 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_FRAME_OPAQUE_BROWSER_FRAME_VIEW_LAYOUT_H_
 #define CHROME_BROWSER_UI_VIEWS_FRAME_OPAQUE_BROWSER_FRAME_VIEW_LAYOUT_H_
 
+#include <vector>
+
 #include "base/macros.h"
 #include "chrome/browser/ui/frame_button_display_types.h"
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
@@ -52,8 +54,6 @@
   gfx::Rect GetBoundsForTabStrip(const gfx::Size& tabstrip_preferred_size,
                                  int total_width) const;
 
-  gfx::Size GetMinimumSize(int available_width) const;
-
   // Returns the bounds of the window required to display the content area at
   // the specified bounds.
   gfx::Rect GetWindowBoundsForClientBounds(
@@ -128,6 +128,11 @@
   // Returns the extra thickness of the area above the tabs.
   int GetNonClientRestoredExtraThickness() const;
 
+  // views::LayoutManager:
+  // Called explicitly from OpaqueBrowserFrameView so we can't group it with
+  // the other overrides.
+  gfx::Size GetMinimumSize(const views::View* host) const override;
+
  protected:
   // Whether a specific button should be inserted on the leading or trailing
   // side.
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
index 655e3ba..9a7d348 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
 
+#include <memory>
+#include <utility>
+#include <vector>
+
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
@@ -266,7 +270,7 @@
     gfx::Size browser_view_min_size(delegate_->GetBrowserViewMinimumSize());
     const int min_width =
         browser_view_min_size.width() + tabstrip_min_size.width() + spacing;
-    gfx::Size min_size(layout_manager_->GetMinimumSize(kWindowWidth));
+    gfx::Size min_size(layout_manager_->GetMinimumSize(root_view_));
     EXPECT_EQ(min_width, min_size.width());
     int restored_border_height =
         2 * OpaqueBrowserFrameViewLayout::kFrameBorderThickness +
diff --git a/chrome/browser/ui/views/frame/toolbar_button_provider.h b/chrome/browser/ui/views/frame/toolbar_button_provider.h
index d34fee41..e51a36e 100644
--- a/chrome/browser/ui/views/frame/toolbar_button_provider.h
+++ b/chrome/browser/ui/views/frame/toolbar_button_provider.h
@@ -15,6 +15,7 @@
 
 namespace views {
 class AccessiblePaneView;
+class View;
 }
 
 // An interface implemented by a view contains and provides access to toolbar
@@ -41,6 +42,9 @@
   // Returns the toolbar as an AccessiblePaneView.
   virtual views::AccessiblePaneView* GetAsAccessiblePaneView() = 0;
 
+  // Returns the toolbar as an anchor point.
+  virtual views::View* GetAnchorView() = 0;
+
   // TODO(calamity): Move other buttons and button actions into here.
  protected:
   virtual ~ToolbarButtonProvider() {}
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index ed1284e..dbb9d0e 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -213,22 +213,20 @@
   }
 
   std::vector<PageActionIconType> page_action_icon_types;
+  page_action_icon_types.push_back(PageActionIconType::kManagePasswords);
   // |browser_| may be null when LocationBarView is used for non-Browser windows
   // such as PresentationReceiverWindowView, which do not support page actions.
   if (browser_) {
-    page_action_icon_types = {PageActionIconType::kFind,
-                              PageActionIconType::kZoom};
+    page_action_icon_types.push_back(PageActionIconType::kFind);
+    page_action_icon_types.push_back(PageActionIconType::kZoom);
   }
+
   page_action_icon_container_view_ = new PageActionIconContainerView(
       page_action_icon_types, GetLayoutConstant(LOCATION_BAR_ICON_SIZE), 0,
-      browser_, this, delegate_);
+      browser_, command_updater(), this, delegate_);
   AddChildView(page_action_icon_container_view_);
   page_action_icon_container_view_->SetIconColor(icon_color);
 
-  manage_passwords_icon_view_ =
-      new ManagePasswordsIconViews(command_updater(), this);
-  page_action_icons_.push_back(manage_passwords_icon_view_);
-
   if (browser_) {
     save_credit_card_icon_view_ = new autofill::SaveCardIconView(
         command_updater(), browser_, this, font_list);
@@ -389,7 +387,6 @@
   // Compute width of omnibox-trailing content.
   int trailing_width =
       IncrementalMinimumWidth(translate_icon_view_) +
-      IncrementalMinimumWidth(manage_passwords_icon_view_) +
       IncrementalMinimumWidth(page_action_icon_container_view_);
   if (star_view_)
     trailing_width += IncrementalMinimumWidth(star_view_);
@@ -518,7 +515,6 @@
     add_trailing_decoration(save_credit_card_icon_view_);
   if (local_card_migration_icon_view_)
     add_trailing_decoration(local_card_migration_icon_view_);
-  add_trailing_decoration(manage_passwords_icon_view_);
   for (ContentSettingViews::const_reverse_iterator i(
            content_setting_views_.rbegin());
        i != content_setting_views_.rend(); ++i) {
@@ -923,13 +919,6 @@
   }
 }
 
-void LocationBarView::UpdateManagePasswordsIconAndBubble() {
-  if (manage_passwords_icon_view_->Update()) {
-    Layout();
-    SchedulePaint();
-  }
-}
-
 void LocationBarView::UpdateSaveCreditCardIcon() {
   if (save_credit_card_icon_view_->Update()) {
     Layout();
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index 1613556b..8d5d5e1 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -43,7 +43,6 @@
 class IntentPickerView;
 class KeywordHintView;
 class LocationIconView;
-class ManagePasswordsIconViews;
 enum class OmniboxPart;
 class OmniboxPopupView;
 enum class OmniboxTint;
@@ -142,11 +141,6 @@
   // Returns the delegate.
   Delegate* delegate() const { return delegate_; }
 
-  // The passwords icon. It may not be visible.
-  ManagePasswordsIconViews* manage_passwords_icon_view() {
-    return manage_passwords_icon_view_;
-  }
-
   // Toggles the star on or off.
   void SetStarToggled(bool on);
 
@@ -321,7 +315,6 @@
   void AcceptInput(base::TimeTicks match_selection_timestamp) override;
   void FocusSearch() override;
   void UpdateContentSettingsIcons() override;
-  void UpdateManagePasswordsIconAndBubble() override;
   void UpdateSaveCreditCardIcon() override;
   void UpdateLocalCardMigrationIcon() override;
   void UpdateBookmarkStarVisibility() override;
@@ -409,9 +402,6 @@
   // The page action icons.
   PageActionIconContainerView* page_action_icon_container_view_ = nullptr;
 
-  // The manage passwords icon.
-  ManagePasswordsIconViews* manage_passwords_icon_view_ = nullptr;
-
   // The save credit card icon.  It will be null when |browser_| is null.
   autofill::SaveCardIconView* save_credit_card_icon_view_ = nullptr;
 
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
index b8686c8..3b73347 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.cc
@@ -225,6 +225,11 @@
   observers_.RemoveObserver(observer);
 }
 
+void CastDialogView::KeepShownForTesting() {
+  keep_shown_for_testing_ = true;
+  set_close_on_deactivate(false);
+}
+
 // static
 void CastDialogView::ShowDialog(views::View* anchor_view,
                                 views::BubbleBorder::Arrow anchor_position,
@@ -382,21 +387,22 @@
   selected_sink_index_ = index;
   const UIMediaSink& sink = sink_buttons_.at(index)->sink();
   if (sink.route) {
-    controller_->StopCasting(sink.route->media_route_id());
     metrics_.OnStopCasting(sink.route->is_local());
+    // StopCasting() may trigger a model update and invalidate |sink|.
+    controller_->StopCasting(sink.route->media_route_id());
   } else {
     base::Optional<MediaCastMode> cast_mode = GetCastModeToUse(sink);
     if (cast_mode) {
       // Starting local file casting may open a new tab synchronously on the UI
       // thread, which deactivates the dialog. So we must prevent it from
       // closing and getting destroyed.
-      if (cast_mode == LOCAL_FILE)
+      if (cast_mode.value() == LOCAL_FILE)
         set_close_on_deactivate(false);
       controller_->StartCasting(sink.id, cast_mode.value());
       // Re-enable close on deactivate so the user can click elsewhere to close
       // the dialog.
-      if (cast_mode == LOCAL_FILE)
-        set_close_on_deactivate(true);
+      if (cast_mode.value() == LOCAL_FILE)
+        set_close_on_deactivate(!keep_shown_for_testing_);
       metrics_.OnStartCasting(base::Time::Now(), index);
     }
   }
@@ -454,7 +460,7 @@
 
 void CastDialogView::OnFilePickerClosed(const ui::SelectedFileInfo* file_info) {
   // Re-enable the setting to close the dialog when it loses focus.
-  set_close_on_deactivate(true);
+  set_close_on_deactivate(!keep_shown_for_testing_);
   if (file_info) {
 #if defined(OS_WIN)
     local_file_name_ = file_info->display_name;
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_view.h b/chrome/browser/ui/views/media_router/cast_dialog_view.h
index 4457547..01ffc07 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_view.h
+++ b/chrome/browser/ui/views/media_router/cast_dialog_view.h
@@ -100,6 +100,11 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  // If the dialog loses focus during a test and closes, the test can
+  // fail unexpectedly. This method prevents that by keeping the dialog from
+  // closing on blur.
+  void KeepShownForTesting();
+
   // Called by tests.
   const std::vector<CastDialogSinkButton*>& sink_buttons_for_test() const {
     return sink_buttons_;
@@ -226,6 +231,9 @@
 
   base::ObserverList<Observer> observers_;
 
+  // When this is set to true, the dialog does not close on blur.
+  bool keep_shown_for_testing_ = false;
+
   base::WeakPtrFactory<CastDialogView> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CastDialogView);
diff --git a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
index be13c4aa3..518a2444 100644
--- a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
+++ b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
@@ -70,25 +70,15 @@
 
   ToolbarButton::Init();
   IssuesObserver::Init();
-  MediaRouterActionController* controller =
-      MediaRouterUIService::Get(profile_)->action_controller();
-  controller->AddObserver(this);
-  SetVisible(controller->ShouldEnableAction());
+
+  DCHECK(GetActionController());
+  GetActionController()->AddObserver(this);
+  SetVisible(GetActionController()->ShouldEnableAction());
 }
 
 CastToolbarButton::~CastToolbarButton() {
-  MediaRouterUIService::Get(profile_)->action_controller()->RemoveObserver(
-      this);
-}
-
-void CastToolbarButton::UpdateIcon() {
-  const gfx::VectorIcon& icon = GetCurrentIcon();
-  SetImage(views::Button::STATE_NORMAL,
-           gfx::CreateVectorIcon(icon, GetIconColor(&icon)));
-  // This icon is smaller than the touchable-UI expected 24dp, so we need to pad
-  // the insets to match.
-  SetLayoutInsetDelta(
-      gfx::Insets(ui::MaterialDesignController::touch_ui() ? 4 : 0));
+  if (GetActionController())
+    GetActionController()->RemoveObserver(this);
 }
 
 const gfx::VectorIcon& CastToolbarButton::GetCurrentIcon() const {
@@ -150,21 +140,15 @@
 }
 
 bool CastToolbarButton::OnMousePressed(const ui::MouseEvent& event) {
-  if (event.IsRightMouseButton()) {
-    MediaRouterUIService::Get(profile_)
-        ->action_controller()
-        ->KeepIconOnRightMousePressed();
-  }
+  if (event.IsRightMouseButton() && GetActionController())
+    GetActionController()->KeepIconOnRightMousePressed();
   return ToolbarButton::OnMousePressed(event);
 }
 
 void CastToolbarButton::OnMouseReleased(const ui::MouseEvent& event) {
   ToolbarButton::OnMouseReleased(event);
-  if (event.IsRightMouseButton()) {
-    MediaRouterUIService::Get(profile_)
-        ->action_controller()
-        ->MaybeHideIconOnRightMouseReleased();
-  }
+  if (event.IsRightMouseButton() && GetActionController())
+    GetActionController()->MaybeHideIconOnRightMouseReleased();
 }
 
 void CastToolbarButton::ButtonPressed(views::Button* sender,
@@ -181,4 +165,18 @@
   }
 }
 
+void CastToolbarButton::UpdateIcon() {
+  const gfx::VectorIcon& icon = GetCurrentIcon();
+  SetImage(views::Button::STATE_NORMAL,
+           gfx::CreateVectorIcon(icon, GetIconColor(&icon)));
+  // This icon is smaller than the touchable-UI expected 24dp, so we need to pad
+  // the insets to match.
+  SetLayoutInsetDelta(
+      gfx::Insets(ui::MaterialDesignController::touch_ui() ? 4 : 0));
+}
+
+MediaRouterActionController* CastToolbarButton::GetActionController() const {
+  return MediaRouterUIService::Get(profile_)->action_controller();
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/ui/views/media_router/cast_toolbar_button.h b/chrome/browser/ui/views/media_router/cast_toolbar_button.h
index 62a6cc6..3663686 100644
--- a/chrome/browser/ui/views/media_router/cast_toolbar_button.h
+++ b/chrome/browser/ui/views/media_router/cast_toolbar_button.h
@@ -70,6 +70,8 @@
  private:
   const gfx::VectorIcon& GetCurrentIcon() const;
 
+  MediaRouterActionController* GetActionController() const;
+
   Browser* const browser_;
   Profile* const profile_;
 
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
index 7daee94..30a98d4 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.cc
@@ -65,6 +65,8 @@
                                           dialog_creation_time);
   }
   CastDialogView::GetCurrentDialogWidget()->AddObserver(this);
+  if (dialog_creation_callback_)
+    dialog_creation_callback_.Run();
 }
 
 void MediaRouterDialogControllerViews::CloseMediaRouterDialog() {
@@ -76,8 +78,11 @@
 }
 
 void MediaRouterDialogControllerViews::Reset() {
-  MediaRouterDialogControllerImplBase::Reset();
-  ui_.reset();
+  // If |ui_| is null, Reset() has already been called.
+  if (ui_) {
+    MediaRouterDialogControllerImplBase::Reset();
+    ui_.reset();
+  }
 }
 
 void MediaRouterDialogControllerViews::OnWidgetClosing(views::Widget* widget) {
@@ -90,6 +95,11 @@
   widget->RemoveObserver(this);
 }
 
+void MediaRouterDialogControllerViews::SetDialogCreationCallbackForTesting(
+    base::RepeatingClosure callback) {
+  dialog_creation_callback_ = std::move(callback);
+}
+
 MediaRouterDialogControllerViews::MediaRouterDialogControllerViews(
     content::WebContents* web_contents)
     : MediaRouterDialogControllerImplBase(web_contents) {}
diff --git a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
index 93d604d..6362a275 100644
--- a/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
+++ b/chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/observer_list.h"
 #include "chrome/browser/ui/media_router/media_router_dialog_controller_impl_base.h"
 #include "chrome/browser/ui/views/media_router/media_router_views_ui.h"
 #include "ui/views/widget/widget_observer.h"
@@ -35,6 +36,9 @@
   void OnWidgetClosing(views::Widget* widget) override;
   void OnWidgetDestroying(views::Widget* widget) override;
 
+  // Sets a callback to be called whenever a dialog is created.
+  void SetDialogCreationCallbackForTesting(base::RepeatingClosure callback);
+
  private:
   friend class content::WebContentsUserData<MediaRouterDialogControllerViews>;
 
@@ -47,6 +51,8 @@
   // closed.
   std::unique_ptr<MediaRouterViewsUI> ui_;
 
+  base::RepeatingClosure dialog_creation_callback_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaRouterDialogControllerViews);
 };
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 369471c5..e415cb7 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -17,8 +17,6 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/command_updater.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h"
-#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
 #include "chrome/browser/ui/omnibox/clipboard_utils.h"
 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
 #include "chrome/browser/ui/view_ids.h"
@@ -1135,22 +1133,17 @@
     location_bar_view_->Layout();
 
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
-  if (location_bar_view_) {
-    // The user must be starting a session in the same tab as a previous one in
-    // order to display the new tab in-product help promo.  While focusing the
-    // omnibox is not always a precursor to starting a new session, we don't
-    // want to wait until the user is in the middle of editing or navigating,
-    // because we'd like to show them the promo at the time when it would be
-    // immediately useful.
-    if (controller()->GetLocationBarModel()->ShouldDisplayURL()) {
-      feature_engagement::NewTabTrackerFactory::GetInstance()
-          ->GetForProfile(location_bar_view_->profile())
-          ->OnOmniboxFocused();
-    }
-
-    auto* reopen_tab_iph = ReopenTabInProductHelpFactory::GetForProfile(
-        location_bar_view_->profile());
-    reopen_tab_iph->OmniboxFocused();
+  // The user must be starting a session in the same tab as a previous one in
+  // order to display the new tab in-product help promo.  While focusing the
+  // omnibox is not always a precursor to starting a new session, we don't
+  // want to wait until the user is in the middle of editing or navigating,
+  // because we'd like to show them the promo at the time when it would be
+  // immediately useful.
+  if (location_bar_view_ &&
+      controller()->GetLocationBarModel()->ShouldDisplayURL()) {
+    feature_engagement::NewTabTrackerFactory::GetInstance()
+        ->GetForProfile(location_bar_view_->profile())
+        ->OnOmniboxFocused();
   }
 #endif
 
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc
index b5d733c..bca847b6 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_container_view.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/views/location_bar/find_bar_icon.h"
 #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h"
 #include "chrome/browser/ui/views/page_action/zoom_view.h"
+#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "ui/views/layout/box_layout.h"
 
 PageActionIconContainerView::PageActionIconContainerView(
@@ -15,6 +16,7 @@
     int icon_size,
     int between_icon_spacing,
     Browser* browser,
+    CommandUpdater* command_updater,
     PageActionIconView::Delegate* page_action_icon_delegate,
     LocationBarView::Delegate* location_bar_delegate)
     : zoom_observer_(this) {
@@ -30,6 +32,11 @@
         find_bar_icon_ = new FindBarIcon(browser, page_action_icon_delegate);
         page_action_icons_.push_back(find_bar_icon_);
         break;
+      case PageActionIconType::kManagePasswords:
+        manage_passwords_icon_ = new ManagePasswordsIconViews(
+            command_updater, page_action_icon_delegate);
+        page_action_icons_.push_back(manage_passwords_icon_);
+        break;
       case PageActionIconType::kZoom:
         zoom_view_ =
             new ZoomView(location_bar_delegate, page_action_icon_delegate);
@@ -61,6 +68,8 @@
   switch (type) {
     case PageActionIconType::kFind:
       return find_bar_icon_;
+    case PageActionIconType::kManagePasswords:
+      return manage_passwords_icon_;
     case PageActionIconType::kZoom:
       return zoom_view_;
   }
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_container_view.h b/chrome/browser/ui/views/page_action/page_action_icon_container_view.h
index 5f7cc9d..2096751 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_container_view.h
+++ b/chrome/browser/ui/views/page_action/page_action_icon_container_view.h
@@ -15,7 +15,9 @@
 #include "ui/views/view.h"
 
 class Browser;
+class CommandUpdater;
 class FindBarIcon;
+class ManagePasswordsIconViews;
 class ZoomView;
 
 class PageActionIconContainerView : public views::View,
@@ -27,6 +29,7 @@
       int icon_size,
       int between_icon_spacing,
       Browser* browser,
+      CommandUpdater* command_updater,
       PageActionIconView::Delegate* page_action_icon_delegate,
       LocationBarView::Delegate* location_bar_delegate);
   ~PageActionIconContainerView() override;
@@ -60,6 +63,7 @@
 
   ZoomView* zoom_view_ = nullptr;
   FindBarIcon* find_bar_icon_ = nullptr;
+  ManagePasswordsIconViews* manage_passwords_icon_ = nullptr;
   std::vector<PageActionIconView*> page_action_icons_;
 
   ScopedObserver<zoom::ZoomEventManager, zoom::ZoomEventManagerObserver>
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
index a6a6555..300b512 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/ui/passwords/manage_passwords_test.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_container_view.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/grit/generated_resources.h"
@@ -24,10 +25,13 @@
   password_manager::ui::State ViewState() { return GetView()->state_; }
 
   ManagePasswordsIconViews* GetView() {
-    return BrowserView::GetBrowserViewForBrowser(browser())
-        ->toolbar()
-        ->location_bar()
-        ->manage_passwords_icon_view();
+    views::View* view =
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->toolbar_button_provider()
+            ->GetPageActionIconContainerView()
+            ->GetPageActionIconView(PageActionIconType::kManagePasswords);
+    DCHECK_EQ(view->GetClassName(), ManagePasswordsIconViews::kClassName);
+    return static_cast<ManagePasswordsIconViews*>(view);
   }
 
   base::string16 GetTooltipText() {
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
index 9af753f5..5b370213 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
@@ -14,6 +14,8 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 
+const char ManagePasswordsIconViews::kClassName[] = "ManagePasswordsIconViews";
+
 ManagePasswordsIconViews::ManagePasswordsIconViews(
     CommandUpdater* updater,
     PageActionIconView::Delegate* delegate)
@@ -108,3 +110,7 @@
   if (IsBubbleShowing())
     PasswordBubbleViewBase::ActivateBubble();
 }
+
+const char* ManagePasswordsIconViews::GetClassName() const {
+  return kClassName;
+}
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.h b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.h
index 21d1b5e..06bfdc30 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.h
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.h
@@ -18,6 +18,8 @@
 class ManagePasswordsIconViews : public ManagePasswordsIconView,
                                  public PageActionIconView {
  public:
+  static const char kClassName[];
+
   ManagePasswordsIconViews(CommandUpdater* updater,
                            PageActionIconView::Delegate* delegate);
   ~ManagePasswordsIconViews() override;
@@ -36,6 +38,7 @@
 
   // views::View:
   void AboutToRequestFocusFromTabTraversal(bool reverse) override;
+  const char* GetClassName() const override;
 
  private:
   friend class ManagePasswordsIconViewTest;
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
index b95493d..6757d524 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
@@ -8,7 +8,9 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_container_view.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "chrome/browser/ui/views/passwords/password_auto_sign_in_view.h"
 #include "chrome/browser/ui/views/passwords/password_items_view.h"
@@ -29,7 +31,8 @@
          !g_manage_passwords_bubble_->GetWidget()->IsVisible());
 
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
-  views::View* const anchor_view = browser_view->GetLocationBarView();
+  views::View* const anchor_view =
+      browser_view->toolbar_button_provider()->GetAnchorView();
 
   PasswordBubbleViewBase* bubble =
       CreateBubble(web_contents, anchor_view, gfx::Point(), reason);
@@ -38,7 +41,9 @@
 
   if (anchor_view) {
     g_manage_passwords_bubble_->SetHighlightedButton(
-        browser_view->GetLocationBarView()->manage_passwords_icon_view());
+        browser_view->toolbar_button_provider()
+            ->GetPageActionIconContainerView()
+            ->GetPageActionIconView(PageActionIconType::kManagePasswords));
   } else {
     g_manage_passwords_bubble_->set_parent_window(
         web_contents->GetNativeView());
diff --git a/chrome/browser/ui/views/quit_instruction_bubble.cc b/chrome/browser/ui/views/quit_instruction_bubble.cc
index 66fd87dd..6fb9ce0 100644
--- a/chrome/browser/ui/views/quit_instruction_bubble.cc
+++ b/chrome/browser/ui/views/quit_instruction_bubble.cc
@@ -60,9 +60,11 @@
 
       // Set the bounds to that of the active browser window so that the widget
       // will be centered on the nearest monitor.
-      params.bounds = BrowserView::GetBrowserViewForBrowser(
-                          BrowserList::GetInstance()->GetLastActive())
-                          ->GetBounds();
+      const Browser* last_active = BrowserList::GetInstance()->GetLastActive();
+      if (last_active) {
+        params.bounds =
+            BrowserView::GetBrowserViewForBrowser(last_active)->GetBounds();
+      }
       params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
       params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
       params.accept_events = false;
diff --git a/chrome/browser/ui/views/tabs/tab_icon.cc b/chrome/browser/ui/views/tabs/tab_icon.cc
index 0f7627e..b77e6a27 100644
--- a/chrome/browser/ui/views/tabs/tab_icon.cc
+++ b/chrome/browser/ui/views/tabs/tab_icon.cc
@@ -29,7 +29,7 @@
 
 constexpr int kLoadingProgressTimeMs = 400;
 constexpr int kLoadingProgressFadeOutMs = 200;
-constexpr int kFaviconFadeInMs = 200;
+constexpr int kFaviconFadeInMs = 500;
 
 bool UseNewLoadingAnimation() {
   return base::FeatureList::IsEnabled(features::kNewTabLoadingAnimation);
@@ -161,7 +161,7 @@
 
   // If the last frame painted still displays the loading indicator or favicon
   // in less than full opacity we need to paint the last frame.
-  if (animation_state_.favicon_alpha < SK_AlphaOPAQUE ||
+  if (animation_state_.favicon_fade_in_progress < 1.0 ||
       animation_state_.loading_progress_alpha > 0) {
     return true;
   }
@@ -257,16 +257,15 @@
   }
 
   // In the waiting/loading state we initially show no favicon.
-  state.favicon_alpha = (network_state_ == TabNetworkState::kWaiting ||
-                         network_state_ == TabNetworkState::kLoading)
-                            ? 0
-                            : SK_AlphaOPAQUE;
+  state.favicon_fade_in_progress =
+      (network_state_ == TabNetworkState::kWaiting ||
+       network_state_ == TabNetworkState::kLoading)
+          ? 0.0
+          : 1.0;
   if (favicon_fade_in_animation_) {
     base::TimeDelta favicon_fade_in_time = now - *favicon_fade_in_animation_;
-    state.favicon_alpha =
-        SK_AlphaOPAQUE *
-        std::min(favicon_fade_in_time.InMillisecondsF() / kFaviconFadeInMs,
-                 1.0);
+    state.favicon_fade_in_progress = std::min(
+        favicon_fade_in_time.InMillisecondsF() / kFaviconFadeInMs, 1.0);
   }
 
   return state;
@@ -285,7 +284,7 @@
   // Compare without |elapsed_time| as it's only used in the waiting state.
   auto tie = [](const LoadingAnimationState& state) {
     return std::tie(state.loading_progress, state.loading_progress_alpha,
-                    state.favicon_alpha);
+                    state.favicon_fade_in_progress);
   };
 
   return tie(new_state) != tie(animation_state_);
@@ -411,18 +410,21 @@
                                 const gfx::Rect& bounds) {
   // While loading, the favicon (or placeholder) isn't drawn until it has
   // started fading in.
-  if (animation_state_.favicon_alpha == 0)
+  if (animation_state_.favicon_fade_in_progress == 0.0)
     return false;
 
   if (icon.isNull())
     return false;
 
   cc::PaintFlags flags;
-  flags.setAlpha(animation_state_.favicon_alpha);
+  flags.setAlpha(animation_state_.favicon_fade_in_progress * SK_AlphaOPAQUE);
+  // Drop in the new favicon from the top while it's fading in.
+  const int offset =
+      round((animation_state_.favicon_fade_in_progress - 1.0) * 4.0);
 
   canvas->DrawImageInt(icon, 0, 0, bounds.width(), bounds.height(), bounds.x(),
-                       bounds.y(), bounds.width(), bounds.height(), false,
-                       flags);
+                       bounds.y() + offset, bounds.width(), bounds.height(),
+                       false, flags);
   return true;
 }
 
@@ -461,15 +463,14 @@
     }
 
     if (old_state == TabNetworkState::kLoading) {
+      // Rewind the progress timer back to currently displayed progress bar so
+      // we don't miss the end of the animation.
+      RewindLoadingProgressTimerIfNecessary(target_loading_progress_);
       target_loading_progress_ = 1.0;
       // Start fading in placeholder favicon if no favicon has loaded so far.
       const base::TimeTicks now = clock_->NowTicks();
       if (!favicon_fade_in_animation_)
         favicon_fade_in_animation_ = now;
-
-      // Rewind the progress timer back to 100% if necessary. This prevents
-      // parts of the fade-out animation to be skipped.
-      RewindLoadingProgressTimerIfNecessary(1.0f);
     }
 
     if (network_state_ == TabNetworkState::kWaiting) {
@@ -480,15 +481,23 @@
     SchedulePaint();
   }
 
-  // The loading progress looks really weird if it ever jumps backwards, so make
-  // sure it only increases.
-  if (network_state_ == TabNetworkState::kLoading &&
-      target_loading_progress_ < load_progress) {
-    DCHECK(loading_progress_timer_);
+  if (network_state_ == TabNetworkState::kLoading) {
+    // Interpolate loading progress to a narrower range to prevent us from
+    // looking stuck doing nothing at 0% or at 100% but still not finishing.
+    constexpr float kLoadingProgressStart = 0.3;
+    constexpr float kLoadingProgressEnd = 0.7;
+    load_progress =
+        kLoadingProgressStart +
+        load_progress * (kLoadingProgressEnd - kLoadingProgressStart);
 
-    RewindLoadingProgressTimerIfNecessary(target_loading_progress_);
+    // The loading progress looks really weird if it ever jumps backwards, so
+    // make sure it only increases.
+    if (target_loading_progress_ < load_progress) {
+      DCHECK(loading_progress_timer_);
 
-    target_loading_progress_ = load_progress;
+      RewindLoadingProgressTimerIfNecessary(target_loading_progress_);
+      target_loading_progress_ = load_progress;
+    }
   }
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_icon.h b/chrome/browser/ui/views/tabs/tab_icon.h
index dd7379e..f4b439cd 100644
--- a/chrome/browser/ui/views/tabs/tab_icon.h
+++ b/chrome/browser/ui/views/tabs/tab_icon.h
@@ -72,7 +72,7 @@
     base::TimeDelta elapsed_time;
     double loading_progress;
     SkAlpha loading_progress_alpha;
-    SkAlpha favicon_alpha;
+    double favicon_fade_in_progress;
   };
 
   // views::View:
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc
index 101f67cd..f7654ea 100644
--- a/chrome/browser/ui/views/tabs/tab_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -718,17 +718,22 @@
   data.network_state = TabNetworkState::kLoading;
   data.load_progress = 0.2;
   tab.SetData(data);
-  EXPECT_FLOAT_EQ(0.2, GetLoadingProgress(icon));
+  float initial_reported_progress = GetLoadingProgress(icon);
+  // Reported progress should interpolate to something between itself and 1.0.
+  EXPECT_GE(initial_reported_progress, 0.2);
+  EXPECT_LT(initial_reported_progress, 1.0);
 
   // Decrease load progress, icon's load progress should not change.
   data.load_progress = 0.1;
   tab.SetData(data);
-  EXPECT_FLOAT_EQ(0.2, GetLoadingProgress(icon));
+  EXPECT_FLOAT_EQ(initial_reported_progress, GetLoadingProgress(icon));
 
   // Though increasing it should be respected.
   data.load_progress = 0.5;
   tab.SetData(data);
-  EXPECT_FLOAT_EQ(0.5, GetLoadingProgress(icon));
+  // A higher load progress should be interpolate to larger value (less than 1).
+  EXPECT_GT(GetLoadingProgress(icon), initial_reported_progress);
+  EXPECT_LT(GetLoadingProgress(icon), 1.0);
 }
 
 TEST_F(TabTest, LoadingProgressGoesTo100PercentAfterLoadingIsDone) {
@@ -745,7 +750,9 @@
   data.network_state = TabNetworkState::kLoading;
   data.load_progress = 0.2;
   tab.SetData(data);
-  EXPECT_FLOAT_EQ(0.2, GetLoadingProgress(icon));
+  // Reported progress should interpolate to something between itself and 1.0.
+  EXPECT_GE(GetLoadingProgress(icon), 0.2);
+  EXPECT_LT(GetLoadingProgress(icon), 1.0);
 
   // Finish loading. Regardless of reported |data.load_progress|, load_progress
   // should be drawn at 100%.
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index bae1555..999a6eb1 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -755,6 +755,10 @@
   return this;
 }
 
+views::View* ToolbarView::GetAnchorView() {
+  return location_bar_;
+}
+
 BrowserRootView::DropIndex ToolbarView::GetDropIndex(
     const ui::DropTargetEvent& event) {
   return {browser_->tab_strip_model()->active_index(), false};
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h
index 6a89142..23b8a08 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -214,6 +214,7 @@
   gfx::Rect GetFindBarBoundingBox(int contents_height) const override;
   void FocusToolbar() override;
   views::AccessiblePaneView* GetAsAccessiblePaneView() override;
+  views::View* GetAnchorView() override;
 
   // BrowserRootView::DropTarget
   BrowserRootView::DropIndex GetDropIndex(
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 dbf07ce..0f8cf09 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui.cc
@@ -102,7 +102,7 @@
   base::Optional<RouteParameters> params =
       GetRouteParameters(sink_id, MediaCastMode::PRESENTATION);
   if (!params) {
-    SendIssueForUnableToCast(MediaCastMode::PRESENTATION);
+    SendIssueForUnableToCast(MediaCastMode::PRESENTATION, sink_id);
     return false;
   }
   GetIssueManager()->ClearNonBlockingIssues();
@@ -315,8 +315,6 @@
       route_request_id, sink_id, cast_mode, presentation_request_source_name,
       result);
   handler_->OnCreateRouteResponseReceived(sink_id, result.route());
-  if (result.result_code() == RouteRequestResult::TIMED_OUT)
-    SendIssueForRouteTimeout(cast_mode, presentation_request_source_name);
 }
 
 void MediaRouterUI::HandleCreateSessionRequestRouteResponse(
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index cd688e7..0559e8909 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -424,12 +424,8 @@
 
 void NetErrorHelper::LoadErrorPage(const std::string& html,
                                    const GURL& failed_url) {
-  render_frame()->GetWebFrame()->CommitDataNavigation(
-      blink::WebURLRequest(GURL(kUnreachableWebDataURL)), blink::WebData(html),
-      blink::WebString::FromUTF8("text/html"),
-      blink::WebString::FromUTF8("UTF-8"), failed_url,
-      blink::WebFrameLoadType::kReplaceCurrentItem, blink::WebHistoryItem(),
-      false /* is_client_redirect */, nullptr, nullptr);
+  render_frame()->LoadHTMLString(html, GURL(kUnreachableWebDataURL), "UTF-8",
+                                 failed_url, true /* replace_current_item */);
 }
 
 void NetErrorHelper::EnablePageHelperFunctions(net::Error net_error) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ff8e634..8b234a101 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -470,7 +470,6 @@
       "//ui/file_manager/gallery/",
       "//ui/file_manager/image_loader/",
       "//ui/file_manager/integration_tests/",
-      "//third_party/analytics/",
       "//third_party/polymer/v1_0/components-chromium/polymer/",
       "$root_gen_dir/ui/file_manager/file_manager/",
       "$root_gen_dir/ui/login/login_resources.pak",
@@ -2357,7 +2356,6 @@
 
     # TODO(newt): move this to test_support_unit?
     "../browser/android/chrome_backup_agent_unittest.cc",
-    "../browser/android/crash/crash_keys_android_unittest.cc",
     "../browser/android/customtabs/detached_resource_request_unittest.cc",
     "../browser/android/favicon_helper_unittest.cc",
     "../browser/android/mock_location_settings.cc",
@@ -4828,6 +4826,7 @@
       "//third_party/libpng",
       "//third_party/zlib",
       "//ui/base:test_support",
+      "//ui/events:events_interactive_ui_tests",
       "//ui/resources:ui_test_pak",
       "//ui/web_dialogs:test_support",
     ]
@@ -5793,12 +5792,13 @@
   fuzzer_test("tts_platform_fuzzer") {
     sources = [
       "../browser/speech/mock_tts_controller.cc",
-      "../browser/speech/tts_controller.h",
       "../browser/speech/tts_mac.mm",
-      "../browser/speech/tts_platform.cc",
       "../browser/speech/tts_platform.h",
       "../browser/speech/tts_platform_fuzzer.cc",
+      "../browser/speech/tts_platform_impl.cc",
+      "../browser/speech/tts_platform_impl.h",
       "../browser/speech/tts_win.cc",
+      "../content/public/browser/tts_controller.h",
     ]
 
     deps = [
diff --git a/chrome/test/base/test_browser_window.cc b/chrome/test/base/test_browser_window.cc
index 7b9116b..568caec 100644
--- a/chrome/test/base/test_browser_window.cc
+++ b/chrome/test/base/test_browser_window.cc
@@ -137,7 +137,7 @@
 }
 
 PageActionIconContainer* TestBrowserWindow::GetPageActionIconContainer() {
-  return nullptr;
+  return &page_action_icon_container_;
 }
 
 ToolbarActionsBar* TestBrowserWindow::GetToolbarActionsBar() {
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index d8d87490..b5beaab6 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -194,7 +194,6 @@
     void FocusLocation(bool select_all) override {}
     void FocusSearch() override {}
     void UpdateContentSettingsIcons() override {}
-    void UpdateManagePasswordsIconAndBubble() override {}
     void UpdateSaveCreditCardIcon() override {}
     void UpdateLocalCardMigrationIcon() override {}
     void UpdateBookmarkStarVisibility() override {}
@@ -208,8 +207,21 @@
     DISALLOW_COPY_AND_ASSIGN(TestLocationBar);
   };
 
+  class TestPageActionIconContainer : public PageActionIconContainer {
+   public:
+    TestPageActionIconContainer() {}
+    ~TestPageActionIconContainer() override {}
+
+    // PageActionIconContainer:
+    void UpdatePageActionIcon(PageActionIconType type) override {}
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(TestPageActionIconContainer);
+  };
+
   TestDownloadShelf download_shelf_;
   TestLocationBar location_bar_;
+  TestPageActionIconContainer page_action_icon_container_;
 
   DISALLOW_COPY_AND_ASSIGN(TestBrowserWindow);
 };
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.js b/chrome/test/data/webui/print_preview/destination_select_test.js
index cf69324..d775850 100644
--- a/chrome/test/data/webui/print_preview/destination_select_test.js
+++ b/chrome/test/data/webui/print_preview/destination_select_test.js
@@ -12,6 +12,7 @@
     DefaultDestinationSelectionRules: 'default destination selection rules',
     SystemDefaultPrinterPolicy: 'system default printer policy',
     KioskModeSelectsFirstPrinter: 'kiosk mode selects first printer',
+    NoPrintersShowsError: 'no printers shows error',
   };
 
   const suiteName = 'DestinationSelectTests';
@@ -42,10 +43,13 @@
 
     /*
      * Sets the initial settings to the stored value and creates the page.
-     * @return {!Promise} Promise that resolves when initial settings and
-     *     printer capabilities have been returned.
+     * @param {boolean=} opt_expectPrinterFailure Whether printer fetch is
+     *     expected to fail
+     * @return {!Promise} Promise that resolves when initial settings and,
+     *     if printer failure is not expected, printer capabilities have
+     *     been returned.
      */
-    function setInitialSettings() {
+    function setInitialSettings(opt_expectPrinterFailure) {
       nativeLayer.setInitialSettings(initialSettings);
       nativeLayer.setLocalDestinations(localDestinations);
       print_preview.NativeLayer.setInstance(nativeLayer);
@@ -53,10 +57,10 @@
       page = document.createElement('print-preview-app');
       document.body.appendChild(page);
 
-      return Promise.all([
-        nativeLayer.whenCalled('getInitialSettings'),
-        nativeLayer.whenCalled('getPrinterCapabilities')
-      ]);
+      const promises = [nativeLayer.whenCalled('getInitialSettings')];
+      if (!opt_expectPrinterFailure)
+        promises.push(nativeLayer.whenCalled('getPrinterCapabilities'));
+      return Promise.all(promises);
     }
 
     /**
@@ -247,6 +251,38 @@
         assertPrinterDisplay(destinations[0].displayName);
       });
     });
+
+    /**
+     * Tests that if there is no system default destination, the default
+     * selection rules and recent destinations are empty, the preview
+     * is in app kiosk mode (so no PDF printer), and there are no
+     * destinations found, the no destinations found error is displayed.
+     */
+    test(assert(TestNames.NoPrintersShowsError), function() {
+      initialSettings.serializedDefaultDestinationSelectionRulesStr = '';
+      initialSettings.serializedAppStateStr = '';
+      initialSettings.isInAppKioskMode = true;
+      initialSettings.printerName = '';
+      localDestinations = [];
+
+      return Promise
+          .all([
+            setInitialSettings(true),
+            test_util.eventToPromise(
+                print_preview.DestinationStore.EventType.NO_DESTINATIONS_FOUND,
+                page.destinationStore_),
+          ])
+          .then(function() {
+            assertEquals(undefined, page.destination_);
+            const destinationSettings =
+                page.$$('print-preview-destination-settings');
+            assertTrue(destinationSettings.$$('.throbber-container').hidden);
+            assertTrue(
+                destinationSettings.$$('.destination-settings-box').hidden);
+            assertFalse(
+                destinationSettings.$$('.no-destinations-display').hidden);
+          });
+    });
   });
 
   return {
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js
index 70c7e30..b94cb19 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -20,9 +20,10 @@
       print_preview.NativeLayer.setInstance(nativeLayer);
       destinationSettings =
           document.createElement('print-preview-destination-settings');
-      destinationSettings.disabled = false;
       destinationSettings.destinationStore = null;
       destinationSettings.state = print_preview_new.State.NOT_READY;
+      // Disabled is true when state is NOT_READY.
+      destinationSettings.disabled = true;
       document.body.appendChild(destinationSettings);
     });
 
@@ -33,8 +34,8 @@
       // Initial state: No destination store, button should be disabled.
       assertTrue(button.disabled);
 
-      // Set up the destination store, but no destination yet. Button is now
-      // enabled.
+      // Set up the destination store, but no destination yet. Button is
+      // disabled.
       const userInfo = new print_preview.UserInfo();
       const destinationStore = new print_preview.DestinationStore(
           userInfo, new WebUIListenerTracker());
@@ -44,7 +45,7 @@
           [] /* recentDestinations */);
       destinationSettings.destinationStore = destinationStore;
       destinationSettings.state = print_preview_new.State.NOT_READY;
-      assertFalse(button.disabled);
+      assertTrue(button.disabled);
 
       // Simulate loading a destination and setting state to ready. The button
       // is still enabled.
@@ -53,6 +54,7 @@
           print_preview.DestinationOrigin.LOCAL, 'FooName', true /* isRecent */,
           print_preview.DestinationConnectionStatus.ONLINE);
       destinationSettings.state = print_preview_new.State.READY;
+      destinationSettings.disabled = false;
       assertFalse(button.disabled);
 
       // Simulate setting a setting to an invalid value. Button is disabled due
@@ -68,6 +70,13 @@
       destinationSettings.state = print_preview_new.State.INVALID_PRINTER;
       destinationSettings.disabled = true;
       assertFalse(button.disabled);
+
+      // Simulate the user having no printers.
+      destinationSettings.destination = null;
+      destinationSettings.state = print_preview_new.State.INVALID_PRINTER;
+      destinationSettings.disabled = true;
+      destinationSettings.noDestinationsFound = true;
+      assertTrue(button.disabled);
     });
   });
 
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index 4b9f366..c06ca7c 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -87,7 +87,8 @@
     /** @override */
     getPrinters(type) {
       this.methodCalled('getPrinters', type);
-      if (type == print_preview.PrinterType.LOCAL_PRINTER) {
+      if (type == print_preview.PrinterType.LOCAL_PRINTER &&
+          this.localDestinationInfos_.length > 0) {
         cr.webUIListenerCallback(
             'printers-added', type, this.localDestinationInfos_);
       } else if (
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index a8f19dc..65d9a5670 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -618,6 +618,7 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
+      '../settings/test_util.js',
       '../test_browser_proxy.js',
       'native_layer_stub.js',
       'print_preview_test_utils.js',
@@ -675,6 +676,12 @@
           destination_select_test.TestNames.KioskModeSelectsFirstPrinter);
     });
 
+GEN('#if defined(OS_CHROMEOS)');
+TEST_F('PrintPreviewDestinationSelectTest', 'NoPrintersShowsError', function() {
+  this.runMochaTest(destination_select_test.TestNames.NoPrintersShowsError);
+});
+GEN('#endif');
+
 PrintPreviewDestinationDialogTest = class extends NewPrintPreviewTest {
   /** @override */
   get browsePreload() {
diff --git a/chrome/test/media_router/media_router_base_browsertest.cc b/chrome/test/media_router/media_router_base_browsertest.cc
index 3e6d7f4..03145cd5 100644
--- a/chrome/test/media_router/media_router_base_browsertest.cc
+++ b/chrome/test/media_router/media_router_base_browsertest.cc
@@ -42,7 +42,7 @@
   ParseCommandLine();
   // The integration and E2E tests depend on the WebUI Cast dialog, so the Views
   // dialog must be disabled.
-  feature_list_.InitAndDisableFeature(features::kViewsCastDialog);
+  feature_list_.InitAndEnableFeature(features::kViewsCastDialog);
   ExtensionBrowserTest::SetUp();
 }
 
diff --git a/chrome/test/media_router/media_router_e2e_browsertest.cc b/chrome/test/media_router/media_router_e2e_browsertest.cc
index 275f4ad..1e616ec 100644
--- a/chrome/test/media_router/media_router_e2e_browsertest.cc
+++ b/chrome/test/media_router/media_router_e2e_browsertest.cc
@@ -81,7 +81,7 @@
   observer_.reset(new TestMediaSinksObserver(media_router_, source, origin));
   observer_->Init();
 
-  DVLOG(1) << "Receiver name: " << receiver();
+  DVLOG(1) << "Receiver name: " << receiver_;
   // Wait for MediaSinks compatible with |source| to be discovered.
   ASSERT_TRUE(ConditionalWait(
       base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
@@ -89,7 +89,7 @@
                  base::Unretained(this))));
 
   const auto& sink_map = observer_->sink_map;
-  const auto it = sink_map.find(receiver());
+  const auto it = sink_map.find(receiver_);
   const MediaSink& sink = it->second;
 
   // The callback will set route_id_ when invoked.
@@ -112,7 +112,7 @@
 }
 
 bool MediaRouterE2EBrowserTest::IsSinkDiscovered() const {
-  return base::ContainsKey(observer_->sink_map, receiver());
+  return base::ContainsKey(observer_->sink_map, receiver_);
 }
 
 bool MediaRouterE2EBrowserTest::IsRouteCreated() const {
diff --git a/chrome/test/media_router/media_router_e2e_ui_browsertest.cc b/chrome/test/media_router/media_router_e2e_ui_browsertest.cc
index 902b08aa..1c5191d 100644
--- a/chrome/test/media_router/media_router_e2e_ui_browsertest.cc
+++ b/chrome/test/media_router/media_router_e2e_ui_browsertest.cc
@@ -29,22 +29,15 @@
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
 
-  content::WebContents* dialog_contents = OpenMRDialog(web_contents);
-  ASSERT_TRUE(dialog_contents);
-  // Wait util the dialog finishes rendering.
-  WaitUntilDialogFullyLoaded(dialog_contents);
-
-  // Get the media router UI
-  MediaRouterUI* media_router_ui = GetMediaRouterUI(dialog_contents);
+  test_ui_->ShowDialog();
+  test_ui_->WaitForSinkAvailable(receiver_);
 
   // Mock out file dialog operations, as those can't be simulated.
-  FileDialogSelectsFile(media_router_ui, file_url);
-  // Open the Cast mode list.
-  ClickHeader(dialog_contents);
+  FileDialogSelectsFile(file_url);
   // Click on the desired mode.
-  ClickCastMode(dialog_contents, MediaCastMode::LOCAL_FILE);
-  WaitUntilSinkDiscoveredOnUI();
-  ChooseSink(web_contents, receiver());
+  test_ui_->ChooseSourceType(CastDialogView::kLocalFile);
+  test_ui_->WaitForSinkAvailable(receiver_);
+  test_ui_->StartCasting(receiver_);
 
   // Play the file for 10 seconds.
   Wait(base::TimeDelta::FromSeconds(10));
@@ -61,9 +54,8 @@
                                              &is_fullscreen));
 
   ASSERT_TRUE(is_fullscreen);
-  if (IsDialogClosed(web_contents))
-    OpenMRDialog(web_contents);
-  CloseRouteOnUI();
+  test_ui_->WaitForSink(receiver_);
+  test_ui_->StopCasting(receiver_);
   // Wait 15s for Chromecast to back to home screen and ready to use status.
   Wait(base::TimeDelta::FromSeconds(15));
 }
@@ -71,23 +63,18 @@
 IN_PROC_BROWSER_TEST_F(MediaRouterE2EBrowserTest, MANUAL_MirrorHTML5Video) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-
-  content::WebContents* dialog_contents = OpenMRDialog(web_contents);
-  ASSERT_TRUE(dialog_contents);
+  test_ui_ = MediaRouterUiForTest::GetOrCreateForWebContents(web_contents);
+  test_ui_->ShowDialog();
 
   // Wait until the dialog finishes rendering.
-  WaitUntilDialogFullyLoaded(dialog_contents);
-  WaitUntilSinkDiscoveredOnUI();
-  ChooseSink(web_contents, receiver());
+  test_ui_->WaitForSinkAvailable(receiver_);
+  test_ui_->StartCasting(receiver_);
 
   // Mirror tab for 10s.
   Wait(base::TimeDelta::FromSeconds(10));
-  if (IsDialogClosed(web_contents))
-    dialog_contents = OpenMRDialog(web_contents);
-  WaitUntilDialogFullyLoaded(dialog_contents);
 
   // Check the mirroring session has started successfully.
-  ASSERT_TRUE(!GetRouteId(receiver()).empty());
+  ASSERT_FALSE(test_ui_->GetRouteIdForSink(receiver_).empty());
   OpenMediaPage();
 
   // Play the video on loop and wait 5s for it to play smoothly.
@@ -101,16 +88,18 @@
       "webkitRequestFullScreen();";
   ExecuteScript(web_contents, script);
   Wait(base::TimeDelta::FromSeconds(5));
-  if (IsDialogClosed(web_contents))
-    dialog_contents = OpenMRDialog(web_contents);
-  WaitUntilDialogFullyLoaded(dialog_contents);
+  if (!test_ui_->IsDialogShown())
+    test_ui_->ShowDialog();
 
   // Check the mirroring session is still live.
-  ASSERT_TRUE(!GetRouteId(receiver()).empty());
+  ASSERT_FALSE(test_ui_->GetRouteIdForSink(receiver_).empty());
   Wait(base::TimeDelta::FromSeconds(20));
-  if (IsDialogClosed(web_contents))
-    OpenMRDialog(web_contents);
-  CloseRouteOnUI();
+  if (!test_ui_->IsDialogShown())
+    test_ui_->ShowDialog();
+  test_ui_->WaitForSink(receiver_);
+  test_ui_->StopCasting(receiver_);
+  test_ui_->WaitUntilNoRoutes();
+  test_ui_->HideDialog();
 }
 
 }  // namespace media_router
diff --git a/chrome/test/media_router/media_router_integration_browsertest.cc b/chrome/test/media_router/media_router_integration_browsertest.cc
index 6c48cea..f228c411 100644
--- a/chrome/test/media_router/media_router_integration_browsertest.cc
+++ b/chrome/test/media_router/media_router_integration_browsertest.cc
@@ -16,14 +16,11 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
 #include "chrome/browser/ui/media_router/media_router_file_dialog.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/webui/media_router/media_router_dialog_controller_webui_impl.h"
-#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
 #include "chrome/common/media_router/issue.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -63,66 +60,6 @@
     "sendMessageAndExpectResponse('%s');";
 const char kSendMessageAndExpectConnectionCloseOnErrorScript[] =
     "sendMessageAndExpectConnectionCloseOnError()";
-const char kChooseSinkScript[] =
-    "var sinks = Array.from(document.getElementById('media-router-container')."
-    "  shadowRoot.getElementById('sink-list').getElementsByTagName('span'));"
-    "var sink = sinks.find(sink => sink.textContent.trim() == '%s');"
-    "if (sink) {"
-    "  sink.click();"
-    "}";
-const char kClickCastModeScript[] =
-    "var mediaRouterContainer ="
-    "   document.getElementById('media-router-container');"
-    "var modes = Array.from(mediaRouterContainer"
-    "   .shadowRoot"
-    "   .getElementById('cast-mode-list')"
-    "   .getElementsByTagName('span'));"
-    "var mode = modes.find(mode => {"
-    "     return mode.textContent.trim() == mediaRouterContainer"
-    "         .castModeList.find(mode => mode.type == %d).description;"
-    "   });"
-    "if (mode) {"
-    "  mode.click();"
-    "}";
-const char kCloseRouteScript[] =
-    "window.document.getElementById('media-router-container').shadowRoot."
-    "  getElementById('route-details').shadowRoot.getElementById("
-    "    'close-route-button').click()";
-const char kClickDialog[] =
-    "window.document.getElementById('media-router-container').click();";
-const char kClickHeader[] =
-    "window.document.getElementById('media-router-container').shadowRoot"
-    ".getElementById('container-header').shadowRoot"
-    ".getElementById('header-text').click();";
-const char kGetSinkIdScript[] =
-    "var sinks = window.document.getElementById('media-router-container')."
-    "  allSinks;"
-    "var sink = sinks.find(sink => sink.name == '%s');"
-    "window.domAutomationController.send(sink ? sink.id : '');";
-const char kGetRouteIdScript[] =
-    "var routes = window.document.getElementById('media-router-container')."
-    "  routeList;"
-    "var route = routes.find(route => route.sinkId == '%s');"
-    "window.domAutomationController.send(route ? route.id : '');";
-const char kFindSinkScript[] =
-    "var sinkList = document.getElementById('media-router-container')."
-    "  shadowRoot.getElementById('sink-list');"
-    "if (!sinkList) {"
-    "  window.domAutomationController.send(false);"
-    "} else {"
-    "  var sinks = Array.from(sinkList.getElementsByTagName('span'));"
-    "  var result = sinks.some(sink => sink.textContent.trim() == '%s');"
-    "  window.domAutomationController.send(result);"
-    "}";
-const char kCheckDialogLoadedScript[] =
-    "var container = document.getElementById('media-router-container');"
-    "/** Wait until media router container is not undefined and "
-    "*   deviceMissingUrl is not undefined, "
-    "*   once deviceMissingUrl is not undefined, which means "
-    "*   the dialog is fully loaded."
-    "*/"
-    "window.domAutomationController.send(!!container && "
-    "    !!container.deviceMissingUrl);";
 
 std::string GetStartedConnectionId(WebContents* web_contents) {
   std::string session_id;
@@ -141,7 +78,7 @@
   return session_id;
 }
 
-// File Dialog which fails on open
+// File Dialog which fails on open.
 class TestFailMediaRouterFileDialog : public MediaRouterFileDialog {
  public:
   TestFailMediaRouterFileDialog(MediaRouterFileDialogDelegate* delegate,
@@ -193,6 +130,13 @@
   policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
 }
 
+void MediaRouterIntegrationBrowserTest::SetUpOnMainThread() {
+  MediaRouterBaseBrowserTest::SetUpOnMainThread();
+  // Dialogs created while MediaRouterUiForTest exists will not close on blur.
+  test_ui_ =
+      MediaRouterUiForTest::GetOrCreateForWebContents(GetActiveWebContents());
+}
+
 void MediaRouterIntegrationBrowserTest::ExecuteJavaScriptAPI(
     WebContents* web_contents,
     const std::string& script) {
@@ -221,11 +165,11 @@
   OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
   WebContents* web_contents = GetActiveWebContents();
   CHECK(web_contents);
-  StartSession(web_contents);
+  ExecuteJavaScriptAPI(web_contents, kStartSessionScript);
 
-  // Wait for any sinks to be displayed.
+  // Wait to simulate the user waiting for any sinks to be displayed.
   Wait(base::TimeDelta::FromSeconds(1));
-  GetControllerForShownDialog(web_contents)->HideMediaRouterDialog();
+  test_ui_->HideDialog();
   CheckStartFailed(web_contents, "NotFoundError", "No screens found.");
 }
 
@@ -235,19 +179,20 @@
   WebContents* web_contents = GetActiveWebContents();
   CHECK(web_contents);
   ExecuteJavaScriptAPI(web_contents, kWaitSinkScript);
-  StartSession(web_contents);
+  ExecuteJavaScriptAPI(web_contents, kStartSessionScript);
+  test_ui_->WaitForDialogShown();
   return web_contents;
 }
 
 WebContents*
 MediaRouterIntegrationBrowserTest::StartSessionWithTestPageAndChooseSink() {
   WebContents* web_contents = StartSessionWithTestPageAndSink();
-  WaitUntilSinkDiscoveredOnUI();
-  ChooseSink(web_contents, receiver_);
+  test_ui_->WaitForSinkAvailable(receiver_);
+  test_ui_->StartCasting(receiver_);
+  // TODO(takumif): Remove the HideDialog() call once the dialog can close
+  // itself automatically after casting.
+  test_ui_->HideDialog();
 
-  // Wait a few seconds for MediaRouter to receive updates containing the
-  // created route.
-  Wait(base::TimeDelta::FromSeconds(3));
   return web_contents;
 }
 
@@ -258,22 +203,15 @@
       : SetTestData(FILE_PATH_LITERAL("local_media_sink_route_fail.json"));
   GURL file_url = net::FilePathToFileURL(
       media::GetTestDataFilePath("butterfly-853x480.webm"));
-  // Open the dialog, waits for it to load
-  WebContents* dialog_contents = OpenMRDialog(GetActiveWebContents());
-  // Get the media router UI
-  MediaRouterUI* media_router_ui = GetMediaRouterUI(dialog_contents);
+  test_ui_->ShowDialog();
   // Mock out file dialog operations, as those can't be simulated.
-  FileDialogSelectsFile(media_router_ui, file_url);
-  // Open the Cast mode list.
-  ClickHeader(dialog_contents);
+  FileDialogSelectsFile(file_url);
   // Click on the desired mode.
-  ClickCastMode(dialog_contents, MediaCastMode::LOCAL_FILE);
+  test_ui_->ChooseSourceType(CastDialogView::kLocalFile);
   // Wait for the sinks to load.
-  WaitUntilSinkDiscoveredOnUI();
+  test_ui_->WaitForSinkAvailable(receiver_);
   // Click on sink.
-  ChooseSink(GetActiveWebContents(), receiver_);
-  // Give casting a few seconds to go through.
-  Wait(base::TimeDelta::FromSeconds(3));
+  test_ui_->StartCasting(receiver_);
   // Expect that the current tab has the file open in it.
   ASSERT_EQ(file_url, GetActiveWebContents()->GetURL());
 }
@@ -282,18 +220,13 @@
   SetTestData(FILE_PATH_LITERAL("local_media_sink.json"));
   GURL file_url = net::FilePathToFileURL(
       media::GetTestDataFilePath("butterfly-853x480.webm"));
-  // Open the dialog, waits for it to load
-  WebContents* dialog_contents = OpenMRDialog(GetActiveWebContents());
-  // Get the media router UI
-  MediaRouterUI* media_router_ui = GetMediaRouterUI(dialog_contents);
+  test_ui_->ShowDialog();
   // Mock out file dialog opperations, as those can't be simulated.
-  FileDialogSelectFails(media_router_ui, IssueInfo());
-  // Open the Cast mode list.
-  ClickHeader(dialog_contents);
+  FileDialogSelectFails(IssueInfo());
   // Click on the desired mode.
-  ClickCastMode(dialog_contents, MediaCastMode::LOCAL_FILE);
+  test_ui_->ChooseSourceType(CastDialogView::kLocalFile);
   // Wait for the issue to appear.
-  WaitUntilIssue();
+  test_ui_->WaitForAnyIssue();
 }
 
 void MediaRouterIntegrationBrowserTest::OpenTestPage(
@@ -309,6 +242,10 @@
       browser(), GetTestPageUrl(full_path),
       WindowOpenDisposition::NEW_FOREGROUND_TAB,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  // Opening a new tab creates new WebContents, so we must re-configure the
+  // test UI for it.
+  test_ui_ =
+      MediaRouterUiForTest::GetOrCreateForWebContents(GetActiveWebContents());
 }
 
 GURL MediaRouterIntegrationBrowserTest::GetTestPageUrl(
@@ -316,34 +253,6 @@
   return net::FilePathToFileURL(full_path);
 }
 
-void MediaRouterIntegrationBrowserTest::StartSession(
-    WebContents* web_contents) {
-  test_navigation_observer_.reset(
-      new content::TestNavigationObserver(web_contents, 1));
-  test_navigation_observer_->StartWatchingNewWebContents();
-  ExecuteJavaScriptAPI(web_contents, kStartSessionScript);
-  test_navigation_observer_->Wait();
-  test_navigation_observer_->StopWatchingNewWebContents();
-}
-
-void MediaRouterIntegrationBrowserTest::ChooseSink(
-    WebContents* web_contents,
-    const std::string& sink_name) {
-  WebContents* dialog_contents = GetMRDialog(web_contents);
-  std::string script = base::StringPrintf(
-      kChooseSinkScript, sink_name.c_str());
-  // Execute javascript to choose sink, but don't wait until it finishes.
-  dialog_contents->GetMainFrame()->ExecuteJavaScriptWithUserGestureForTests(
-      base::UTF8ToUTF16(script));
-}
-
-void MediaRouterIntegrationBrowserTest::ClickCastMode(
-    WebContents* dialog_contents,
-    MediaCastMode mode) {
-  std::string script = base::StringPrintf(kClickCastModeScript, mode);
-  ASSERT_TRUE(content::ExecuteScript(dialog_contents, script));
-}
-
 void MediaRouterIntegrationBrowserTest::CheckStartFailed(
     WebContents* web_contents,
     const std::string& error_name,
@@ -354,52 +263,6 @@
   ExecuteJavaScriptAPI(web_contents, script);
 }
 
-void MediaRouterIntegrationBrowserTest::ClickDialog() {
-  WebContents* web_contents = GetActiveWebContents();
-  WebContents* dialog_contents = GetMRDialog(web_contents);
-  ASSERT_TRUE(content::ExecuteScript(dialog_contents, kClickDialog));
-}
-
-void MediaRouterIntegrationBrowserTest::ClickHeader(
-    WebContents* dialog_contents) {
-  ASSERT_TRUE(content::ExecuteScript(dialog_contents, kClickHeader));
-}
-
-WebContents* MediaRouterIntegrationBrowserTest::GetMRDialog(
-    WebContents* web_contents) {
-  MediaRouterDialogControllerWebUIImpl* controller =
-      MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
-          web_contents);
-  WebContents* dialog_contents = controller->GetMediaRouterDialog();
-  CHECK(dialog_contents);
-  WaitUntilDialogFullyLoaded(dialog_contents);
-  return dialog_contents;
-}
-
-bool MediaRouterIntegrationBrowserTest::IsDialogClosed(
-    WebContents* web_contents) {
-  MediaRouterDialogControllerWebUIImpl* controller =
-      MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
-          web_contents);
-  return !controller->GetMediaRouterDialog();
-}
-
-void MediaRouterIntegrationBrowserTest::WaitUntilDialogClosed(
-    WebContents* web_contents) {
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(5), base::TimeDelta::FromSeconds(1),
-      base::Bind(&MediaRouterIntegrationBrowserTest::IsDialogClosed,
-                 base::Unretained(this), web_contents)));
-}
-
-void MediaRouterIntegrationBrowserTest::CheckDialogRemainsOpen(
-    WebContents* web_contents) {
-  ASSERT_FALSE(ConditionalWait(
-      base::TimeDelta::FromSeconds(5), base::TimeDelta::FromSeconds(1),
-      base::Bind(&MediaRouterIntegrationBrowserTest::IsDialogClosed,
-                 base::Unretained(this), web_contents)));
-}
-
 void MediaRouterIntegrationBrowserTest::SetTestData(
     base::FilePath::StringPieceType test_data_file) {
   base::FilePath full_path = GetResourceFile(test_data_file);
@@ -421,23 +284,6 @@
                          test_data_str.c_str()));
 }
 
-WebContents* MediaRouterIntegrationBrowserTest::OpenMRDialog(
-    WebContents* web_contents) {
-  MediaRouterDialogControllerWebUIImpl* controller =
-      MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
-          web_contents);
-  test_navigation_observer_.reset(
-        new content::TestNavigationObserver(web_contents, 1));
-  test_navigation_observer_->StartWatchingNewWebContents();
-  CHECK(controller->ShowMediaRouterDialog());
-  test_navigation_observer_->Wait();
-  test_navigation_observer_->StopWatchingNewWebContents();
-  WebContents* dialog_contents = controller->GetMediaRouterDialog();
-  CHECK(dialog_contents);
-  WaitUntilDialogFullyLoaded(dialog_contents);
-  return dialog_contents;
-}
-
 base::FilePath MediaRouterIntegrationBrowserTest::GetResourceFile(
     base::FilePath::StringPieceType relative_path) const {
   base::FilePath base_dir;
@@ -481,110 +327,22 @@
 }
 
 bool MediaRouterIntegrationBrowserTest::IsRouteCreatedOnUI() {
-  return !GetRouteId(receiver()).empty();
-}
-
-std::string MediaRouterIntegrationBrowserTest::GetRouteId(
-    const std::string& sink_name) {
-  WebContents* web_contents = GetActiveWebContents();
-  WebContents* dialog_contents = GetMRDialog(web_contents);
-  std::string script = base::StringPrintf(kGetSinkIdScript, sink_name.c_str());
-  std::string sink_id = ExecuteScriptAndExtractString(dialog_contents, script);
-  DVLOG(0) << "sink id: " << sink_id;
-  script = base::StringPrintf(kGetRouteIdScript, sink_id.c_str());
-  std::string route_id = ExecuteScriptAndExtractString(dialog_contents, script);
-  DVLOG(0) << "route id: " << route_id;
-  return route_id;
-}
-
-void MediaRouterIntegrationBrowserTest::WaitUntilRouteCreated() {
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(10), base::TimeDelta::FromSeconds(1),
-      base::Bind(&MediaRouterIntegrationBrowserTest::IsRouteCreatedOnUI,
-                 base::Unretained(this))));
+  return !test_ui_->GetRouteIdForSink(receiver_).empty();
 }
 
 bool MediaRouterIntegrationBrowserTest::IsUIShowingIssue() {
-  WebContents* web_contents = GetActiveWebContents();
-  WebContents* dialog_contents = GetMRDialog(web_contents);
-  std::string script = base::StringPrintf(
-      "domAutomationController.send(window.document.getElementById("
-      "'media-router-container').issue != undefined)");
-  bool has_issue = false;
-  CHECK(content::ExecuteScriptAndExtractBool(dialog_contents, script,
-                                             &has_issue));
-  return has_issue;
-}
-
-void MediaRouterIntegrationBrowserTest::WaitUntilIssue() {
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
-      base::Bind(&MediaRouterIntegrationBrowserTest::IsUIShowingIssue,
-                 base::Unretained(this))));
-}
-
-std::string MediaRouterIntegrationBrowserTest::GetIssueTitle() {
-  WebContents* web_contents = GetActiveWebContents();
-  WebContents* dialog_contents = GetMRDialog(web_contents);
-  std::string script = base::StringPrintf(
-      "domAutomationController.send(window.document.getElementById("
-      "'media-router-container').issue.title)");
-  return ExecuteScriptAndExtractString(dialog_contents, script);
+  std::string issue_text = test_ui_->GetIssueTextForSink(receiver_);
+  return !issue_text.empty();
 }
 
 bool MediaRouterIntegrationBrowserTest::IsRouteClosedOnUI() {
   // After execute js script to close route on UI, the dialog will dispear
   // after 3s. But sometimes it takes more than 3s to close the route, so
   // we need to re-open the dialog if it is closed.
-  WebContents* web_contents = GetActiveWebContents();
-  MediaRouterDialogControllerWebUIImpl* controller =
-      MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
-          web_contents);
-  WebContents* dialog_contents = controller->GetMediaRouterDialog();
-  if (!dialog_contents) {
-    VLOG(0) << "Media router dialog was closed, reopen it again.";
-    OpenMRDialog(web_contents);
-  }
-  return GetRouteId(receiver()).empty();
-}
-
-void MediaRouterIntegrationBrowserTest::CloseRouteOnUI() {
-  WebContents* web_contents = GetActiveWebContents();
-  WebContents* dialog_contents = GetMRDialog(web_contents);
-  ASSERT_TRUE(content::ExecuteScript(dialog_contents, kCloseRouteScript));
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(10), base::TimeDelta::FromSeconds(1),
-      base::Bind(&MediaRouterIntegrationBrowserTest::IsRouteClosedOnUI,
-                 base::Unretained(this))));
-}
-
-bool MediaRouterIntegrationBrowserTest::IsSinkDiscoveredOnUI() {
-  WebContents* web_contents = GetActiveWebContents();
-  WebContents* dialog_contents = GetMRDialog(web_contents);
-  std::string script = base::StringPrintf(kFindSinkScript, receiver().c_str());
-  return ExecuteScriptAndExtractBool(dialog_contents, script);
-}
-
-void MediaRouterIntegrationBrowserTest::WaitUntilSinkDiscoveredOnUI() {
-  DVLOG(0) << "Receiver name: " << receiver_;
-  // Wait for sink to show up in UI.
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
-      base::Bind(&MediaRouterIntegrationBrowserTest::IsSinkDiscoveredOnUI,
-                 base::Unretained(this))));
-}
-
-bool MediaRouterIntegrationBrowserTest::IsDialogLoaded(
-    WebContents* dialog_contents) {
-  return ExecuteScriptAndExtractBool(dialog_contents, kCheckDialogLoadedScript);
-}
-
-void MediaRouterIntegrationBrowserTest::WaitUntilDialogFullyLoaded(
-    WebContents* dialog_contents) {
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
-      base::Bind(&MediaRouterIntegrationBrowserTest::IsDialogLoaded,
-                 base::Unretained(this), dialog_contents)));
+  if (!test_ui_->IsDialogShown())
+    test_ui_->ShowDialog();
+  test_ui_->WaitForSink(receiver_);
+  return test_ui_->GetRouteIdForSink(receiver_).empty();
 }
 
 void MediaRouterIntegrationBrowserTest::ParseCommandLine() {
@@ -606,49 +364,26 @@
   EXPECT_EQ(session_id, default_request_session_id);
 }
 
-MediaRouterDialogControllerWebUIImpl*
-MediaRouterIntegrationBrowserTest::GetControllerForShownDialog(
-    WebContents* web_contents) {
-  MediaRouterDialogControllerWebUIImpl* controller =
-      MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
-          web_contents);
-  EXPECT_TRUE(controller->IsShowingMediaRouterDialog());
-  return controller;
-}
-
 WebContents* MediaRouterIntegrationBrowserTest::GetActiveWebContents() {
   return browser()->tab_strip_model()->GetActiveWebContents();
 }
 
-MediaRouterUI* MediaRouterIntegrationBrowserTest::GetMediaRouterUI(
-    content::WebContents* dialog_contents) {
-  content::WebUI* web_ui = dialog_contents->GetWebUI();
-  CHECK(web_ui) << "Error getting MediaRouterUI, no WebUI at all";
-  return static_cast<MediaRouterUI*>(web_ui->GetController());
-}
-
-void MediaRouterIntegrationBrowserTest::FileDialogSelectsFile(
-    MediaRouterUI* media_router_ui,
-    GURL file_url) {
-  // Ensure that the media_router_ui is set
-  DCHECK(media_router_ui);
-  media_router_ui->InitForTest(
-      std::make_unique<TestMediaRouterFileDialog>(media_router_ui, file_url));
+void MediaRouterIntegrationBrowserTest::FileDialogSelectsFile(GURL file_url) {
+  // TODO(https://crbug.com/900248): Implement this.
+  NOTIMPLEMENTED();
 }
 
 void MediaRouterIntegrationBrowserTest::FileDialogSelectFails(
-    MediaRouterUI* media_router_ui,
     const IssueInfo& issue) {
-  // Ensure that the media_router_ui is set
-  DCHECK(media_router_ui);
-  media_router_ui->InitForTest(
-      std::make_unique<TestFailMediaRouterFileDialog>(media_router_ui, issue));
+  // TODO(https://crbug.com/900248): Implement this.
+  NOTIMPLEMENTED();
 }
 
 void MediaRouterIntegrationBrowserTest::RunBasicTest() {
   WebContents* web_contents = StartSessionWithTestPageAndChooseSink();
   CheckSessionValidity(web_contents);
   ExecuteJavaScriptAPI(web_contents, kTerminateSessionScript);
+  test_ui_->WaitUntilNoRoutes();
 }
 
 void MediaRouterIntegrationBrowserTest::RunSendMessageTest(
@@ -690,6 +425,7 @@
   ASSERT_EQ(session_id, reconnected_session_id);
 
   ExecuteJavaScriptAPI(web_contents, kTerminateSessionScript);
+  test_ui_->WaitUntilNoRoutes();
 }
 
 void MediaRouterIntegrationBrowserTest::SetEnableMediaRouter(bool enable) {
@@ -698,8 +434,7 @@
              policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
              std::make_unique<base::Value>(enable), nullptr);
   provider_.UpdateChromePolicy(policy);
-  base::RunLoop loop;
-  loop.RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
 }
 
 void MediaRouterIntegrationBrowserTest::RunReconnectSessionSameTabTest() {
@@ -718,15 +453,14 @@
   ASSERT_EQ(session_id, reconnected_session_id);
 }
 
-// TODO(crbug.com/822337): Flaky on Linux_CFI bot.
-IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, MANUAL_Basic) {
+IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, Basic) {
   RunBasicTest();
 }
 
 // Tests that creating a route with a local file opens the file in a new tab.
-// TODO(crbug.com/818767): Fails when run with Chromium component.
+// TODO(https://crbug.com/900248): Make this test pass with the Views dialog.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       MANUAL_OpenLocalMediaFileInCurrentTab) {
+                       DISABLED_OpenLocalMediaFileInCurrentTab) {
   // Start at a new tab, the file should open in the same tab.
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
   // Make sure there is 1 tab.
@@ -737,20 +471,18 @@
   // Expect that no new tab has been opened.
   ASSERT_EQ(1, browser()->tab_strip_model()->count());
 
-  // Open the dialog again to check for a route.
-  OpenMRDialog(GetActiveWebContents());
+  test_ui_->ShowDialog();
 
   // Wait for a route to be created.
-  WaitUntilRouteCreated();
+  test_ui_->WaitForAnyRoute();
 }
 
-// TODO(crbug.com/849216): Disabled due to crashes and timeouts.
-//
 // Tests that creating a route with a local file opens the file in a new tab.
+// TODO(https://crbug.com/900248): Make this test pass with the Views dialog.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
                        DISABLED_OpenLocalMediaFileInNewTab) {
   // Start at a tab with content in it, the file will open in a new tab.
-  ui_test_utils::NavigateToURL(browser(), GURL("http://google.com"));
+  ui_test_utils::NavigateToURL(browser(), GURL("https://google.com"));
   // Make sure there is 1 tab.
   ASSERT_EQ(1, browser()->tab_strip_model()->count());
 
@@ -759,25 +491,25 @@
   // Expect that a new tab has been opened.
   ASSERT_EQ(2, browser()->tab_strip_model()->count());
 
-  // Open the dialog again to check for a route.
-  OpenMRDialog(GetActiveWebContents());
+  test_ui_->ShowDialog();
 
   // Wait for a route to be created.
-  WaitUntilRouteCreated();
+  test_ui_->WaitForAnyRoute();
 }
 
 // Tests that failing to create a route with a local file shows an issue.
+// TODO(https://crbug.com/900248): Make this test pass with the Views dialog.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       OpenLocalMediaFileFailsAndShowsIssue) {
+                       DISABLED_OpenLocalMediaFileFailsAndShowsIssue) {
   OpenDialogAndCastFileFails();
   // Expect that the issue is showing.
   ASSERT_TRUE(IsUIShowingIssue());
 }
 
 // Tests that creating a route with a local file opens in fullscreen.
-// TODO(crbug.com/822029): Fails on msan; fix and re-enable.
+// TODO(https://crbug.com/900248): Make this test pass with the Views dialog.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       MANUAL_OpenLocalMediaFileFullscreen) {
+                       DISABLED_OpenLocalMediaFileFullscreen) {
   // Start at a new tab, the file should open in the same tab.
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
   // Make sure there is 1 tab.
@@ -799,8 +531,9 @@
 }
 
 // Tests that failed route creation of local file does not enter fullscreen.
+// TODO(https://crbug.com/900248): Make this test pass with the Views dialog.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       OpenLocalMediaFileCastFailNoFullscreen) {
+                       DISABLED_OpenLocalMediaFileCastFailNoFullscreen) {
   // Start at a new tab, the file should open in the same tab.
   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
   // Make sure there is 1 tab.
@@ -836,8 +569,9 @@
   RunFailToSendMessageTest();
 }
 
+// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       DISABLED_Fail_NoProvider) {
+                       MANUAL_Fail_NoProvider) {
   SetTestData(FILE_PATH_LITERAL("no_provider.json"));
   WebContents* web_contents = StartSessionWithTestPageAndChooseSink();
   CheckStartFailed(web_contents, "UnknownError",
@@ -850,20 +584,12 @@
   CheckStartFailed(web_contents, "UnknownError", "Unknown sink");
 }
 
-// TODO(crbug.com/822231): Flaky in Chromium waterfall.
-IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       MANUAL_ReconnectSession) {
+IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, ReconnectSession) {
   RunReconnectSessionTest();
 }
 
-// Flaky on Linux MSAN. https://crbug.com/840165
-#if defined(OS_LINUX) && defined(MEMORY_SANITIZER)
-#define MAYBE_Fail_ReconnectSession DISABLED_Fail_ReconnectSession
-#else
-#define MAYBE_Fail_ReconnectSession Fail_ReconnectSession
-#endif
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       MAYBE_Fail_ReconnectSession) {
+                       Fail_ReconnectSession) {
   WebContents* web_contents = StartSessionWithTestPageAndChooseSink();
   CheckSessionValidity(web_contents);
   std::string session_id(GetStartedConnectionId(web_contents));
@@ -880,7 +606,7 @@
 
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, Fail_StartCancelled) {
   WebContents* web_contents = StartSessionWithTestPageAndSink();
-  GetControllerForShownDialog(web_contents)->HideMediaRouterDialog();
+  test_ui_->HideDialog();
   CheckStartFailed(web_contents, "NotAllowedError", "Dialog closed.");
 }
 
@@ -914,21 +640,21 @@
   return incognito_browser_;
 }
 
-// Test is flaky on Linux. (https://crbug.com/853167)
-#if defined(OS_LINUX)
-#define MAYBE_Basic DISABLED_Basic
-#else
-#define MAYBE_Basic Basic
-#endif
-IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationIncognitoBrowserTest,
-                       MAYBE_Basic) {
+IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationIncognitoBrowserTest, Basic) {
   RunBasicTest();
+  // If we tear down before route observers are notified of route termination,
+  // MediaRouter will create another TerminateRoute() request which will have a
+  // dangling Mojo callback at shutdown. So we must wait for the update.
+  test_ui_->WaitUntilNoRoutes();
 }
 
-// TODO(crbug.com/822300): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationIncognitoBrowserTest,
-                       MANUAL_ReconnectSession) {
+                       ReconnectSession) {
   RunReconnectSessionTest();
+  // If we tear down before route observers are notified of route termination,
+  // MediaRouter will create another TerminateRoute() request which will have a
+  // dangling Mojo callback at shutdown. So we must wait for the update.
+  test_ui_->WaitUntilNoRoutes();
 }
 
 }  // namespace media_router
diff --git a/chrome/test/media_router/media_router_integration_browsertest.h b/chrome/test/media_router/media_router_integration_browsertest.h
index db7f3e6..087039f4 100644
--- a/chrome/test/media_router/media_router_integration_browsertest.h
+++ b/chrome/test/media_router/media_router_integration_browsertest.h
@@ -14,14 +14,13 @@
 #include "chrome/browser/ui/media_router/media_cast_mode.h"
 #include "chrome/browser/ui/toolbar/media_router_action.h"
 #include "chrome/test/media_router/media_router_base_browsertest.h"
+#include "chrome/test/media_router/media_router_ui_for_test.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 
 namespace media_router {
 
-class MediaRouterDialogControllerWebUIImpl;
-class MediaRouterUI;
 struct IssueInfo;
 
 class MediaRouterIntegrationBrowserTest : public MediaRouterBaseBrowserTest {
@@ -33,23 +32,11 @@
   // InProcessBrowserTest Overrides
   void TearDownOnMainThread() override;
   void SetUpInProcessBrowserTestFixture() override;
+  void SetUpOnMainThread() override;
 
   // MediaRouterBaseBrowserTest Overrides
   void ParseCommandLine() override;
 
-  // Simulate user action to choose one sink in the popup dialog.
-  // |web_contents|: The web contents of the test page which invokes the popup
-  //                 dialog.
-  // |sink_name|: The sink's human readable name.
-  void ChooseSink(content::WebContents* web_contents,
-                  const std::string& sink_name);
-
-  // Simulate user action to choose the local media cast mode in the popup
-  // dialog. This will not work unless the dialog is in the choose cast mode
-  // view. |dialog_contents|: The web contents of the popup dialog.
-  // |cast_mode_text|: The cast mode's dialog name.
-  void ClickCastMode(content::WebContents* dialog_contents, MediaCastMode mode);
-
   // Checks that the request initiated from |web_contents| to start presentation
   // failed with expected |error_name| and |error_message_substring|.
   void CheckStartFailed(content::WebContents* web_contents,
@@ -67,12 +54,6 @@
   static std::string ExecuteScriptAndExtractString(
       const content::ToRenderFrameHost& adapter, const std::string& script);
 
-  void ClickDialog();
-
-  // Clicks on the header of the dialog. If in sinks view, will change to cast
-  // mode view, if in cast mode view, will change to sinks view.
-  void ClickHeader(content::WebContents* dialog_contents);
-
   static bool ExecuteScriptAndExtractBool(
       const content::ToRenderFrameHost& adapter,
       const std::string& script);
@@ -80,17 +61,6 @@
   static void ExecuteScript(const content::ToRenderFrameHost& adapter,
                             const std::string& script);
 
-  // Get the chrome modal dialog.
-  // |web_contents|: The web contents of the test page which invokes the popup
-  //                 dialog.
-  content::WebContents* GetMRDialog(content::WebContents* web_contents);
-
-  // Checks that the chrome modal dialog does not exist.
-  bool IsDialogClosed(content::WebContents* web_contents);
-  void WaitUntilDialogClosed(content::WebContents* web_contents);
-
-  void CheckDialogRemainsOpen(content::WebContents* web_contents);
-
   // Opens "basic_test.html" and asserts that attempting to start a presentation
   // fails with NotFoundError due to no sinks available.
   void StartSessionAndAssertNotFoundError();
@@ -121,76 +91,30 @@
 
   void SetTestData(base::FilePath::StringPieceType test_data_file);
 
-  // Start presentation and wait until the pop dialog shows up.
-  // |web_contents|: The web contents of the test page which invokes the popup
-  //                 dialog.
-  void StartSession(content::WebContents* web_contents);
-
-  // Open the chrome modal dialog.
-  // |web_contents|: The web contents of the test page which invokes the popup
-  //                 dialog.
-  content::WebContents* OpenMRDialog(content::WebContents* web_contents);
   bool IsRouteCreatedOnUI();
 
   bool IsRouteClosedOnUI();
 
-  bool IsSinkDiscoveredOnUI();
-
-  // Close route through clicking 'Stop casting' button in route details dialog.
-  void CloseRouteOnUI();
-
-  // Wait for the route to show up in the UI with a timeout. Fails if the
-  // route did not show up before the timeout.
-  void WaitUntilRouteCreated();
-
-  // Wait until there is an issue showing in the UI.
-  void WaitUntilIssue();
-
   // Returns true if there is an issue showing in the UI.
   bool IsUIShowingIssue();
 
-  // Returns the title of issue showing in UI. It is an error to call this if
-  // there are no issues showing in UI.
-  std::string GetIssueTitle();
-
   // Returns the route ID for the specific sink.
   std::string GetRouteId(const std::string& sink_id);
 
-  // Wait for the specific sink shows up in UI with a timeout. Fails if the sink
-  // doesn't show up before the timeout.
-  void WaitUntilSinkDiscoveredOnUI();
-
-  // Checks if media router dialog is fully loaded.
-  bool IsDialogLoaded(content::WebContents* dialog_contents);
-
-  // Wait until media router dialog is fully loaded.
-  void WaitUntilDialogFullyLoaded(content::WebContents* dialog_contents);
-
   // Checks that the presentation started for |web_contents| has connected and
   // is the default presentation.
   void CheckSessionValidity(content::WebContents* web_contents);
 
-  // Checks that a Media Router dialog is shown for |web_contents|, and returns
-  // its controller.
-  MediaRouterDialogControllerWebUIImpl* GetControllerForShownDialog(
-      content::WebContents* web_contents);
-
   // Returns the active WebContents for the current window.
   content::WebContents* GetActiveWebContents();
 
-  // Gets the MediaRouterUI that is associated with the open dialog.
-  // This is needed to bypass potential issues that may arise when trying to
-  // test code that uses the native file dialog.
-  MediaRouterUI* GetMediaRouterUI(content::WebContents* media_router_dialog);
-
   // Sets the MediaRouterFileDialog to act like a valid file was selected on
   // opening the dialog.
-  void FileDialogSelectsFile(MediaRouterUI* media_router_ui, GURL file_url);
+  void FileDialogSelectsFile(GURL file_url);
 
   // Sets the MediaRouterFileDialog to act like a bad file was selected on
   // opening the dialog.
-  void FileDialogSelectFails(MediaRouterUI* media_router_ui,
-                             const IssueInfo& issue);
+  void FileDialogSelectFails(const IssueInfo& issue);
 
   // Runs a basic test in which a presentation is created through the
   // MediaRouter dialog, then terminated.
@@ -213,11 +137,15 @@
   // Sets whether media router is enabled.
   void SetEnableMediaRouter(bool enable);
 
-  std::string receiver() const { return receiver_; }
+  // Test API for manipulating the UI.
+  MediaRouterUiForTest* test_ui_ = nullptr;
 
-  // Enabled features
+  // Enabled features.
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  // Name of the test receiver to use.
+  std::string receiver_;
+
  private:
   // Get the full path of the resource file.
   // |relative_path|: The relative path to
@@ -228,14 +156,11 @@
 
   std::unique_ptr<content::TestNavigationObserver> test_navigation_observer_;
   policy::MockConfigurationPolicyProvider provider_;
-
-  // Fields
-  std::string receiver_;
 };
 
 class MediaRouterIntegrationIncognitoBrowserTest
     : public MediaRouterIntegrationBrowserTest {
- public:
+ protected:
   void InstallAndEnableMRExtension() override;
   void UninstallMRExtension() override;
   Browser* browser() override;
diff --git a/chrome/test/media_router/media_router_integration_ui_browsertest.cc b/chrome/test/media_router/media_router_integration_ui_browsertest.cc
index 77b2a76e..33fc5bd 100644
--- a/chrome/test/media_router/media_router_integration_ui_browsertest.cc
+++ b/chrome/test/media_router/media_router_integration_ui_browsertest.cc
@@ -17,150 +17,37 @@
 
 namespace media_router {
 
-namespace {
-const char kTestSinkName[] = "test-sink-1";
-}
-
-// Disabled due to flakiness: https://crbug.com/873912.
-#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
-#define MAYBE_Dialog_Basic DISABLED_Dialog_Basic
-#else
-#define MAYBE_Dialog_Basic Dialog_Basic
-#endif
-IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, MAYBE_Dialog_Basic) {
+// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
+IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest, MANUAL_Dialog_Basic) {
   OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  content::WebContents* dialog_contents = OpenMRDialog(web_contents);
-  WaitUntilSinkDiscoveredOnUI();
-  // Verify the sink list.
-  std::string sink_length_script = base::StringPrintf(
-      "domAutomationController.send("
-      "window.document.getElementById('media-router-container')."
-      "sinksToShow_.length)");
-  ASSERT_GT(ExecuteScriptAndExtractInt(dialog_contents, sink_length_script), 0);
-  LOG(INFO) << "Choose Sink";
-  ChooseSink(web_contents, kTestSinkName);
+  test_ui_->ShowDialog();
+  test_ui_->WaitForSinkAvailable(receiver_);
+  test_ui_->StartCasting(receiver_);
+  test_ui_->WaitForAnyRoute();
 
-// Linux and Windows bots run browser tests without a physical display, which
-// is causing flaky event dispatching of mouseenter and mouseleave events. This
-// causes the dialog to sometimes close prematurely even though a mouseenter
-// event is explicitly dispatched in the test.
-// Here, we still dispatch the mouseenter event for OSX, but close
-// the dialog and reopen it on Linux and Windows.
-// The test succeeds fine when run with a physical display.
-// http://crbug.com/577943 http://crbug.com/591779
-#if defined(OS_MACOSX)
-  // Simulate keeping the mouse on the dialog to prevent it from automatically
-  // closing after the route has been created. Then, check that the dialog
-  // remains open.
-  std::string mouse_enter_script = base::StringPrintf(
-      "window.document.getElementById('media-router-container')"
-      "    .dispatchEvent(new Event('mouseenter'));");
-  ASSERT_TRUE(content::ExecuteScript(dialog_contents, mouse_enter_script));
-#endif
-  WaitUntilRouteCreated();
+  if (!test_ui_->IsDialogShown())
+    test_ui_->ShowDialog();
 
-#if defined(OS_MACOSX)
-  CheckDialogRemainsOpen(web_contents);
-#elif defined(OS_LINUX) || defined(OS_WIN)
-  Wait(base::TimeDelta::FromSeconds(5));
-  LOG(INFO) << "Waiting for dialog to be closed";
-  WaitUntilDialogClosed(web_contents);
-  LOG(INFO) << "Reopen MR dialog";
-  dialog_contents = OpenMRDialog(web_contents);
-#endif
+  ASSERT_EQ("Test Route", test_ui_->GetStatusTextForSink(receiver_));
 
-  LOG(INFO) << "Check route details dialog";
-  std::string route_script;
-  // Verify the route details is not undefined.
-  route_script = base::StringPrintf(
-      "domAutomationController.send("
-      "window.document.getElementById('media-router-container').shadowRoot."
-      "getElementById('route-details') != undefined)");
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
-      base::Bind(
-        &MediaRouterIntegrationBrowserTest::ExecuteScriptAndExtractBool,
-        dialog_contents, route_script)));
-  route_script = base::StringPrintf(
-      "domAutomationController.send("
-      "window.document.getElementById('media-router-container').currentView_ "
-      "== media_router.MediaRouterView.ROUTE_DETAILS)");
-  ASSERT_TRUE(ExecuteScriptAndExtractBool(dialog_contents, route_script));
-
-  // Verify the route details page.
-  route_script = base::StringPrintf(
-      "domAutomationController.send("
-      "window.document.getElementById('media-router-container').shadowRoot."
-      "getElementById('route-details').shadowRoot.getElementById("
-      "'route-description').innerText)");
-  std::string route_information = ExecuteScriptAndExtractString(
-      dialog_contents, route_script);
-  ASSERT_EQ("Test Route", route_information);
-
-  std::string sink_script;
-  // Verify the container header is not undefined.
-  sink_script = base::StringPrintf(
-      "domAutomationController.send("
-      "window.document.getElementById('media-router-container').shadowRoot."
-      "getElementById('container-header') != undefined)");
-  LOG(INFO) << "Checking container-header";
-  ASSERT_TRUE(ConditionalWait(
-      base::TimeDelta::FromSeconds(30), base::TimeDelta::FromSeconds(1),
-      base::Bind(
-        &MediaRouterIntegrationBrowserTest::ExecuteScriptAndExtractBool,
-        dialog_contents, sink_script)));
-
-  sink_script = base::StringPrintf(
-      "domAutomationController.send("
-      "window.document.getElementById('media-router-container').shadowRoot."
-      "getElementById('container-header').shadowRoot.getElementById("
-      "'header-text').innerText)");
-  std::string sink_name = ExecuteScriptAndExtractString(
-      dialog_contents, sink_script);
-  ASSERT_EQ(kTestSinkName, sink_name);
-  LOG(INFO) << "Finish verification";
-
-#if defined(OS_MACOSX)
-  // Simulate moving the mouse off the dialog. Confirm that the dialog closes
-  // automatically after the route is closed.
-  // In tests, it sometimes takes too long to CloseRouteOnUI() to finish so
-  // the timer started when the route is initially closed times out before the
-  // mouseleave event is dispatched. In that case, the dialog remains open.
-  std::string mouse_leave_script = base::StringPrintf(
-      "window.document.getElementById('media-router-container')"
-      "    .dispatchEvent(new Event('mouseleave'));");
-  ASSERT_TRUE(content::ExecuteScript(dialog_contents, mouse_leave_script));
-#endif
-  LOG(INFO) << "Closing route on UI";
-  CloseRouteOnUI();
-#if defined(OS_MACOSX)
-  LOG(INFO) << "Waiting for dialog to be closed";
-  WaitUntilDialogClosed(web_contents);
-#endif
-  LOG(INFO) << "Closed dialog, end of test";
+  test_ui_->StopCasting(receiver_);
+  test_ui_->WaitUntilNoRoutes();
+  // TODO(takumif): Remove the HideDialog() call once the dialog can close on
+  // its own.
+  test_ui_->HideDialog();
 }
 
-// TODO(crbug.com/822301): Flaky in Chromium waterfall.
+// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
                        MANUAL_Dialog_RouteCreationTimedOut) {
   SetTestData(FILE_PATH_LITERAL("route_creation_timed_out.json"));
   OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  content::WebContents* dialog_contents = OpenMRDialog(web_contents);
-
-  // Verify the sink list.
-  std::string sink_length_script = base::StringPrintf(
-      "domAutomationController.send("
-      "window.document.getElementById('media-router-container')."
-      "sinksToShow_.length)");
-  ASSERT_GT(ExecuteScriptAndExtractInt(dialog_contents, sink_length_script), 0);
+  test_ui_->ShowDialog();
+  test_ui_->WaitForSinkAvailable(receiver_);
 
   base::TimeTicks start_time(base::TimeTicks::Now());
-  ChooseSink(web_contents, kTestSinkName);
-  WaitUntilIssue();
+  test_ui_->StartCasting(receiver_);
+  test_ui_->WaitForAnyIssue();
 
   base::TimeDelta elapsed(base::TimeTicks::Now() - start_time);
   // The hardcoded timeout route creation timeout for the UI.
@@ -170,7 +57,7 @@
   EXPECT_GE(elapsed, expected_timeout);
   EXPECT_LE(elapsed - expected_timeout, base::TimeDelta::FromSeconds(5));
 
-  std::string issue_title = GetIssueTitle();
+  std::string issue_title = test_ui_->GetIssueTextForSink(receiver_);
   // TODO(imcheng): Fix host name for file schemes (crbug.com/560576).
   ASSERT_EQ(
       l10n_util::GetStringFUTF8(IDS_MEDIA_ROUTER_ISSUE_CREATE_ROUTE_TIMEOUT,
@@ -178,7 +65,8 @@
       issue_title);
 
   // Route will still get created, it just takes longer than usual.
-  WaitUntilRouteCreated();
+  test_ui_->WaitForAnyRoute();
+  test_ui_->HideDialog();
 }
 
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
@@ -191,33 +79,22 @@
   // Enable media routing and open media router dialog.
   SetEnableMediaRouter(true);
   OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  OpenMRDialog(web_contents);
-
-  MediaRouterDialogControllerWebUIImpl* controller =
-      MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
-          web_contents);
-  ASSERT_TRUE(controller->IsShowingMediaRouterDialog());
+  test_ui_->ShowDialog();
+  ASSERT_TRUE(test_ui_->IsDialogShown());
+  test_ui_->HideDialog();
 }
 
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
                        DisableMediaRoutingWhenDialogIsOpened) {
   // Open media router dialog.
   OpenTestPage(FILE_PATH_LITERAL("basic_test.html"));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  OpenMRDialog(web_contents);
-
-  MediaRouterDialogControllerWebUIImpl* controller =
-      MediaRouterDialogControllerWebUIImpl::GetOrCreateForWebContents(
-          web_contents);
-  ASSERT_TRUE(controller->IsShowingMediaRouterDialog());
+  test_ui_->ShowDialog();
+  ASSERT_TRUE(test_ui_->IsDialogShown());
 
   // Disable media routing.
   SetEnableMediaRouter(false);
 
-  ASSERT_FALSE(controller->IsShowingMediaRouterDialog());
+  ASSERT_FALSE(test_ui_->IsDialogShown());
 }
 
 }  // namespace media_router
diff --git a/chrome/test/media_router/media_router_one_ua_integration_browsertest.cc b/chrome/test/media_router/media_router_one_ua_integration_browsertest.cc
index 270e01a..aaf4ad6f 100644
--- a/chrome/test/media_router/media_router_one_ua_integration_browsertest.cc
+++ b/chrome/test/media_router/media_router_one_ua_integration_browsertest.cc
@@ -41,18 +41,17 @@
   }
 };
 
-// TODO(crbug.com/822231): Flaky in Chromium waterfall.
-IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest, MANUAL_Basic) {
+IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest, Basic) {
   RunBasicTest();
 }
 
-// TODO(crbug.com/822216): Flaky in Chromium waterfall.
+// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
                        MANUAL_SendAndOnMessage) {
   RunSendMessageTest("foo");
 }
 
-// TODO(crbug.com/821717): Flaky in Chromium waterfall.
+// TODO(https://crbug.com/822231): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
                        MANUAL_ReceiverCloseConnection) {
   WebContents* web_contents = StartSessionWithTestPageAndChooseSink();
@@ -60,21 +59,18 @@
   ExecuteJavaScriptAPI(web_contents, kInitiateCloseFromReceiverPageScript);
 }
 
-// TODO(crbug.com/824889): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
-                       MANUAL_Fail_SendMessage) {
+                       Fail_SendMessage) {
   RunFailToSendMessageTest();
 }
 
-// TODO(crbug.com/821717): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
-                       MANUAL_ReconnectSession) {
+                       ReconnectSession) {
   RunReconnectSessionTest();
 }
 
-// TODO(crbug.com/821717): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUABrowserTest,
-                       MANUAL_ReconnectSessionSameTab) {
+                       ReconnectSessionSameTab) {
   RunReconnectSessionSameTabTest();
 }
 
@@ -87,28 +83,23 @@
   }
 };
 
-// TODO(crbug.com/822179,822337): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
-                       MANUAL_Basic) {
+                       Basic) {
   RunBasicTest();
 }
 
-// TODO(crbug.com/821717): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
-                       MANUAL_Fail_SendMessage) {
+                       Fail_SendMessage) {
   RunFailToSendMessageTest();
 }
 
-// TODO(crbug.com/822231): Flaky in Chromium waterfall.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
-                       MANUAL_ReconnectSession) {
+                       ReconnectSession) {
   RunReconnectSessionTest();
 }
 
-// TODO(crbug.com/826016): Crashes on ASAN.
-// TODO(crbug.com/834681): Crashes elsewhere too, flakily.
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationOneUANoReceiverBrowserTest,
-                       MANUAL_ReconnectSessionSameTab) {
+                       ReconnectSessionSameTab) {
   RunReconnectSessionSameTabTest();
 }
 
diff --git a/chrome/test/media_router/media_router_ui_for_test.cc b/chrome/test/media_router/media_router_ui_for_test.cc
index f8a29f8..3a82446 100644
--- a/chrome/test/media_router/media_router_ui_for_test.cc
+++ b/chrome/test/media_router/media_router_ui_for_test.cc
@@ -5,6 +5,8 @@
 #include "chrome/test/media_router/media_router_ui_for_test.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/media_routes_observer.h"
 #include "chrome/browser/ui/views/media_router/cast_dialog_sink_button.h"
 #include "chrome/browser/ui/views/media_router/cast_dialog_view.h"
 #include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h"
@@ -30,6 +32,24 @@
                         ui::EF_LEFT_MOUSE_BUTTON, 0);
 }
 
+// Routes observer that calls a callback once there are no routes.
+class NoRoutesObserver : public MediaRoutesObserver {
+ public:
+  NoRoutesObserver(MediaRouter* router, base::OnceClosure callback)
+      : MediaRoutesObserver(router), callback_(std::move(callback)) {}
+  ~NoRoutesObserver() override = default;
+
+  void OnRoutesUpdated(
+      const std::vector<MediaRoute>& routes,
+      const std::vector<MediaRoute::Id>& joinable_route_ids) override {
+    if (callback_ && routes.empty())
+      std::move(callback_).Run();
+  }
+
+ private:
+  base::OnceClosure callback_;
+};
+
 }  // namespace
 
 // static
@@ -80,15 +100,15 @@
   dialog_view->sources_menu_model_for_test()->ActivatedAt(source_index);
 }
 
-void MediaRouterUiForTest::StartCasting(const MediaSink::Id& sink_id) {
-  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+void MediaRouterUiForTest::StartCasting(const std::string& sink_name) {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
   sink_button->OnMousePressed(CreateMousePressedEvent());
   sink_button->OnMouseReleased(CreateMouseReleasedEvent());
   base::RunLoop().RunUntilIdle();
 }
 
-void MediaRouterUiForTest::StopCasting(const MediaSink::Id& sink_id) {
-  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+void MediaRouterUiForTest::StopCasting(const std::string& sink_name) {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
   sink_button->icon_view()->OnMousePressed(CreateMousePressedEvent());
   sink_button->icon_view()->OnMouseReleased(CreateMouseReleasedEvent());
   base::RunLoop().RunUntilIdle();
@@ -109,8 +129,12 @@
   NOTREACHED() << "Sink was not found";
 }
 
-void MediaRouterUiForTest::WaitForSink(const MediaSink::Id& sink_id) {
-  ObserveDialog(WatchType::kSink, sink_id);
+void MediaRouterUiForTest::WaitForSink(const std::string& sink_name) {
+  ObserveDialog(WatchType::kSink, sink_name);
+}
+
+void MediaRouterUiForTest::WaitForSinkAvailable(const std::string& sink_name) {
+  ObserveDialog(WatchType::kSinkAvailable, sink_name);
 }
 
 void MediaRouterUiForTest::WaitForAnyIssue() {
@@ -121,37 +145,53 @@
   ObserveDialog(WatchType::kAnyRoute);
 }
 
-void MediaRouterUiForTest::WaitForDialogClosed() {
-  ObserveDialog(WatchType::kDialogClosed);
+void MediaRouterUiForTest::WaitForDialogShown() {
+  CHECK(!watch_sink_name_);
+  CHECK(!watch_callback_);
+  CHECK_EQ(watch_type_, WatchType::kNone);
+  if (IsDialogShown())
+    return;
+
+  base::RunLoop run_loop;
+  watch_callback_ = run_loop.QuitClosure();
+  watch_type_ = WatchType::kDialogShown;
+  run_loop.Run();
 }
 
-std::string MediaRouterUiForTest::GetSinkName(
-    const MediaSink::Id& sink_id) const {
-  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
-  return base::UTF16ToUTF8(sink_button->sink().friendly_name);
+void MediaRouterUiForTest::WaitForDialogHidden() {
+  ObserveDialog(WatchType::kDialogHidden);
+}
+
+void MediaRouterUiForTest::WaitUntilNoRoutes() {
+  base::RunLoop run_loop;
+  NoRoutesObserver no_routes_observer(
+      MediaRouterFactory::GetApiForBrowserContext(
+          web_contents_->GetBrowserContext()),
+      run_loop.QuitClosure());
+  run_loop.Run();
 }
 
 MediaRoute::Id MediaRouterUiForTest::GetRouteIdForSink(
-    const MediaSink::Id& sink_id) const {
-  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+    const std::string& sink_name) const {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
   if (!sink_button->sink().route) {
-    NOTREACHED() << "Route not found for sink " << sink_id;
+    NOTREACHED() << "Route not found for sink " << sink_name;
     return "";
   }
   return sink_button->sink().route->media_route_id();
 }
 
 std::string MediaRouterUiForTest::GetStatusTextForSink(
-    const MediaSink::Id& sink_id) const {
-  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+    const std::string& sink_name) const {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
   return base::UTF16ToUTF8(sink_button->sink().status_text);
 }
 
 std::string MediaRouterUiForTest::GetIssueTextForSink(
-    const MediaSink::Id& sink_id) const {
-  CastDialogSinkButton* sink_button = GetSinkButton(sink_id);
+    const std::string& sink_name) const {
+  CastDialogSinkButton* sink_button = GetSinkButton(sink_name);
   if (!sink_button->sink().issue) {
-    NOTREACHED() << "Issue not found for sink " << sink_id;
+    NOTREACHED() << "Issue not found for sink " << sink_name;
     return "";
   }
   return sink_button->sink().issue->info().title;
@@ -161,11 +201,17 @@
     : web_contents_(web_contents),
       dialog_controller_(
           MediaRouterDialogControllerViews::GetOrCreateForWebContents(
-              web_contents)) {}
+              web_contents)),
+      weak_factory_(this) {
+  dialog_controller_->SetDialogCreationCallbackForTesting(base::BindRepeating(
+      &MediaRouterUiForTest::OnDialogCreated, weak_factory_.GetWeakPtr()));
+}
 
 void MediaRouterUiForTest::OnDialogModelUpdated(CastDialogView* dialog_view) {
-  if (!watch_callback_ || watch_type_ == WatchType::kDialogClosed)
+  if (!watch_callback_ || watch_type_ == WatchType::kDialogShown ||
+      watch_type_ == WatchType::kDialogHidden) {
     return;
+  }
 
   const std::vector<CastDialogSinkButton*>& sink_buttons =
       dialog_view->sink_buttons_for_test();
@@ -173,27 +219,34 @@
                    [&, this](CastDialogSinkButton* sink_button) {
                      switch (watch_type_) {
                        case WatchType::kSink:
-                         return sink_button->sink().id == *watch_sink_id_;
+                         return sink_button->sink().friendly_name ==
+                                base::UTF8ToUTF16(*watch_sink_name_);
+                       case WatchType::kSinkAvailable:
+                         return sink_button->sink().friendly_name ==
+                                    base::UTF8ToUTF16(*watch_sink_name_) &&
+                                sink_button->sink().state ==
+                                    UIMediaSinkState::AVAILABLE;
                        case WatchType::kAnyIssue:
                          return sink_button->sink().issue.has_value();
                        case WatchType::kAnyRoute:
                          return sink_button->sink().route.has_value();
                        case WatchType::kNone:
-                       case WatchType::kDialogClosed:
+                       case WatchType::kDialogShown:
+                       case WatchType::kDialogHidden:
                          NOTREACHED() << "Invalid WatchType";
                          return false;
                      }
                    }) != sink_buttons.end()) {
     std::move(*watch_callback_).Run();
     watch_callback_.reset();
-    watch_sink_id_.reset();
+    watch_sink_name_.reset();
     watch_type_ = WatchType::kNone;
     dialog_view->RemoveObserver(this);
   }
 }
 
 void MediaRouterUiForTest::OnDialogWillClose(CastDialogView* dialog_view) {
-  if (watch_type_ == WatchType::kDialogClosed) {
+  if (watch_type_ == WatchType::kDialogHidden) {
     std::move(*watch_callback_).Run();
     watch_callback_.reset();
     watch_type_ = WatchType::kNone;
@@ -203,18 +256,28 @@
     dialog_view->RemoveObserver(this);
 }
 
+void MediaRouterUiForTest::OnDialogCreated() {
+  if (watch_type_ == WatchType::kDialogShown) {
+    std::move(*watch_callback_).Run();
+    watch_callback_.reset();
+    watch_type_ = WatchType::kNone;
+  }
+  CastDialogView::GetInstance()->KeepShownForTesting();
+}
+
 CastDialogSinkButton* MediaRouterUiForTest::GetSinkButton(
-    const MediaSink::Id& sink_id) const {
+    const std::string& sink_name) const {
   CastDialogView* dialog_view = CastDialogView::GetInstance();
   CHECK(dialog_view);
   const std::vector<CastDialogSinkButton*>& sink_buttons =
       dialog_view->sink_buttons_for_test();
   auto it = std::find_if(sink_buttons.begin(), sink_buttons.end(),
-                         [sink_id](CastDialogSinkButton* sink_button) {
-                           return sink_button->sink().id == sink_id;
+                         [sink_name](CastDialogSinkButton* sink_button) {
+                           return sink_button->sink().friendly_name ==
+                                  base::UTF8ToUTF16(sink_name);
                          });
   if (it == sink_buttons.end()) {
-    NOTREACHED() << "Sink button not found for sink ID: " << sink_id;
+    NOTREACHED() << "Sink button not found for sink: " << sink_name;
     return nullptr;
   } else {
     return *it;
@@ -223,12 +286,12 @@
 
 void MediaRouterUiForTest::ObserveDialog(
     WatchType watch_type,
-    base::Optional<MediaSink::Id> sink_id) {
-  CHECK(!watch_sink_id_);
+    base::Optional<std::string> sink_name) {
+  CHECK(!watch_sink_name_);
   CHECK(!watch_callback_);
   CHECK_EQ(watch_type_, WatchType::kNone);
   base::RunLoop run_loop;
-  watch_sink_id_ = std::move(sink_id);
+  watch_sink_name_ = std::move(sink_name);
   watch_callback_ = run_loop.QuitClosure();
   watch_type_ = watch_type;
 
diff --git a/chrome/test/media_router/media_router_ui_for_test.h b/chrome/test/media_router/media_router_ui_for_test.h
index 908afdc..3b51970 100644
--- a/chrome/test/media_router/media_router_ui_for_test.h
+++ b/chrome/test/media_router/media_router_ui_for_test.h
@@ -7,8 +7,10 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "chrome/browser/ui/views/media_router/cast_dialog_view.h"
+#include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h"
 #include "chrome/common/media_router/media_sink.h"
 #include "chrome/common/media_router/media_source.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -19,8 +21,6 @@
 
 namespace media_router {
 
-class MediaRouterDialogControllerViews;
-
 class MediaRouterUiForTest
     : public content::WebContentsUserData<MediaRouterUiForTest>,
       public CastDialogView::Observer {
@@ -39,31 +39,41 @@
 
   // These methods require that the dialog is shown and the specified sink is
   // shown in the dialog.
-  void StartCasting(const MediaSink::Id& sink_id);
-  void StopCasting(const MediaSink::Id& sink_id);
+  void StartCasting(const std::string& sink_name);
+  void StopCasting(const std::string& sink_name);
   // Stops casting to the first active sink found on the sink list. Requires
   // that such a sink exists.
   void StopCasting();
 
-  // Waits until . Requires that the dialog is shown.
-  void WaitForSink(const MediaSink::Id& sink_id);
+  // Waits until a condition is met. Requires that the dialog is shown.
+  void WaitForSink(const std::string& sink_name);
+  void WaitForSinkAvailable(const std::string& sink_name);
   void WaitForAnyIssue();
   void WaitForAnyRoute();
-  void WaitForDialogClosed();
+  void WaitForDialogShown();
+  void WaitForDialogHidden();
+  void WaitUntilNoRoutes();
 
   // These methods require that the dialog is shown, and the sink specified by
-  // |sink_id| is in the dialog.
-  std::string GetSinkName(const MediaSink::Id& sink_id) const;
-  MediaRoute::Id GetRouteIdForSink(const MediaSink::Id& sink_id) const;
-  std::string GetStatusTextForSink(const MediaSink::Id& sink_id) const;
-  std::string GetIssueTextForSink(const MediaSink::Id& sink_id) const;
+  // |sink_name| is in the dialog.
+  MediaRoute::Id GetRouteIdForSink(const std::string& sink_name) const;
+  std::string GetStatusTextForSink(const std::string& sink_name) const;
+  std::string GetIssueTextForSink(const std::string& sink_name) const;
 
   content::WebContents* web_contents() const { return web_contents_; }
 
  private:
   friend class content::WebContentsUserData<MediaRouterUiForTest>;
 
-  enum class WatchType { kNone, kSink, kAnyIssue, kAnyRoute, kDialogClosed };
+  enum class WatchType {
+    kNone,
+    kSink,           // Sink is found in any state.
+    kSinkAvailable,  // Sink is found in the "Available" state.
+    kAnyIssue,
+    kAnyRoute,
+    kDialogShown,
+    kDialogHidden
+  };
 
   explicit MediaRouterUiForTest(content::WebContents* web_contents);
 
@@ -71,21 +81,26 @@
   void OnDialogModelUpdated(CastDialogView* dialog_view) override;
   void OnDialogWillClose(CastDialogView* dialog_view) override;
 
-  CastDialogSinkButton* GetSinkButton(const MediaSink::Id& sink_id) const;
+  // Called by MediaRouterDialogControllerViews.
+  void OnDialogCreated();
+
+  CastDialogSinkButton* GetSinkButton(const std::string& sink_name) const;
 
   // Registers itself as an observer to the dialog, and waits until an event
-  // of |watch_type| is observed. |sink_id| should be set only if observing for
-  // a sink.
+  // of |watch_type| is observed. |sink_name| should be set only if observing
+  // for a sink.
   void ObserveDialog(WatchType watch_type,
-                     base::Optional<MediaSink::Id> sink_id = base::nullopt);
+                     base::Optional<std::string> sink_name = base::nullopt);
 
   content::WebContents* web_contents_;
   MediaRouterDialogControllerViews* dialog_controller_;
 
-  base::Optional<MediaSink::Id> watch_sink_id_;
+  base::Optional<std::string> watch_sink_name_;
   base::Optional<base::OnceClosure> watch_callback_;
   WatchType watch_type_ = WatchType::kNone;
 
+  base::WeakPtrFactory<MediaRouterUiForTest> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaRouterUiForTest);
 };
 
diff --git a/chrome/test/media_router/resources/common.js b/chrome/test/media_router/resources/common.js
index 48ce0d9..141a109a 100644
--- a/chrome/test/media_router/resources/common.js
+++ b/chrome/test/media_router/resources/common.js
@@ -120,7 +120,7 @@
       sendResult(false, 'start() unexpectedly succeeded.');
     }).catch(function(e) {
       if (expectedErrorName != e.name) {
-        sendResult(false, 'Got unexpected error: ' + e.name);
+        sendResult(false, 'Got unexpected error. ' + e.name + ': ' + e.message);
       } else if (e.message.indexOf(expectedErrorMessageSubstring) == -1) {
         sendResult(false,
             'Error message is not correct, it should contain "' +
@@ -219,6 +219,12 @@
     sendResult(false, 'startedConnection does not exist.');
     return;
   }
+  if (startedConnection.state != 'connected') {
+    sendResult(false,
+        `Expected the connection state to be connected but it was \
+         ${startedConnection.state}`);
+    return;
+  }
   startedConnection.onmessage = function(receivedMessage) {
     var expectedResponse = 'Pong: ' + message;
     var actualResponse = receivedMessage.data;
@@ -241,6 +247,12 @@
     sendResult(false, 'startedConnection does not exist.');
     return;
   }
+  if (startedConnection.state != 'connected') {
+    sendResult(false,
+        `Expected the connection state to be connected but it was \
+         ${startedConnection.state}`);
+    return;
+  }
   startedConnection.onclose = (event) => {
     const reason = event.reason;
     if (reason != 'closed') {
diff --git a/chromecast/base/chromecast_config_android.h b/chromecast/base/chromecast_config_android.h
index 3b38e88..58fdeae 100644
--- a/chromecast/base/chromecast_config_android.h
+++ b/chromecast/base/chromecast_config_android.h
@@ -18,8 +18,15 @@
 
   // Returns whether or not the user has allowed sending usage stats and
   // crash reports.
+  // TODO(ziyangch): Remove CanSendUsageStats() and switch to pure callback
+  // style.
   virtual bool CanSendUsageStats() = 0;
 
+  // Set the the user's sending usage stats.
+  // TODO(ziyangch): Remove SetSendUsageStats() after switching to Crashpad on
+  // Android.(The CL which does this is at https://crrev.com/c/989401.)
+  virtual void SetSendUsageStats(bool enabled) = 0;
+
   // Registers a handler to be notified when SendUsageStats is changed.
   virtual void SetSendUsageStatsChangedCallback(
       base::RepeatingCallback<void(bool)> callback) = 0;
diff --git a/chromecast/base/chromecast_config_android_dummy.cc b/chromecast/base/chromecast_config_android_dummy.cc
index cc7e820..86982604 100644
--- a/chromecast/base/chromecast_config_android_dummy.cc
+++ b/chromecast/base/chromecast_config_android_dummy.cc
@@ -16,6 +16,8 @@
 
   bool CanSendUsageStats() override { return false; }
 
+  void SetSendUsageStats(bool enabled) override {}
+
   void SetSendUsageStatsChangedCallback(
       base::RepeatingCallback<void(bool)> callback) override {}
 
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 0d27b5b..ff33f40 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -279,6 +279,8 @@
       "//base:base_java_unittest_support",
       "//components/autofill_assistant/browser:unit_tests",
       "//components/cdm/browser:unit_tests",
+      "//components/crash/android:java",
+      "//components/crash/android:unit_tests",
       "//components/gcm_driver/instance_id:test_support",
       "//components/gcm_driver/instance_id/android:instance_id_driver_java",
       "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 966a0b5..80959ab 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -101,6 +101,8 @@
     "credit_card_field.h",
     "credit_card_save_manager.cc",
     "credit_card_save_manager.h",
+    "credit_card_save_strike_database.cc",
+    "credit_card_save_strike_database.h",
     "email_field.cc",
     "email_field.h",
     "field_candidates.cc",
@@ -351,6 +353,8 @@
     "test_autofill_provider.h",
     "test_credit_card_save_manager.cc",
     "test_credit_card_save_manager.h",
+    "test_credit_card_save_strike_database.cc",
+    "test_credit_card_save_strike_database.h",
     "test_event_waiter.h",
     "test_form_data_importer.cc",
     "test_form_data_importer.h",
@@ -484,6 +488,7 @@
     "country_names_unittest.cc",
     "credit_card_field_unittest.cc",
     "credit_card_save_manager_unittest.cc",
+    "credit_card_save_strike_database_unittest.cc",
     "credit_card_unittest.cc",
     "field_candidates_unittest.cc",
     "field_filler_unittest.cc",
diff --git a/components/autofill/core/browser/credit_card_save_strike_database.cc b/components/autofill/core/browser/credit_card_save_strike_database.cc
new file mode 100644
index 0000000..aca26ba3
--- /dev/null
+++ b/components/autofill/core/browser/credit_card_save_strike_database.cc
@@ -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.
+
+#include "components/autofill/core/browser/credit_card_save_strike_database.h"
+
+#include "components/autofill/core/browser/proto/strike_data.pb.h"
+
+namespace autofill {
+
+CreditCardSaveStrikeDatabase::CreditCardSaveStrikeDatabase(
+    const base::FilePath& database_dir)
+    : StrikeDatabase(database_dir) {}
+
+CreditCardSaveStrikeDatabase::~CreditCardSaveStrikeDatabase() {}
+
+std::string CreditCardSaveStrikeDatabase::GetProjectPrefix() {
+  return "CreditCardSave";
+}
+
+int CreditCardSaveStrikeDatabase::GetMaxStrikesLimit() {
+  return 3;
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/credit_card_save_strike_database.h b/components/autofill/core/browser/credit_card_save_strike_database.h
new file mode 100644
index 0000000..fab05c6
--- /dev/null
+++ b/components/autofill/core/browser/credit_card_save_strike_database.h
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_CREDIT_CARD_SAVE_STRIKE_DATABASE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_CREDIT_CARD_SAVE_STRIKE_DATABASE_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/strike_database.h"
+
+namespace autofill {
+
+// Implementation of StrikeDatabase for credit card saves (both local and
+// upload).
+class CreditCardSaveStrikeDatabase : public StrikeDatabase {
+ public:
+  CreditCardSaveStrikeDatabase(const base::FilePath& database_dir);
+  ~CreditCardSaveStrikeDatabase() override;
+
+  std::string GetProjectPrefix() override;
+  int GetMaxStrikesLimit() override;
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_CREDIT_CARD_SAVE_STRIKE_DATABASE_H_
diff --git a/components/autofill/core/browser/credit_card_save_strike_database_unittest.cc b/components/autofill/core/browser/credit_card_save_strike_database_unittest.cc
new file mode 100644
index 0000000..cad6258
--- /dev/null
+++ b/components/autofill/core/browser/credit_card_save_strike_database_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/credit_card_save_strike_database.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/autofill/core/browser/proto/strike_data.pb.h"
+#include "components/autofill/core/browser/test_credit_card_save_strike_database.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class CreditCardSaveStrikeDatabaseTest : public ::testing::Test {
+ public:
+  CreditCardSaveStrikeDatabaseTest() : strike_database_(InitFilePath()) {}
+
+ protected:
+  base::HistogramTester* GetHistogramTester() { return &histogram_tester_; }
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  TestCreditCardSaveStrikeDatabase strike_database_;
+
+ private:
+  static const base::FilePath InitFilePath() {
+    base::ScopedTempDir temp_dir_;
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+    const base::FilePath file_path =
+        temp_dir_.GetPath().AppendASCII("StrikeDatabaseTest");
+    return file_path;
+  }
+
+  base::HistogramTester histogram_tester_;
+};
+
+TEST_F(CreditCardSaveStrikeDatabaseTest, GetKeyForCreditCardSaveTest) {
+  const std::string last_four = "1234";
+  EXPECT_EQ("CreditCardSave__1234", strike_database_.GetKey(last_four));
+}
+
+TEST_F(CreditCardSaveStrikeDatabaseTest, MaxStrikesLimitReachedTest) {
+  const std::string last_four = "1234";
+  EXPECT_EQ(false, strike_database_.IsMaxStrikesLimitReached(last_four));
+  // 1st strike added for |last_four|.
+  strike_database_.AddStrike(last_four);
+  EXPECT_EQ(false, strike_database_.IsMaxStrikesLimitReached(last_four));
+  // 2nd strike added for |last_four|.
+  strike_database_.AddStrike(last_four);
+  EXPECT_EQ(false, strike_database_.IsMaxStrikesLimitReached(last_four));
+  // 3rd strike added for |last_four|.
+  strike_database_.AddStrike(last_four);
+  EXPECT_EQ(true, strike_database_.IsMaxStrikesLimitReached(last_four));
+}
+
+TEST_F(CreditCardSaveStrikeDatabaseTest,
+       CreditCardSaveNthStrikeAddedHistogram) {
+  const std::string last_four1 = "1234";
+  const std::string last_four2 = "9876";
+  // 1st strike added for |last_four1|.
+  strike_database_.AddStrike(last_four1);
+  // 2nd strike added for |last_four1|.
+  strike_database_.AddStrike(last_four1);
+  // 1st strike added for |last_four2|.
+  strike_database_.AddStrike(last_four2);
+  std::vector<base::Bucket> buckets = GetHistogramTester()->GetAllSamples(
+      "Autofill.StrikeDatabase.NthStrikeAdded.CreditCardSave");
+  // There should be two buckets, one for 1st strike, one for 2nd strike count.
+  ASSERT_EQ(2U, buckets.size());
+  // Both |last_four1| and |last_four2| have 1st strikes recorded.
+  EXPECT_EQ(2, buckets[0].count);
+  // Only |last_four1| has 2nd strike recorded.
+  EXPECT_EQ(1, buckets[1].count);
+}
+
+TEST_F(CreditCardSaveStrikeDatabaseTest,
+       AddStrikeForZeroAndNonZeroStrikesTest) {
+  const std::string last_four = "1234";
+  EXPECT_EQ(0, strike_database_.GetStrikes(last_four));
+  strike_database_.AddStrike(last_four);
+  EXPECT_EQ(1, strike_database_.GetStrikes(last_four));
+  strike_database_.AddStrike(last_four);
+  EXPECT_EQ(2, strike_database_.GetStrikes(last_four));
+}
+
+TEST_F(CreditCardSaveStrikeDatabaseTest, ClearStrikesForNonZeroStrikesTest) {
+  const std::string last_four = "1234";
+  strike_database_.AddStrike(last_four);
+  EXPECT_EQ(1, strike_database_.GetStrikes(last_four));
+  strike_database_.ClearStrikes(last_four);
+  EXPECT_EQ(0, strike_database_.GetStrikes(last_four));
+}
+
+TEST_F(CreditCardSaveStrikeDatabaseTest, ClearStrikesForZeroStrikesTest) {
+  const std::string last_four = "1234";
+  strike_database_.ClearStrikes(last_four);
+  EXPECT_EQ(0, strike_database_.GetStrikes(last_four));
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/strike_database.cc b/components/autofill/core/browser/strike_database.cc
index 451af485..6e0e8b8 100644
--- a/components/autofill/core/browser/strike_database.cc
+++ b/components/autofill/core/browser/strike_database.cc
@@ -39,6 +39,10 @@
 
 StrikeDatabase::~StrikeDatabase() {}
 
+bool StrikeDatabase::IsMaxStrikesLimitReached(const std::string id) {
+  return GetStrikes(id) >= GetMaxStrikesLimit();
+}
+
 int StrikeDatabase::AddStrike(const std::string id) {
   std::string key = GetKey(id);
   int num_strikes = strike_map_cache_.count(key)  // Cache has entry for |key|.
@@ -50,6 +54,9 @@
       base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
   UpdateCache(key, data);
   SetProtoStrikeData(key, data, base::DoNothing());
+  base::UmaHistogramCounts1000(
+      "Autofill.StrikeDatabase.NthStrikeAdded." + GetProjectPrefix(),
+      num_strikes);
   return num_strikes;
 }
 
diff --git a/components/autofill/core/browser/strike_database.h b/components/autofill/core/browser/strike_database.h
index 6b79c564..5c69ace7 100644
--- a/components/autofill/core/browser/strike_database.h
+++ b/components/autofill/core/browser/strike_database.h
@@ -43,7 +43,7 @@
   explicit StrikeDatabase(const base::FilePath& database_dir);
   ~StrikeDatabase() override;
 
-  bool IsMaxStrikesLimitReached();
+  bool IsMaxStrikesLimitReached(const std::string id);
 
   // Increments in-memory cache and updates underlying ProtoDatabase.
   int AddStrike(const std::string id);
@@ -78,6 +78,8 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(ChromeBrowsingDataRemoverDelegateTest,
                            StrikeDatabaseEmptyOnAutofillRemoveEverything);
+  FRIEND_TEST_ALL_PREFIXES(CreditCardSaveStrikeDatabaseTest,
+                           GetKeyForCreditCardSaveTest);
   friend class StrikeDatabaseTest;
   friend class StrikeDatabaseTester;
 
diff --git a/components/autofill/core/browser/test_credit_card_save_strike_database.cc b/components/autofill/core/browser/test_credit_card_save_strike_database.cc
new file mode 100644
index 0000000..da24bb6
--- /dev/null
+++ b/components/autofill/core/browser/test_credit_card_save_strike_database.cc
@@ -0,0 +1,15 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/test_credit_card_save_strike_database.h"
+
+namespace autofill {
+
+TestCreditCardSaveStrikeDatabase::TestCreditCardSaveStrikeDatabase(
+    const base::FilePath& database_dir)
+    : CreditCardSaveStrikeDatabase(database_dir) {
+  database_initialized_ = true;
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/test_credit_card_save_strike_database.h b/components/autofill/core/browser/test_credit_card_save_strike_database.h
new file mode 100644
index 0000000..21c33a5
--- /dev/null
+++ b/components/autofill/core/browser/test_credit_card_save_strike_database.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_CREDIT_CARD_SAVE_STRIKE_DATABASE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_CREDIT_CARD_SAVE_STRIKE_DATABASE_H_
+
+#include "components/autofill/core/browser/credit_card_save_strike_database.h"
+
+namespace autofill {
+
+class TestCreditCardSaveStrikeDatabase : public CreditCardSaveStrikeDatabase {
+ public:
+  TestCreditCardSaveStrikeDatabase(const base::FilePath& database_dir);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_CREDIT_CARD_SAVE_STRIKE_DATABASE_H_
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 164352f..3bc8872 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -142,40 +142,7 @@
   }
 }
 
-void Controller::Start(const GURL& initialUrl) {
-  DCHECK(initialUrl.is_valid());
-  GetOrCheckScripts(initialUrl);
-  if (allow_autostart_) {
-    auto iter = parameters_->find(kCallerScriptParameterName);
-    // TODO(crbug.com/806868): Put back an explicit AUTOSTART parameter so we
-    // don't need to know who calls us.
-    if (iter != parameters_->end() && iter->second == "1") {
-      should_fail_after_checking_scripts_ = true;
-      GetUiController()->ShowOverlay();
-      GetUiController()->ShowStatusMessage(l10n_util::GetStringFUTF8(
-          IDS_AUTOFILL_ASSISTANT_LOADING,
-          base::UTF8ToUTF16(web_contents()->GetVisibleURL().host())));
-    }
-  }
-
-  touchable_element_area_.SetOnUpdate(base::BindRepeating(
-      &UiController::UpdateTouchableArea,
-      // Unretained is safe, since touchable_element_area_ is guaranteed to be
-      // deleted before the UI controller.
-      base::Unretained(GetUiController())));
-}
-
 void Controller::GetOrCheckScripts(const GURL& url) {
-  if (IsCookieExperimentEnabled() && !started_) {
-    GetWebController()->HasCookie(
-        base::BindOnce(&Controller::OnGetCookie,
-                       // WebController is owned by Controller.
-                       base::Unretained(this), url));
-    return;
-  } else {
-    started_ = true;
-  }
-
   if (!started_ || script_tracker_->running()) {
     return;
   }
@@ -364,11 +331,6 @@
   return false;
 }
 
-void Controller::OnClickOverlay() {
-  GetUiController()->HideOverlay();
-  // TODO(crbug.com/806868): Stop executing scripts.
-}
-
 void Controller::OnGetCookie(const GURL& initial_url, bool has_cookie) {
   if (has_cookie) {
     // This code is only active with the experiment parameter.
@@ -386,12 +348,56 @@
 
 void Controller::OnSetCookie(const GURL& initial_url, bool result) {
   DCHECK(result) << "Setting cookie failed";
-  // Failing to set the cookie should not be fatal since it would prevent
-  // checking for available scripts.
-  started_ = true;
+  FinishStart(initial_url);
+}
 
-  // Kick off another check scripts run since we may have blocked the first one.
+void Controller::FinishStart(const GURL& initial_url) {
+  started_ = true;
   GetOrCheckScripts(initial_url);
+  if (allow_autostart_) {
+    auto iter = parameters_->find(kCallerScriptParameterName);
+    // TODO(crbug.com/806868): Put back an explicit AUTOSTART parameter so we
+    // don't need to know who calls us.
+    if (iter != parameters_->end() && iter->second == "1") {
+      should_fail_after_checking_scripts_ = true;
+      GetUiController()->ShowOverlay();
+      GetUiController()->ShowStatusMessage(l10n_util::GetStringFUTF8(
+          IDS_AUTOFILL_ASSISTANT_LOADING,
+          base::UTF8ToUTF16(web_contents()->GetVisibleURL().host())));
+    }
+  }
+
+  touchable_element_area_.SetOnUpdate(base::BindRepeating(
+      &UiController::UpdateTouchableArea,
+      // Unretained is safe, since touchable_element_area_ is guaranteed to be
+      // deleted before the UI controller.
+      base::Unretained(GetUiController())));
+}
+
+void Controller::Start(const GURL& initialUrl) {
+  DCHECK(initialUrl.is_valid());
+  if (IsCookieExperimentEnabled()) {
+    GetWebController()->HasCookie(
+        base::BindOnce(&Controller::OnGetCookie,
+                       // WebController is owned by Controller.
+                       base::Unretained(this), initialUrl));
+  } else {
+    FinishStart(initialUrl);
+  }
+}
+
+void Controller::OnClickOverlay() {
+  GetUiController()->HideOverlay();
+  // TODO(crbug.com/806868): Stop executing scripts.
+}
+
+void Controller::OnDestroy() {
+  delete this;
+}
+
+bool Controller::Terminate() {
+  StopPeriodicScriptChecks();
+  return script_tracker_->Terminate();
 }
 
 void Controller::OnScriptSelected(const std::string& script_path) {
@@ -403,6 +409,15 @@
   ExecuteScript(script_path);
 }
 
+void Controller::ScrollBy(float distanceXRatio, float distanceYRatio) {
+  GetWebController()->ScrollBy(distanceXRatio, distanceYRatio);
+  touchable_element_area_.UpdatePositions();
+}
+
+void Controller::UpdateTouchableArea() {
+  touchable_element_area_.UpdatePositions();
+}
+
 std::string Controller::GetDebugContext() {
   base::Value dict(base::Value::Type::DICTIONARY);
 
@@ -420,43 +435,6 @@
   return output_js;
 }
 
-void Controller::OnDestroy() {
-  delete this;
-}
-
-bool Controller::Terminate() {
-  StopPeriodicScriptChecks();
-  return script_tracker_->Terminate();
-}
-
-void Controller::ScrollBy(float distanceXRatio, float distanceYRatio) {
-  GetWebController()->ScrollBy(distanceXRatio, distanceYRatio);
-  touchable_element_area_.UpdatePositions();
-}
-
-void Controller::UpdateTouchableArea() {
-  touchable_element_area_.UpdatePositions();
-}
-
-void Controller::DidAttachInterstitialPage() {
-  GetUiController()->Shutdown();
-}
-
-void Controller::DidGetUserInteraction(const blink::WebInputEvent::Type type) {
-  switch (type) {
-    case blink::WebInputEvent::kTouchStart:
-    case blink::WebInputEvent::kGestureTapDown:
-      if (!script_tracker_->running()) {
-        script_tracker_->CheckScripts(kPeriodicScriptCheckInterval);
-        StartPeriodicScriptChecks();
-      }
-      break;
-
-    default:
-      break;
-  }
-}
-
 void Controller::OnNoRunnableScriptsAnymore() {
   if (script_tracker_->running())
     return;
@@ -502,8 +480,23 @@
   GetUiController()->UpdateScripts(scripts_to_update);
 }
 
-void Controller::DocumentAvailableInMainFrame() {
-  GetOrCheckScripts(web_contents()->GetLastCommittedURL());
+void Controller::DidAttachInterstitialPage() {
+  GetUiController()->Shutdown();
+}
+
+void Controller::DidGetUserInteraction(const blink::WebInputEvent::Type type) {
+  switch (type) {
+    case blink::WebInputEvent::kTouchStart:
+    case blink::WebInputEvent::kGestureTapDown:
+      if (!script_tracker_->running()) {
+        script_tracker_->CheckScripts(kPeriodicScriptCheckInterval);
+        StartPeriodicScriptChecks();
+      }
+      break;
+
+    default:
+      break;
+  }
 }
 
 void Controller::DidFinishLoad(content::RenderFrameHost* render_frame_host,
@@ -550,6 +543,10 @@
   }
 }
 
+void Controller::DocumentAvailableInMainFrame() {
+  GetOrCheckScripts(web_contents()->GetLastCommittedURL());
+}
+
 void Controller::RenderProcessGone(base::TerminationStatus status) {
   GetUiController()->Shutdown();
 }
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 3a7d99f6..57379f9 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -97,8 +97,9 @@
   // a script terminated with a Stop action.
   void OnGetCookie(const GURL& initial_url, bool has_cookie);
   void OnSetCookie(const GURL& initial_url, bool result);
+  void FinishStart(const GURL& initial_url);
 
-  // Overrides content::UiDelegate:
+  // Overrides autofill_assistant::UiDelegate:
   void Start(const GURL& initialUrl) override;
   void OnClickOverlay() override;
   void OnDestroy() override;
@@ -158,6 +159,7 @@
   // elements.
   ElementArea touchable_element_area_;
 
+  // Flag indicates whether it is ready to fetch and execute scripts.
   bool started_ = false;
 
   base::WeakPtrFactory<Controller> weak_ptr_factory_;
diff --git a/components/autofill_assistant_strings.grdp b/components/autofill_assistant_strings.grdp
index 37275e3..77fcbc8c 100644
--- a/components/autofill_assistant_strings.grdp
+++ b/components/autofill_assistant_strings.grdp
@@ -28,5 +28,11 @@
     <message name="IDS_AUTOFILL_ASSISTANT_DETAILS_DIFFER_GO_BACK" desc="Shown on a button allowing going back when details differ." formatter_data="android_java">
       Go back
     </message>
+    <message name="IDS_AUTOFILL_ASSISTANT_FIRST_RUN_ACCESSIBILITY" desc="Accessibility description of Autofill Assistant first run screen is shown." formatter_data="android_java">
+      Autofill assistant first run screen is shown
+    </message>
+    <message name="IDS_AUTOFILL_ASSISTANT_AVAILABLE_ACCESSIBILITY" desc="Accessibility description of Autofill Assistant is available." formatter_data="android_java">
+      Autofill assistant is available near bottom of the screen
+    </message>
   </if>
 </grit-part>
diff --git a/components/crash/android/BUILD.gn b/components/crash/android/BUILD.gn
index 97b8d2b4..05cc295 100644
--- a/components/crash/android/BUILD.gn
+++ b/components/crash/android/BUILD.gn
@@ -4,22 +4,43 @@
 
 import("//build/config/android/rules.gni")
 
-_jni_sources =
-    [ "java/src/org/chromium/components/crash/browser/ChildProcessCrashObserver.java",
-      "java/src/org/chromium/components/crash/browser/CrashDumpManager.java" ]
+_jni_sources = [
+  "java/src/org/chromium/components/crash/browser/ChildProcessCrashObserver.java",
+  "java/src/org/chromium/components/crash/browser/CrashDumpManager.java",
+  "java/src/org/chromium/components/crash/CrashKeys.java",
+]
 
 generate_jni("jni_headers") {
   sources = _jni_sources
   jni_package = "crash"
 }
 
+java_cpp_enum("java_enums_srcjar") {
+  sources = [
+    "crash_keys_android.h",
+  ]
+}
+
 android_library("java") {
   deps = [
     "//base:base_java",
   ]
+  srcjar_deps = [ ":java_enums_srcjar" ]
   java_files = _jni_sources
 }
 
+source_set("crash_android") {
+  sources = [
+    "crash_keys_android.cc",
+    "crash_keys_android.h",
+  ]
+  deps = [
+    ":jni_headers",
+    "//base",
+    "//components/crash/core/common:crash_key",
+  ]
+}
+
 android_library("javatests") {
   testonly = true
   deps = [
@@ -31,3 +52,15 @@
   ]
   java_files = [ "javatests/src/org/chromium/components/crash/browser/CrashDumpManagerTest.java" ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "crash_keys_android_unittest.cc",
+  ]
+  deps = [
+    ":crash_android",
+    "//components/crash/core/common:crash_key",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/android/crash/crash_keys_android.cc b/components/crash/android/crash_keys_android.cc
similarity index 96%
rename from chrome/browser/android/crash/crash_keys_android.cc
rename to components/crash/android/crash_keys_android.cc
index dfd065c..6eaadc1 100644
--- a/chrome/browser/android/crash/crash_keys_android.cc
+++ b/components/crash/android/crash_keys_android.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/crash/crash_keys_android.h"
+#include "components/crash/android/crash_keys_android.h"
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
diff --git a/chrome/browser/android/crash/crash_keys_android.h b/components/crash/android/crash_keys_android.h
similarity index 72%
rename from chrome/browser/android/crash/crash_keys_android.h
rename to components/crash/android/crash_keys_android.h
index 25636e0..a545dcd 100644
--- a/chrome/browser/android/crash/crash_keys_android.h
+++ b/components/crash/android/crash_keys_android.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_BROWSER_ANDROID_CRASH_CRASH_KEYS_ANDROID_H_
-#define CHROME_BROWSER_ANDROID_CRASH_CRASH_KEYS_ANDROID_H_
+#ifndef COMPONENTS_CRASH_ANDROID_CRASH_KEYS_ANDROID_H_
+#define COMPONENTS_CRASH_ANDROID_CRASH_KEYS_ANDROID_H_
 
 #include <string>
 
 // See CrashKeys.java for how to add a new crash key.
 // A Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.crash
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.crash
 enum class CrashKeyIndex {
   LOADED_DYNAMIC_MODULE = 0,
   ACTIVE_DYNAMIC_MODULE,
@@ -23,4 +23,4 @@
 void ClearAndroidCrashKey(CrashKeyIndex index);
 void FlushAndroidCrashKeys();
 
-#endif  // CHROME_BROWSER_ANDROID_CRASH_CRASH_KEYS_ANDROID_H_
+#endif  // COMPONENTS_CRASH_ANDROID_CRASH_KEYS_ANDROID_H_
diff --git a/chrome/browser/android/crash/crash_keys_android_unittest.cc b/components/crash/android/crash_keys_android_unittest.cc
similarity index 95%
rename from chrome/browser/android/crash/crash_keys_android_unittest.cc
rename to components/crash/android/crash_keys_android_unittest.cc
index e407826..45bae4b 100644
--- a/chrome/browser/android/crash/crash_keys_android_unittest.cc
+++ b/components/crash/android/crash_keys_android_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/android/crash/crash_keys_android.h"
+#include "components/crash/android/crash_keys_android.h"
 
 #include "components/crash/core/common/crash_key.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/CrashKeys.java b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
similarity index 91%
rename from chrome/android/java/src/org/chromium/chrome/browser/crash/CrashKeys.java
rename to components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
index 79eae23..17279b30 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/CrashKeys.java
+++ b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
@@ -2,7 +2,7 @@
 // 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.crash;
+package org.chromium.components.crash;
 
 import android.support.annotation.Nullable;
 
@@ -19,6 +19,7 @@
  *     <li>The CrashKeyString array in {@code crash_keys_android.cc}</li>
  *     <li>The {@link #KEYS} array in this class.</li>
  * </ol>
+ * The crash keys will only be included in browser process crash reports.
  */
 public class CrashKeys {
     private static final String[] KEYS =
@@ -47,7 +48,7 @@
      * @param keyIndex The index of a crash key.
      * @return The key for the given index.
      */
-    static String getKey(@CrashKeyIndex int keyIndex) {
+    public static String getKey(@CrashKeyIndex int keyIndex) {
         return KEYS[keyIndex];
     }
 
@@ -56,7 +57,7 @@
      *         the values have been flushed to the native side.
      * @see #flushToNative
      */
-    AtomicReferenceArray<String> getValues() {
+    public AtomicReferenceArray<String> getValues() {
         assert !mFlushed;
         return mValues;
     }
@@ -75,7 +76,6 @@
             nativeSet(keyIndex, value);
             return;
         }
-
         mValues.set(keyIndex, value);
     }
 
@@ -84,7 +84,7 @@
      * pure-Java exception handling is disabled in favor of native crash reporting.
      */
     @CalledByNative
-    void flushToNative() {
+    public void flushToNative() {
         ThreadUtils.assertOnUiThread();
 
         assert !mFlushed;
diff --git a/components/crash/content/app/BUILD.gn b/components/crash/content/app/BUILD.gn
index 542a231..732ae726 100644
--- a/components/crash/content/app/BUILD.gn
+++ b/components/crash/content/app/BUILD.gn
@@ -126,6 +126,7 @@
     deps = [
       "//base",
       "//components/browser_watcher:crash_stability",
+      "//components/gwp_asan/crash_handler",
       "//third_party/crashpad/crashpad/client",
       "//third_party/crashpad/crashpad/handler",
       "//third_party/crashpad/crashpad/minidump",
diff --git a/components/crash/content/app/DEPS b/components/crash/content/app/DEPS
index 9f77331..019cf70 100644
--- a/components/crash/content/app/DEPS
+++ b/components/crash/content/app/DEPS
@@ -2,6 +2,7 @@
   "+sandbox",
 
   "+components/browser_watcher/stability_report_user_stream_data_source.h",
+  "+components/gwp_asan/crash_handler/crash_handler.h",
   "+content/public/common/content_descriptors.h",
   "+content/public/common/result_codes.h",
   "+third_party/crashpad",
diff --git a/components/crash/content/app/run_as_crashpad_handler_win.cc b/components/crash/content/app/run_as_crashpad_handler_win.cc
index 7013c90..2948156 100644
--- a/components/crash/content/app/run_as_crashpad_handler_win.cc
+++ b/components/crash/content/app/run_as_crashpad_handler_win.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/browser_watcher/stability_report_user_stream_data_source.h"
+#include "components/gwp_asan/crash_handler/crash_handler.h"
 #include "third_party/crashpad/crashpad/client/crashpad_info.h"
 #include "third_party/crashpad/crashpad/client/simple_string_dictionary.h"
 #include "third_party/crashpad/crashpad/handler/handler_main.h"
@@ -83,6 +84,9 @@
             user_data_dir));
   }
 
+  user_stream_data_sources.push_back(
+      std::make_unique<gwp_asan::UserStreamDataSource>());
+
   return crashpad::HandlerMain(static_cast<int>(storage.size()),
                                argv_as_utf8.get(), &user_stream_data_sources);
 }
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index ebb72780..d4c9c8a 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/public/web/web_element.h"
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_navigation_control.h"
 #include "third_party/blink/public/web/web_plugin_container.h"
 #include "third_party/blink/public/web/web_view.h"
 
@@ -66,7 +67,7 @@
                                      const GURL& url) {
   DCHECK(url.is_valid()) << "Blink requires the WebView to have a valid URL.";
   WebViewPlugin* plugin = new WebViewPlugin(render_view, delegate, preferences);
-  plugin->main_frame()->LoadHTMLString(html_data, url);
+  plugin->web_view_helper_.main_frame()->LoadHTMLString(html_data, url);
   return plugin;
 }
 
@@ -269,13 +270,6 @@
   web_view_->MainFrameWidget()->Close();
 }
 
-blink::WebLocalFrame* WebViewPlugin::WebViewHelper::main_frame() {
-  // WebViewHelper doesn't support OOPIFs so the main frame will
-  // always be local.
-  DCHECK(web_view_->MainFrame()->IsWebLocalFrame());
-  return static_cast<WebLocalFrame*>(web_view_->MainFrame());
-}
-
 bool WebViewPlugin::WebViewHelper::AcceptsLoadDrops() {
   return false;
 }
@@ -311,7 +305,7 @@
                                                  const SkBitmap&,
                                                  const gfx::Point&) {
   // Immediately stop dragging.
-  main_frame()->FrameWidget()->DragSourceSystemDragEnded();
+  frame_->FrameWidget()->DragSourceSystemDragEnded();
 }
 
 bool WebViewPlugin::WebViewHelper::AllowsBrokenNullLayerTreeView() const {
@@ -353,13 +347,18 @@
       ->CreateURLLoaderFactory();
 }
 
+void WebViewPlugin::WebViewHelper::BindToFrame(
+    blink::WebNavigationControl* frame) {
+  frame_ = frame;
+}
+
 void WebViewPlugin::WebViewHelper::DidClearWindowObject() {
   if (!plugin_->delegate_)
     return;
 
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
-  v8::Local<v8::Context> context = main_frame()->MainWorldScriptContext();
+  v8::Local<v8::Context> context = frame_->MainWorldScriptContext();
   DCHECK(!context.IsEmpty());
 
   v8::Context::Scope context_scope(context);
@@ -370,15 +369,16 @@
 }
 
 void WebViewPlugin::WebViewHelper::FrameDetached(DetachType type) {
-  main_frame()->FrameWidget()->Close();
-  main_frame()->Close();
+  frame_->FrameWidget()->Close();
+  frame_->Close();
+  frame_ = nullptr;
 }
 
 void WebViewPlugin::WebViewHelper::BeginNavigation(
     std::unique_ptr<blink::WebNavigationInfo> info) {
   // TODO(dgozman): remove this method and effectively disallow
   // content-inititated navigations in WebViewPlugin.
-  main_frame()->CommitNavigation(
+  frame_->CommitNavigation(
       info->url_request, info->frame_load_type, blink::WebHistoryItem(),
       info->is_client_redirect, base::UnguessableToken::Create(),
       nullptr /* navigation_params */, nullptr /* extra_data */);
diff --git a/components/plugins/renderer/webview_plugin.h b/components/plugins/renderer/webview_plugin.h
index 015ed87..111fb708 100644
--- a/components/plugins/renderer/webview_plugin.h
+++ b/components/plugins/renderer/webview_plugin.h
@@ -16,6 +16,7 @@
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/blink.h"
 #include "third_party/blink/public/web/web_local_frame_client.h"
+#include "third_party/blink/public/web/web_navigation_control.h"
 #include "third_party/blink/public/web/web_plugin.h"
 #include "third_party/blink/public/web/web_view_client.h"
 #include "third_party/blink/public/web/web_widget_client.h"
@@ -157,7 +158,7 @@
     ~WebViewHelper() override;
 
     blink::WebView* web_view() { return web_view_; }
-    blink::WebLocalFrame* main_frame();
+    blink::WebNavigationControl* main_frame() { return frame_; }
 
     // WebViewClient methods:
     bool AcceptsLoadDrops() override;
@@ -184,6 +185,7 @@
         override;
 
     // WebLocalFrameClient methods:
+    void BindToFrame(blink::WebNavigationControl* frame) override;
     void DidClearWindowObject() override;
     void FrameDetached(DetachType) override;
     void BeginNavigation(
@@ -191,6 +193,7 @@
 
    private:
     WebViewPlugin* plugin_;
+    blink::WebNavigationControl* frame_ = nullptr;
 
     // Owned by us, deleted via |close()|.
     blink::WebView* web_view_;
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 3a91761..9fb226cd 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_local_frame_client.h"
+#include "third_party/blink/public/web/web_navigation_control.h"
 #include "third_party/blink/public/web/web_plugin.h"
 #include "third_party/blink/public/web/web_plugin_document.h"
 #include "third_party/blink/public/web/web_print_params.h"
@@ -670,7 +671,9 @@
   class HeaderAndFooterClient final : public blink::WebLocalFrameClient {
    public:
     // WebLocalFrameClient:
-    void BindToFrame(blink::WebLocalFrame* frame) override { frame_ = frame; }
+    void BindToFrame(blink::WebNavigationControl* frame) override {
+      frame_ = frame;
+    }
     void FrameDetached(DetachType detach_type) override {
       frame_->FrameWidget()->Close();
       frame_->Close();
@@ -685,7 +688,7 @@
     }
 
    private:
-    blink::WebLocalFrame* frame_;
+    blink::WebNavigationControl* frame_ = nullptr;
   };
 
   class NonCompositingWebWidgetClient : public blink::WebWidgetClient {
@@ -788,6 +791,7 @@
   WebWidgetClient* WidgetClient() override { return this; }
 
   // blink::WebLocalFrameClient:
+  void BindToFrame(blink::WebNavigationControl* frame) override;
   blink::WebLocalFrame* CreateChildFrame(
       blink::WebLocalFrame* parent,
       blink::WebTreeScopeType scope,
@@ -807,6 +811,7 @@
   void CopySelection(const WebPreferences& preferences);
 
   FrameReference frame_;
+  blink::WebNavigationControl* navigation_control_ = nullptr;
   blink::WebNode node_to_print_;
   bool owns_web_view_ = false;
   blink::WebPrintParams web_print_params_;
@@ -939,8 +944,8 @@
 
   // When loading is done this will call didStopLoading() and that will do the
   // actual printing.
-  frame()->LoadHTMLString(blink::WebData(html),
-                          blink::WebURL(GURL(url::kAboutBlankURL)));
+  navigation_control_->LoadHTMLString(blink::WebData(html),
+                                      blink::WebURL(GURL(url::kAboutBlankURL)));
 }
 
 bool PrepareFrameAndViewForPrint::AllowsBrokenNullLayerTreeView() const {
@@ -960,6 +965,11 @@
                                 weak_ptr_factory_.GetWeakPtr()));
 }
 
+void PrepareFrameAndViewForPrint::BindToFrame(
+    blink::WebNavigationControl* navigation_control) {
+  navigation_control_ = navigation_control;
+}
+
 blink::WebLocalFrame* PrepareFrameAndViewForPrint::CreateChildFrame(
     blink::WebLocalFrame* parent,
     blink::WebTreeScopeType scope,
@@ -981,6 +991,7 @@
   DCHECK(frame);
   frame->FrameWidget()->Close();
   frame->Close();
+  navigation_control_ = nullptr;
   frame_.Reset(nullptr);
 }
 
@@ -988,7 +999,7 @@
     std::unique_ptr<blink::WebNavigationInfo> info) {
   // TODO(dgozman): We disable javascript through WebPreferences, so perhaps
   // we want to disallow any navigations here by just removing this method?
-  frame()->CommitNavigation(
+  navigation_control_->CommitNavigation(
       info->url_request, info->frame_load_type, blink::WebHistoryItem(),
       info->is_client_redirect, base::UnguessableToken::Create(),
       nullptr /* navigation_params */, nullptr /* extra_data */);
@@ -1039,6 +1050,7 @@
       web_view->MainFrameWidget()->Close();
     }
   }
+  navigation_control_ = nullptr;
   frame_.Reset(nullptr);
   on_ready_.Reset();
 }
diff --git a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-25.png b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-25.png
index fcb15074..7ca1ffac 100644
--- a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-25.png
+++ b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-25.png
Binary files differ
diff --git a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-26.png b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-26.png
index 6d0c881..546724a 100644
--- a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-26.png
+++ b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_alert.Pixel_XL-26.png
Binary files differ
diff --git a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-25.png b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-25.png
index 7d69967..d545f85c 100644
--- a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-25.png
+++ b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-25.png
Binary files differ
diff --git a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-26.png b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-26.png
index 50fc5eb3..5971b68 100644
--- a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-26.png
+++ b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_confirm.Pixel_XL-26.png
Binary files differ
diff --git a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-25.png b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-25.png
index 39d529d..6b729e3 100644
--- a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-25.png
+++ b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-25.png
Binary files differ
diff --git a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-26.png b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-26.png
index aec7f9973..ea29cb3d 100644
--- a/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-26.png
+++ b/components/test/data/js_dialogs/render_tests/VrBrowserJavaScriptModalDialogTest.js_modal_view_vr_prompt.Pixel_XL-26.png
Binary files differ
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-25.png b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-25.png
index 2168ff5..307c4e4 100644
--- a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-25.png
+++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-25.png
Binary files differ
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-26.png b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-26.png
index 41932e3..a25bffee 100644
--- a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-26.png
+++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_granted_browser_ui.Pixel_XL-26.png
Binary files differ
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-25.png b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-25.png
index c20d6ad56..5769baa 100644
--- a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-25.png
+++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-25.png
Binary files differ
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-26.png b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-26.png
index 54d15f3..ba02734 100644
--- a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-26.png
+++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_visible_browser_ui.Pixel_XL-26.png
Binary files differ
diff --git a/components/viz/host/hit_test/hit_test_query_fuzzer.cc b/components/viz/host/hit_test/hit_test_query_fuzzer.cc
index 772637e5..89d2da18 100644
--- a/components/viz/host/hit_test/hit_test_query_fuzzer.cc
+++ b/components/viz/host/hit_test/hit_test_query_fuzzer.cc
@@ -14,11 +14,6 @@
 
 namespace {
 
-uint32_t GetNextUInt32(base::FuzzedDataProvider* fuzz) {
-  return fuzz->ConsumeUint32InRange(std::numeric_limits<uint32_t>::min(),
-                                    std::numeric_limits<uint32_t>::max());
-}
-
 void AddHitTestRegion(base::FuzzedDataProvider* fuzz,
                       std::vector<viz::AggregatedHitTestRegion>* regions,
                       std::vector<viz::FrameSinkId>* frame_sink_ids,
@@ -26,17 +21,17 @@
   constexpr uint32_t kMaxDepthAllowed = 25;
   if (fuzz->remaining_bytes() < sizeof(viz::AggregatedHitTestRegion))
     return;
-  viz::FrameSinkId frame_sink_id(GetNextUInt32(fuzz), GetNextUInt32(fuzz));
-  uint32_t flags = GetNextUInt32(fuzz);
+  viz::FrameSinkId frame_sink_id(fuzz->ConsumeUint32(), fuzz->ConsumeUint32());
+  uint32_t flags = fuzz->ConsumeUint32();
   // The reasons' value is kNotAsyncHitTest if the flag's value is kHitTestAsk.
-  uint32_t reasons =
-      (flags & viz::HitTestRegionFlags::kHitTestAsk)
-          ? fuzz->ConsumeUint32InRange(1, std::numeric_limits<uint32_t>::max())
-          : viz::AsyncHitTestReasons::kNotAsyncHitTest;
+  uint32_t reasons = (flags & viz::HitTestRegionFlags::kHitTestAsk)
+                         ? fuzz->ConsumeIntegralInRange<uint32_t>(
+                               1, std::numeric_limits<uint32_t>::max())
+                         : viz::AsyncHitTestReasons::kNotAsyncHitTest;
   gfx::Rect rect(fuzz->ConsumeUint8(), fuzz->ConsumeUint8(),
                  fuzz->ConsumeUint16(), fuzz->ConsumeUint16());
   int32_t child_count =
-      depth < kMaxDepthAllowed ? fuzz->ConsumeUint32InRange(0, 10) : 0;
+      depth < kMaxDepthAllowed ? fuzz->ConsumeIntegralInRange(0, 10) : 0;
   gfx::Transform transform;
   if (fuzz->ConsumeBool() && fuzz->remaining_bytes() >= sizeof(transform)) {
     std::vector<uint8_t> matrix_bytes =
diff --git a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
index e534bb0..957fa0a 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
+++ b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
@@ -15,6 +15,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/client_native_pixmap_factory.h"
 
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
 namespace viz {
 
 namespace {
@@ -171,34 +175,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestGpuService);
 };
 
-// It is necessary to install a custom pixmap factory which claims to support
-// all native configurations, so that code that deals with this can be tested
-// correctly.
-class FakeClientNativePixmapFactory : public gfx::ClientNativePixmapFactory {
- public:
-  explicit FakeClientNativePixmapFactory(bool allow_native_buffers)
-      : allow_native_buffers_(allow_native_buffers) {}
-  ~FakeClientNativePixmapFactory() override {}
-
-  // gfx::ClientNativePixmapFactory:
-  bool IsConfigurationSupported(gfx::BufferFormat format,
-                                gfx::BufferUsage usage) const override {
-    return allow_native_buffers_;
-  }
-  std::unique_ptr<gfx::ClientNativePixmap> ImportFromHandle(
-      const gfx::NativePixmapHandle& handle,
-      const gfx::Size& size,
-      gfx::BufferUsage usage) override {
-    NOTREACHED();
-    return nullptr;
-  }
-
- private:
-  bool allow_native_buffers_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeClientNativePixmapFactory);
-};
-
 }  // namespace
 
 class HostGpuMemoryBufferManagerTest : public ::testing::Test {
@@ -232,32 +208,33 @@
   DISALLOW_COPY_AND_ASSIGN(HostGpuMemoryBufferManagerTest);
 };
 
-std::unique_ptr<gpu::GpuMemoryBufferSupport> MakeGpuMemoryBufferSupport(
-    bool allow_native_buffers) {
-#if defined(OS_LINUX)
-  return std::make_unique<gpu::GpuMemoryBufferSupport>(
-      std::make_unique<FakeClientNativePixmapFactory>(allow_native_buffers));
-#else
-  return std::make_unique<gpu::GpuMemoryBufferSupport>();
-#endif
-}
-
 // Tests that allocation requests from a client that goes away before allocation
 // completes are cleaned up correctly.
 TEST_F(HostGpuMemoryBufferManagerTest, AllocationRequestsForDestroyedClient) {
-#if !defined(USE_OZONE) && !defined(OS_MACOSX) && !defined(OS_WIN)
-  // Not all platforms support native configurations (currently only ozone and
-  // mac support it). Abort the test in those platforms.
-  gpu::GpuMemoryBufferSupport support;
-  DCHECK(gpu::GetNativeGpuMemoryBufferConfigurations(&support).empty());
-  return;
-#else
+  // Not all platforms support native configurations (currently only Windows,
+  // Mac and some Ozone platforms). Abort the test in those platforms.
+  bool native_pixmap_supported = false;
+#if defined(USE_OZONE)
+  native_pixmap_supported =
+      ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported(
+          gfx::BufferFormat::RGBA_8888, gfx::BufferUsage::GPU_READ);
+#elif defined(OS_MACOSX) || defined(OS_WIN)
+  native_pixmap_supported = true;
+#endif
+
+  if (!native_pixmap_supported) {
+    gpu::GpuMemoryBufferSupport support;
+    DCHECK(gpu::GetNativeGpuMemoryBufferConfigurations(&support).empty());
+    return;
+  }
+
   // Note: HostGpuMemoryBufferManager normally operates on a mojom::GpuService
   // implementation over mojo. Which means the communication from SGMBManager to
   // GpuService is asynchronous. In this test, the mojom::GpuService is not
   // bound to a mojo pipe, which means those calls are all synchronous.
   TestGpuService gpu_service;
-  auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(true);
+  auto gpu_memory_buffer_support =
+      std::make_unique<gpu::GpuMemoryBufferSupport>();
   HostGpuMemoryBufferManager manager(gpu_service.CreateProvider(), 1,
                                      std::move(gpu_memory_buffer_support),
                                      base::ThreadTaskRunnerHandle::Get());
@@ -282,12 +259,12 @@
   // should request the allocated memory to be freed.
   gpu_service.SatisfyAllocationRequest(buffer_id, client_id);
   EXPECT_TRUE(gpu_service.HasDestructionRequest(buffer_id, client_id));
-#endif
 }
 
 TEST_F(HostGpuMemoryBufferManagerTest, RequestsFromUntrustedClientsValidated) {
   TestGpuService gpu_service;
-  auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(false);
+  auto gpu_memory_buffer_support =
+      std::make_unique<gpu::GpuMemoryBufferSupport>();
   HostGpuMemoryBufferManager manager(gpu_service.CreateProvider(), 1,
                                      std::move(gpu_memory_buffer_support),
                                      base::ThreadTaskRunnerHandle::Get());
@@ -332,7 +309,8 @@
 
 TEST_F(HostGpuMemoryBufferManagerTest, GpuMemoryBufferDestroyed) {
   TestGpuService gpu_service;
-  auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(false);
+  auto gpu_memory_buffer_support =
+      std::make_unique<gpu::GpuMemoryBufferSupport>();
   HostGpuMemoryBufferManager manager(gpu_service.CreateProvider(), 1,
                                      std::move(gpu_memory_buffer_support),
                                      base::ThreadTaskRunnerHandle::Get());
@@ -344,7 +322,8 @@
 TEST_F(HostGpuMemoryBufferManagerTest,
        GpuMemoryBufferDestroyedOnDifferentThread) {
   TestGpuService gpu_service;
-  auto gpu_memory_buffer_support = MakeGpuMemoryBufferSupport(false);
+  auto gpu_memory_buffer_support =
+      std::make_unique<gpu::GpuMemoryBufferSupport>();
   HostGpuMemoryBufferManager manager(gpu_service.CreateProvider(), 1,
                                      std::move(gpu_memory_buffer_support),
                                      base::ThreadTaskRunnerHandle::Get());
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 06e64fe..35732a7 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -4153,10 +4153,6 @@
       /*secure_output_only=*/false, ui::ProtectedVideoType::kClear);
 
   EXPECT_CALL(overlay_scheduler,
-              Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, kSurfaceOverlayTextureId,
-                       gfx::Rect(viewport_size), _, _, kGpuFenceId))
-      .Times(1);
-  EXPECT_CALL(overlay_scheduler,
               Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _,
                        gfx::Rect(viewport_size), _, _, kGpuFenceId))
       .Times(1);
diff --git a/components/viz/service/display/overlay_processor.cc b/components/viz/service/display/overlay_processor.cc
index 20bd647..bc662fce 100644
--- a/components/viz/service/display/overlay_processor.cc
+++ b/components/viz/service/display/overlay_processor.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/service/display/dc_layer_overlay.h"
 #include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/display/output_surface.h"
@@ -251,4 +252,61 @@
   damage_rect->Union(output_surface_overlay_damage_rect);
 }
 
+namespace {
+
+bool DiscardableQuad(const DrawQuad* q) {
+  float opacity = q->shared_quad_state->opacity;
+  if (opacity < std::numeric_limits<float>::epsilon())
+    return true;
+
+  if (q->material == DrawQuad::SOLID_COLOR) {
+    if (SolidColorDrawQuad::MaterialCast(q)->color == SK_ColorBLACK ||
+        SolidColorDrawQuad::MaterialCast(q)->color == SK_ColorTRANSPARENT)
+      return true;
+
+    const SkColor color = SolidColorDrawQuad::MaterialCast(q)->color;
+    const float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
+    return q->ShouldDrawWithBlending() &&
+           alpha < std::numeric_limits<float>::epsilon();
+  }
+
+  return false;
+}
+
+}  // namespace
+
+// static
+void OverlayProcessor::EliminateOrCropPrimary(
+    const QuadList& quad_list,
+    const QuadList::Iterator& candidate_iterator,
+    OverlayCandidate* primary,
+    OverlayCandidateList* candidate_list) {
+  gfx::RectF content_rect;
+
+  for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
+    if (it == candidate_iterator)
+      continue;
+    if (!DiscardableQuad(*it)) {
+      auto& transform = it->shared_quad_state->quad_to_target_transform;
+      gfx::RectF display_rect = gfx::RectF(it->rect);
+      transform.TransformRect(&display_rect);
+      content_rect.Union(display_rect);
+    }
+  }
+
+  if (!content_rect.IsEmpty()) {
+    // Sometimes the content quads extend past primary->display_rect, so first
+    // clip the content_rect to that.
+    content_rect.Intersect(primary->display_rect);
+    DCHECK_NE(0, primary->display_rect.width());
+    DCHECK_NE(0, primary->display_rect.height());
+    primary->uv_rect = gfx::ScaleRect(
+        content_rect, 1. / primary->resource_size_in_pixels.width(),
+        1. / primary->resource_size_in_pixels.height());
+    primary->display_rect = content_rect;
+
+    candidate_list->push_back(*primary);
+  }
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display/overlay_processor.h b/components/viz/service/display/overlay_processor.h
index e3f25e0..14797b4 100644
--- a/components/viz/service/display/overlay_processor.h
+++ b/components/viz/service/display/overlay_processor.h
@@ -79,6 +79,15 @@
       gfx::Rect* damage_rect,
       std::vector<gfx::Rect>* content_bounds);
 
+  // Determine if we can eliminate (all remaining quads are black or
+  // transparent) or crop (non-black content is a small sub-rectangle) the
+  // primary framebuffer.
+  static void EliminateOrCropPrimary(
+      const QuadList& quad_list,
+      const QuadList::Iterator& candidate_iterator,
+      OverlayCandidate* primary,
+      OverlayCandidateList* candidate_list);
+
  protected:
   StrategyList strategies_;
   OutputSurface* surface_;
diff --git a/components/viz/service/display/overlay_strategy_single_on_top.cc b/components/viz/service/display/overlay_strategy_single_on_top.cc
index ccfc620..100332f6 100644
--- a/components/viz/service/display/overlay_strategy_single_on_top.cc
+++ b/components/viz/service/display/overlay_strategy_single_on_top.cc
@@ -44,29 +44,25 @@
   if (best_quad_it == quad_list->end())
     return false;
 
-  if (TryOverlay(quad_list, candidate_list, best_candidate, best_quad_it))
-    return true;
+  OverlayCandidateList new_candidate_list;
+  if (candidate_list->size() == 1) {
+    OverlayCandidate primary(candidate_list->back());
+    OverlayProcessor::EliminateOrCropPrimary(*quad_list, best_quad_it, &primary,
+                                             &new_candidate_list);
+  } else {
+    new_candidate_list = *candidate_list;
+  }
 
-  return false;
-}
-
-bool OverlayStrategySingleOnTop::TryOverlay(
-    QuadList* quad_list,
-    OverlayCandidateList* candidate_list,
-    const OverlayCandidate& candidate,
-    QuadList::Iterator candidate_iterator) {
   // Add the overlay.
-  OverlayCandidateList new_candidate_list = *candidate_list;
-  new_candidate_list.push_back(candidate);
+  new_candidate_list.push_back(best_candidate);
   new_candidate_list.back().plane_z_order = 1;
 
   // Check for support.
   capability_checker_->CheckOverlaySupport(&new_candidate_list);
 
-  const OverlayCandidate& overlay_candidate = new_candidate_list.back();
   // If the candidate can be handled by an overlay, create a pass for it.
-  if (overlay_candidate.overlay_handled) {
-    quad_list->EraseAndInvalidateAllPointers(candidate_iterator);
+  if (new_candidate_list.back().overlay_handled) {
+    quad_list->EraseAndInvalidateAllPointers(best_quad_it);
     candidate_list->swap(new_candidate_list);
     return true;
   }
diff --git a/components/viz/service/display/overlay_strategy_underlay.cc b/components/viz/service/display/overlay_strategy_underlay.cc
index 2dd89a5..b0bf8355 100644
--- a/components/viz/service/display/overlay_strategy_underlay.cc
+++ b/components/viz/service/display/overlay_strategy_underlay.cc
@@ -57,11 +57,19 @@
       continue;
     }
 
+    OverlayCandidateList new_candidate_list;
+    if (candidate_list->size() == 1) {
+      OverlayCandidate primary(candidate_list->back());
+      primary.is_opaque = false;
+      OverlayProcessor::EliminateOrCropPrimary(quad_list, it, &primary,
+                                               &new_candidate_list);
+    } else {
+      new_candidate_list = *candidate_list;
+    }
+
     // Add the overlay.
-    OverlayCandidateList new_candidate_list = *candidate_list;
     new_candidate_list.push_back(candidate);
     new_candidate_list.back().plane_z_order = -1;
-    new_candidate_list.front().is_opaque = false;
 
     // Check for support.
     capability_checker_->CheckOverlaySupport(&new_candidate_list);
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 8a3c848..411b85f 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -583,6 +583,18 @@
   std::vector<gfx::Rect> content_bounds_;
 };
 
+OverlayCandidateList BackbufferOverlayList(const RenderPass* root_render_pass) {
+  OverlayCandidateList list;
+  OverlayCandidate output_surface_plane;
+  output_surface_plane.display_rect = gfx::RectF(root_render_pass->output_rect);
+  output_surface_plane.resource_size_in_pixels =
+      root_render_pass->output_rect.size();
+  output_surface_plane.use_output_surface_for_resource = true;
+  output_surface_plane.overlay_handled = true;
+  list.push_back(output_surface_plane);
+  return list;
+}
+
 using FullscreenOverlayTest = OverlayTest<FullscreenOverlayValidator>;
 using SingleOverlayOnTopTest = OverlayTest<SingleOnTopOverlayValidator>;
 using UnderlayTest = OverlayTest<UnderlayOverlayValidator>;
@@ -1544,6 +1556,25 @@
   EXPECT_EQ(0U, candidate_list.size());
 }
 
+TEST_F(SingleOverlayOnTopTest, AllowVideoNormalTransform) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateVideoQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(),
+      kNormalTransform);
+
+  OverlayCandidateList candidate_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_background_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_background_filters, &candidate_list,
+      nullptr, nullptr, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, candidate_list.size());
+}
+
 TEST_F(SingleOverlayOnTopTest, RejectVideoSwapTransform) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   CreateFullscreenCandidateVideoQuad(
@@ -1563,6 +1594,33 @@
   EXPECT_EQ(0U, candidate_list.size());
 }
 
+TEST_F(SingleOverlayOnTopTest,
+       AllowVideoNormalTransformWithOutputSurfaceOverlay) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+
+  gfx::Rect video_rect = gfx::ScaleToEnclosingRect(pass->output_rect, .5);
+  CreateCandidateVideoQuadAt(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(),
+      video_rect, kNormalTransform);
+
+  OverlayCandidateList candidate_list(BackbufferOverlayList(pass.get()));
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_background_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_background_filters, &candidate_list,
+      nullptr, nullptr, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(2U, candidate_list.size());
+  EXPECT_EQ(candidate_list[0].uv_rect, gfx::RectF(.5, .5));
+  EXPECT_EQ(candidate_list[1].uv_rect, gfx::RectF(.1, .2, .9, .8));
+}
+
 TEST_F(UnderlayTest, AllowVideoXMirrorTransform) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   CreateFullscreenCandidateVideoQuad(
@@ -1928,6 +1986,7 @@
 
   OverlayCandidateList candidate_list;
   OverlayCandidate candidate;
+  candidate.display_rect = gfx::RectF(pass->output_rect);
   candidate.use_output_surface_for_resource = true;
   candidate.is_opaque = true;
   candidate_list.push_back(candidate);
@@ -2226,6 +2285,7 @@
 
   OverlayCandidateList candidate_list;
   OverlayCandidate candidate;
+  candidate.display_rect = gfx::RectF(pass->output_rect);
   candidate.use_output_surface_for_resource = true;
   candidate.is_opaque = true;
   candidate_list.push_back(candidate);
@@ -2265,16 +2325,6 @@
   EXPECT_TRUE(content_bounds_.empty());
 }
 
-OverlayCandidateList BackbufferOverlayList(const RenderPass* root_render_pass) {
-  OverlayCandidateList list;
-  OverlayCandidate output_surface_plane;
-  output_surface_plane.display_rect = gfx::RectF(root_render_pass->output_rect);
-  output_surface_plane.use_output_surface_for_resource = true;
-  output_surface_plane.overlay_handled = true;
-  list.push_back(output_surface_plane);
-  return list;
-}
-
 TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   CreateFullscreenCandidateQuad(
@@ -2808,7 +2858,7 @@
                         OutputSurface* output_surface,
                         DisplayResourceProvider* resource_provider)
       : GLRenderer(settings, output_surface, resource_provider, nullptr),
-        expect_overlays_(false) {}
+        expected_overlay_count_(0) {}
 
   MOCK_METHOD2(DoDrawQuad,
                void(const DrawQuad* quad, const gfx::QuadF* draw_region));
@@ -2822,21 +2872,17 @@
   void FinishDrawingFrame() override {
     GLRenderer::FinishDrawingFrame();
 
-    if (!expect_overlays_) {
-      EXPECT_EQ(0U, current_frame()->overlay_list.size());
-      return;
-    }
-
-    ASSERT_EQ(2U, current_frame()->overlay_list.size());
-    EXPECT_GE(current_frame()->overlay_list.back().resource_id, 0U);
+    ASSERT_EQ(expected_overlay_count_, current_frame()->overlay_list.size());
+    if (expected_overlay_count_ > 0)
+      EXPECT_GE(current_frame()->overlay_list.back().resource_id, 0U);
   }
 
-  void set_expect_overlays(bool expect_overlays) {
-    expect_overlays_ = expect_overlays;
+  void set_expected_overlay_count(unsigned int overlay_count) {
+    expected_overlay_count_ = overlay_count;
   }
 
  private:
-  bool expect_overlays_;
+  unsigned int expected_overlay_count_;
 };
 
 class MockOverlayScheduler {
@@ -2921,7 +2967,7 @@
 TEST_F(GLRendererWithOverlaysTest, OverlayQuadNotDrawn) {
   bool use_validator = true;
   Init(use_validator);
-  renderer_->set_expect_overlays(true);
+  renderer_->set_expected_overlay_count(1);
   output_surface_->GetOverlayCandidateValidator()->AddExpectedRect(
       gfx::RectF(kOverlayBottomRightRect));
 
@@ -2942,10 +2988,6 @@
   // Candidate pass was taken out and extra skipped pass added,
   // so only draw 2 quads.
   EXPECT_CALL(*renderer_, DoDrawQuad(_, _)).Times(2);
-  EXPECT_CALL(scheduler_,
-              Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, _,
-                       gfx::Rect(kDisplaySize), gfx::RectF(0, 0, 1, 1), _, _))
-      .Times(1);
   EXPECT_CALL(
       scheduler_,
       Schedule(1, gfx::OVERLAY_TRANSFORM_NONE, _, kOverlayBottomRightRect,
@@ -2963,7 +3005,7 @@
 TEST_F(GLRendererWithOverlaysTest, OccludedQuadInUnderlay) {
   bool use_validator = true;
   Init(use_validator);
-  renderer_->set_expect_overlays(true);
+  renderer_->set_expected_overlay_count(1);
 
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
 
@@ -2983,10 +3025,6 @@
   // Expect to be replaced with transparent hole quad and placed in underlay.
   EXPECT_CALL(*renderer_, DoDrawQuad(_, _)).Times(3);
   EXPECT_CALL(scheduler_,
-              Schedule(0, gfx::OVERLAY_TRANSFORM_NONE, _,
-                       gfx::Rect(kDisplaySize), gfx::RectF(0, 0, 1, 1), _, _))
-      .Times(1);
-  EXPECT_CALL(scheduler_,
               Schedule(-1, gfx::OVERLAY_TRANSFORM_NONE, _, kOverlayRect,
                        BoundingRect(kUVTopLeft, kUVBottomRight), _, _))
       .Times(1);
@@ -3002,7 +3040,7 @@
 TEST_F(GLRendererWithOverlaysTest, NoValidatorNoOverlay) {
   bool use_validator = false;
   Init(use_validator);
-  renderer_->set_expect_overlays(false);
+  renderer_->set_expected_overlay_count(0);
 
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
 
@@ -3029,13 +3067,14 @@
   Mock::VerifyAndClearExpectations(&scheduler_);
 }
 
-// GLRenderer skips drawing occluded quads when partial swap is enabled.
+// GLRenderer skips drawing occluded quads when partial swap is enabled and
+// turns off primary plane.
 TEST_F(GLRendererWithOverlaysTest, OccludedQuadNotDrawnWhenPartialSwapEnabled) {
   provider_->TestContextGL()->set_have_post_sub_buffer(true);
   settings_.partial_swap_enabled = true;
   bool use_validator = true;
   Init(use_validator);
-  renderer_->set_expect_overlays(true);
+  renderer_->set_expected_overlay_count(1);
 
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
 
@@ -3052,7 +3091,8 @@
 
   output_surface_->set_is_displayed_as_overlay_plane(true);
   EXPECT_CALL(*renderer_, DoDrawQuad(_, _)).Times(0);
-  EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _, _, _)).Times(2);
+  // Only the candidate quad gets scheduled
+  EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _, _, _)).Times(1);
   DrawFrame(&pass_list, kDisplaySize);
   EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
   SwapBuffers();
@@ -3060,12 +3100,13 @@
   Mock::VerifyAndClearExpectations(&scheduler_);
 }
 
-// GLRenderer skips drawing occluded quads when empty swap is enabled.
+// GLRenderer skips drawing occluded quads when empty swap is enabled and
+// turns off primary plane.
 TEST_F(GLRendererWithOverlaysTest, OccludedQuadNotDrawnWhenEmptySwapAllowed) {
   provider_->TestContextGL()->set_have_commit_overlay_planes(true);
   bool use_validator = true;
   Init(use_validator);
-  renderer_->set_expect_overlays(true);
+  renderer_->set_expected_overlay_count(1);
 
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
 
@@ -3083,7 +3124,8 @@
 
   output_surface_->set_is_displayed_as_overlay_plane(true);
   EXPECT_CALL(*renderer_, DoDrawQuad(_, _)).Times(0);
-  EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _, _, _)).Times(2);
+  // Only the candidate quad gets scheduled
+  EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _, _, _)).Times(1);
   DrawFrame(&pass_list, kDisplaySize);
   EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
   SwapBuffers();
@@ -3094,7 +3136,7 @@
 TEST_F(GLRendererWithOverlaysTest, ResourcesExportedAndReturnedWithDelay) {
   bool use_validator = true;
   Init(use_validator);
-  renderer_->set_expect_overlays(true);
+  renderer_->set_expected_overlay_count(2);
 
   ResourceId resource1 = CreateResourceInLayerTree(
       child_resource_provider_.get(), gfx::Size(32, 32), true);
@@ -3202,7 +3244,7 @@
   EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _, _, _)).Times(0);
   DirectRenderer::DrawingFrame frame_no_overlays;
   frame_no_overlays.render_passes_in_draw_order = &pass_list;
-  renderer_->set_expect_overlays(false);
+  renderer_->set_expected_overlay_count(0);
   renderer_->SetCurrentFrame(frame_no_overlays);
   renderer_->BeginDrawingFrame();
   renderer_->FinishDrawingFrame();
@@ -3221,7 +3263,7 @@
   EXPECT_FALSE(resource_provider_->InUse(mapped_resource3));
 
   // Use the same buffer twice.
-  renderer_->set_expect_overlays(true);
+  renderer_->set_expected_overlay_count(2);
   EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _, _, _)).Times(2);
   renderer_->SetCurrentFrame(frame1);
   renderer_->BeginDrawingFrame();
@@ -3259,7 +3301,7 @@
   EXPECT_FALSE(resource_provider_->InUse(mapped_resource3));
 
   EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _, _, _)).Times(0);
-  renderer_->set_expect_overlays(false);
+  renderer_->set_expected_overlay_count(0);
   renderer_->SetCurrentFrame(frame_no_overlays);
   renderer_->BeginDrawingFrame();
   renderer_->FinishDrawingFrame();
@@ -3283,7 +3325,7 @@
   bool use_validator = true;
   settings_.release_overlay_resources_after_gpu_query = true;
   Init(use_validator);
-  renderer_->set_expect_overlays(true);
+  renderer_->set_expected_overlay_count(2);
 
   ResourceId resource1 = CreateResourceInLayerTree(
       child_resource_provider_.get(), gfx::Size(32, 32), true);
diff --git a/components/viz/service/hit_test/hit_test_manager_fuzzer.cc b/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
index d53a3ac..e2f0268 100644
--- a/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
+++ b/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
@@ -25,7 +25,8 @@
 // TODO(riajiang): Move into common functions that can be used by the fuzzer
 // for HitTestQuery.
 uint32_t GetNextUInt32NonZero(base::FuzzedDataProvider* fuzz) {
-  return fuzz->ConsumeUint32InRange(1, std::numeric_limits<uint32_t>::max());
+  return fuzz->ConsumeIntegralInRange<uint32_t>(
+      1, std::numeric_limits<uint32_t>::max());
 }
 
 gfx::Transform GetNextTransform(base::FuzzedDataProvider* fuzz) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index a1060f0..c5c7097 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -619,8 +619,6 @@
     "cookie_store/cookie_store_host.h",
     "cookie_store/cookie_store_manager.cc",
     "cookie_store/cookie_store_manager.h",
-    "dedicated_worker/dedicated_worker_host.cc",
-    "dedicated_worker/dedicated_worker_host.h",
     "devtools/browser_devtools_agent_host.cc",
     "devtools/browser_devtools_agent_host.h",
     "devtools/devtools_agent_host_impl.cc",
@@ -1692,24 +1690,6 @@
     "service_worker/service_worker_version.h",
     "service_worker/service_worker_write_to_cache_job.cc",
     "service_worker/service_worker_write_to_cache_job.h",
-    "shared_worker/shared_worker_connector_impl.cc",
-    "shared_worker/shared_worker_connector_impl.h",
-    "shared_worker/shared_worker_content_settings_proxy_impl.cc",
-    "shared_worker/shared_worker_content_settings_proxy_impl.h",
-    "shared_worker/shared_worker_host.cc",
-    "shared_worker/shared_worker_host.h",
-    "shared_worker/shared_worker_instance.cc",
-    "shared_worker/shared_worker_instance.h",
-    "shared_worker/shared_worker_service_impl.cc",
-    "shared_worker/shared_worker_service_impl.h",
-    "shared_worker/worker_script_fetch_initiator.cc",
-    "shared_worker/worker_script_fetch_initiator.h",
-    "shared_worker/worker_script_fetcher.cc",
-    "shared_worker/worker_script_fetcher.h",
-    "shared_worker/worker_script_loader.cc",
-    "shared_worker/worker_script_loader.h",
-    "shared_worker/worker_script_loader_factory.cc",
-    "shared_worker/worker_script_loader_factory.h",
     "site_instance_impl.cc",
     "site_instance_impl.h",
     "speech/speech_recognition_dispatcher_host.cc",
@@ -1877,6 +1857,26 @@
     "webui/web_ui_message_handler.cc",
     "webui/web_ui_url_loader_factory.cc",
     "webui/web_ui_url_loader_factory_internal.h",
+    "worker_host/dedicated_worker_host.cc",
+    "worker_host/dedicated_worker_host.h",
+    "worker_host/shared_worker_connector_impl.cc",
+    "worker_host/shared_worker_connector_impl.h",
+    "worker_host/shared_worker_content_settings_proxy_impl.cc",
+    "worker_host/shared_worker_content_settings_proxy_impl.h",
+    "worker_host/shared_worker_host.cc",
+    "worker_host/shared_worker_host.h",
+    "worker_host/shared_worker_instance.cc",
+    "worker_host/shared_worker_instance.h",
+    "worker_host/shared_worker_service_impl.cc",
+    "worker_host/shared_worker_service_impl.h",
+    "worker_host/worker_script_fetch_initiator.cc",
+    "worker_host/worker_script_fetch_initiator.h",
+    "worker_host/worker_script_fetcher.cc",
+    "worker_host/worker_script_fetcher.h",
+    "worker_host/worker_script_loader.cc",
+    "worker_host/worker_script_loader.h",
+    "worker_host/worker_script_loader_factory.cc",
+    "worker_host/worker_script_loader_factory.h",
   ]
 
   if (toolkit_views) {
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc
index af607ed..c67b3f37 100644
--- a/content/browser/accessibility/browser_accessibility_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/accessibility/browser_accessibility.h"
 
+#include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -16,6 +17,7 @@
   ~BrowserAccessibilityTest() override;
 
  private:
+  base::test::ScopedTaskEnvironment task_environment_;
   DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityTest);
 };
 
diff --git a/content/browser/dedicated_worker/OWNERS b/content/browser/dedicated_worker/OWNERS
deleted file mode 100644
index eb8bfba6..0000000
--- a/content/browser/dedicated_worker/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-nhiroki@chromium.org
-
-# TEAM: worker-dev@chromium.org
-# COMPONENT: Blink>Workers
diff --git a/content/browser/dedicated_worker/README.md b/content/browser/dedicated_worker/README.md
deleted file mode 100644
index a11d35f..0000000
--- a/content/browser/dedicated_worker/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Dedicated Worker Host
-
-`content/browser/dedicated_worker` implements the host side of dedicated worker.
-It tracks the security principal of the worker in the renderer and uses it to
-broker access to mojo interfaces providing powerful web APIs. See: [Design
-doc].
-
-[Design doc]: https://docs.google.com/document/d/1Bg84lQqeJ8D2J-_wOOLlRVAtZNMstEUHmVhJqCxpjUk/edit?usp=sharing
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index 1a6a4d9..b044983b 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -26,32 +26,32 @@
 namespace content {
 namespace protocol {
 
-namespace {
-Storage::StorageType GetTypeName(storage::QuotaClient::ID id) {
-  switch (id) {
-    case storage::QuotaClient::kFileSystem:
-      return Storage::StorageTypeEnum::File_systems;
-    case storage::QuotaClient::kDatabase:
-      return Storage::StorageTypeEnum::Websql;
-    case storage::QuotaClient::kAppcache:
-      return Storage::StorageTypeEnum::Appcache;
-    case storage::QuotaClient::kIndexedDatabase:
-      return Storage::StorageTypeEnum::Indexeddb;
-    case storage::QuotaClient::kServiceWorkerCache:
-      return Storage::StorageTypeEnum::Cache_storage;
-    case storage::QuotaClient::kServiceWorker:
-      return Storage::StorageTypeEnum::Service_workers;
-    default:
-      return Storage::StorageTypeEnum::Other;
-  }
-}
+struct UsageListInitializer {
+  const char* type;
+  int64_t blink::mojom::UsageBreakdown::*usage_member;
+};
 
+UsageListInitializer initializers[] = {
+    {Storage::StorageTypeEnum::File_systems,
+     &blink::mojom::UsageBreakdown::fileSystem},
+    {Storage::StorageTypeEnum::Websql, &blink::mojom::UsageBreakdown::webSql},
+    {Storage::StorageTypeEnum::Appcache,
+     &blink::mojom::UsageBreakdown::appcache},
+    {Storage::StorageTypeEnum::Indexeddb,
+     &blink::mojom::UsageBreakdown::indexedDatabase},
+    {Storage::StorageTypeEnum::Cache_storage,
+     &blink::mojom::UsageBreakdown::serviceWorkerCache},
+    {Storage::StorageTypeEnum::Service_workers,
+     &blink::mojom::UsageBreakdown::serviceWorker},
+};
+
+namespace {
 void ReportUsageAndQuotaDataOnUIThread(
     std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback,
     blink::mojom::QuotaStatusCode code,
     int64_t usage,
     int64_t quota,
-    base::flat_map<storage::QuotaClient::ID, int64_t> usage_breakdown) {
+    blink::mojom::UsageBreakdownPtr usage_breakdown) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (code != blink::mojom::QuotaStatusCode::kOk) {
     return callback->sendFailure(
@@ -60,14 +60,17 @@
 
   std::unique_ptr<Array<Storage::UsageForType>> usageList =
       Array<Storage::UsageForType>::create();
-  for (const auto& specific_usage : usage_breakdown) {
+
+  blink::mojom::UsageBreakdown* breakdown_ptr = usage_breakdown.get();
+  for (const auto initializer : initializers) {
     std::unique_ptr<Storage::UsageForType> entry =
         Storage::UsageForType::Create()
-            .SetStorageType(GetTypeName(specific_usage.first))
-            .SetUsage(specific_usage.second)
+            .SetStorageType(initializer.type)
+            .SetUsage(breakdown_ptr->*(initializer.usage_member))
             .Build();
     usageList->addItem(std::move(entry));
   }
+
   callback->sendSuccess(usage, quota, std::move(usageList));
 }
 
@@ -76,7 +79,7 @@
     blink::mojom::QuotaStatusCode code,
     int64_t usage,
     int64_t quota,
-    base::flat_map<storage::QuotaClient::ID, int64_t> usage_breakdown) {
+    blink::mojom::UsageBreakdownPtr usage_breakdown) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.cc b/content/browser/devtools/shared_worker_devtools_agent_host.cc
index 7ff0a7d..a74e129 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.cc
@@ -12,9 +12,9 @@
 #include "content/browser/devtools/protocol/schema_handler.h"
 #include "content/browser/devtools/protocol/target_handler.h"
 #include "content/browser/devtools/shared_worker_devtools_manager.h"
-#include "content/browser/shared_worker/shared_worker_host.h"
-#include "content/browser/shared_worker/shared_worker_instance.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_instance.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "third_party/blink/public/web/devtools_agent.mojom.h"
diff --git a/content/browser/devtools/shared_worker_devtools_manager.cc b/content/browser/devtools/shared_worker_devtools_manager.cc
index 5731e4bb..d863a22f 100644
--- a/content/browser/devtools/shared_worker_devtools_manager.cc
+++ b/content/browser/devtools/shared_worker_devtools_manager.cc
@@ -5,7 +5,7 @@
 #include "content/browser/devtools/shared_worker_devtools_manager.h"
 
 #include "content/browser/devtools/shared_worker_devtools_agent_host.h"
-#include "content/browser/shared_worker/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_host.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace content {
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 60dac5f..3c9de03 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -35,7 +35,6 @@
 #include "content/browser/bluetooth/web_bluetooth_service_impl.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/dedicated_worker/dedicated_worker_host.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/dom_storage/dom_storage_context_wrapper.h"
 #include "content/browser/download/mhtml_generation_manager.h"
@@ -91,8 +90,6 @@
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
 #include "content/browser/renderer_interface_binders.h"
 #include "content/browser/scoped_active_url.h"
-#include "content/browser/shared_worker/shared_worker_connector_impl.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
 #include "content/browser/speech/speech_recognition_dispatcher_host.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/webauth/authenticator_impl.h"
@@ -101,6 +98,9 @@
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
 #include "content/browser/webui/web_ui_url_loader_factory_internal.h"
+#include "content/browser/worker_host/dedicated_worker_host.h"
+#include "content/browser/worker_host/shared_worker_connector_impl.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/common/accessibility_messages.h"
 #include "content/common/associated_interfaces.mojom.h"
 #include "content/common/content_security_policy/content_security_policy.h"
diff --git a/content/browser/media/audible_metrics.h b/content/browser/media/audible_metrics.h
index 02d9867..67290c1d 100644
--- a/content/browser/media/audible_metrics.h
+++ b/content/browser/media/audible_metrics.h
@@ -31,6 +31,10 @@
 
   void SetClockForTest(const base::TickClock* test_clock);
 
+  int GetAudibleWebContentsSizeForTest() const {
+    return audible_web_contents_.size();
+  }
+
  private:
   void AddAudibleWebContents(const WebContents* web_contents);
   void RemoveAudibleWebContents(const WebContents* web_contents);
diff --git a/content/browser/media/audible_metrics_unittest.cc b/content/browser/media/audible_metrics_unittest.cc
index a9f1b47..49006f7 100644
--- a/content/browser/media/audible_metrics_unittest.cc
+++ b/content/browser/media/audible_metrics_unittest.cc
@@ -8,6 +8,10 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/simple_test_tick_clock.h"
+#include "content/browser/media/media_web_contents_observer.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/test/test_web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
@@ -31,17 +35,23 @@
   AudibleMetricsTest() = default;
 
   void SetUp() override {
+    audible_metrics_ = std::make_unique<AudibleMetrics>();
+    browser_context_ = std::make_unique<TestBrowserContext>();
+
     // Set the clock to a value different than 0 so the time it gives is
     // recognized as initialized.
     clock_.Advance(base::TimeDelta::FromMilliseconds(1));
-    audible_metrics_.SetClockForTest(&clock_);
+    audible_metrics_->SetClockForTest(&clock_);
+  }
+
+  void TearDown() override {
+    audible_metrics_.reset();
+    browser_context_.reset();
   }
 
   base::SimpleTestTickClock* clock() { return &clock_; }
 
-  AudibleMetrics* audible_metrics() {
-    return &audible_metrics_;
-  };
+  AudibleMetrics* audible_metrics() { return audible_metrics_.get(); };
 
   const base::UserActionTester& user_action_tester() const {
     return user_action_tester_;
@@ -52,9 +62,18 @@
     return histogram_tester_.GetHistogramSamplesSinceCreation(name);
   }
 
+  std::unique_ptr<WebContentsImpl> CreateWebContents() {
+    return TestWebContents::Create(
+        browser_context_.get(), SiteInstance::Create(browser_context_.get()));
+  }
+
  private:
+  TestBrowserThreadBundle test_browser_thread_bundle_;
+
+  std::unique_ptr<AudibleMetrics> audible_metrics_;
+  std::unique_ptr<TestBrowserContext> browser_context_;
+
   base::SimpleTestTickClock clock_;
-  AudibleMetrics audible_metrics_;
   base::HistogramTester histogram_tester_;
   base::UserActionTester user_action_tester_;
 
@@ -352,4 +371,65 @@
   }
 }
 
+TEST_F(AudibleMetricsTest, MediaWebContentsObserver_Audible_Muted) {
+  std::unique_ptr<WebContentsImpl> web_contents(CreateWebContents());
+  MediaWebContentsObserver media_observer(web_contents.get());
+  media_observer.SetAudibleMetricsForTest(audible_metrics());
+
+  web_contents->SetAudioMuted(true);
+  web_contents->audio_stream_monitor()->set_is_currently_audible_for_testing(
+      true);
+
+  EXPECT_TRUE(web_contents->audio_stream_monitor()->IsCurrentlyAudible());
+  EXPECT_TRUE(web_contents->IsAudioMuted());
+
+  EXPECT_EQ(0, audible_metrics()->GetAudibleWebContentsSizeForTest());
+  media_observer.MaybeUpdateAudibleState();
+  EXPECT_EQ(0, audible_metrics()->GetAudibleWebContentsSizeForTest());
+}
+
+TEST_F(AudibleMetricsTest, MediaWebContentsObserver_Audible_NotMuted) {
+  std::unique_ptr<WebContentsImpl> web_contents(CreateWebContents());
+  MediaWebContentsObserver media_observer(web_contents.get());
+  media_observer.SetAudibleMetricsForTest(audible_metrics());
+
+  web_contents->audio_stream_monitor()->set_is_currently_audible_for_testing(
+      true);
+
+  EXPECT_TRUE(web_contents->audio_stream_monitor()->IsCurrentlyAudible());
+  EXPECT_FALSE(web_contents->IsAudioMuted());
+
+  EXPECT_EQ(0, audible_metrics()->GetAudibleWebContentsSizeForTest());
+  media_observer.MaybeUpdateAudibleState();
+  EXPECT_EQ(1, audible_metrics()->GetAudibleWebContentsSizeForTest());
+}
+
+TEST_F(AudibleMetricsTest, MediaWebContentsObserver_NotAudible_Muted) {
+  std::unique_ptr<WebContentsImpl> web_contents(CreateWebContents());
+  MediaWebContentsObserver media_observer(web_contents.get());
+  media_observer.SetAudibleMetricsForTest(audible_metrics());
+
+  web_contents->SetAudioMuted(true);
+
+  EXPECT_FALSE(web_contents->audio_stream_monitor()->IsCurrentlyAudible());
+  EXPECT_TRUE(web_contents->IsAudioMuted());
+
+  EXPECT_EQ(0, audible_metrics()->GetAudibleWebContentsSizeForTest());
+  media_observer.MaybeUpdateAudibleState();
+  EXPECT_EQ(0, audible_metrics()->GetAudibleWebContentsSizeForTest());
+}
+
+TEST_F(AudibleMetricsTest, MediaWebContentsObserver_NotAudible_NotMuted) {
+  std::unique_ptr<WebContentsImpl> web_contents(CreateWebContents());
+  MediaWebContentsObserver media_observer(web_contents.get());
+  media_observer.SetAudibleMetricsForTest(audible_metrics());
+
+  EXPECT_FALSE(web_contents->audio_stream_monitor()->IsCurrentlyAudible());
+  EXPECT_FALSE(web_contents->IsAudioMuted());
+
+  EXPECT_EQ(0, audible_metrics()->GetAudibleWebContentsSizeForTest());
+  media_observer.MaybeUpdateAudibleState();
+  EXPECT_EQ(0, audible_metrics()->GetAudibleWebContentsSizeForTest());
+}
+
 }  // namespace content
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index e636fca..a1e18f13 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -58,12 +58,13 @@
 
 MediaWebContentsObserver::MediaWebContentsObserver(WebContents* web_contents)
     : WebContentsObserver(web_contents),
+      audible_metrics_(GetAudibleMetrics()),
       session_controllers_manager_(this) {}
 
 MediaWebContentsObserver::~MediaWebContentsObserver() = default;
 
 void MediaWebContentsObserver::WebContentsDestroyed() {
-  GetAudibleMetrics()->UpdateAudibleWebContentsState(web_contents(), false);
+  audible_metrics_->UpdateAudibleWebContentsState(web_contents(), false);
 }
 
 void MediaWebContentsObserver::RenderFrameDeleted(
@@ -92,8 +93,9 @@
   else
     CancelAudioLock();
 
-  GetAudibleMetrics()->UpdateAudibleWebContentsState(
-      web_contents(), audio_stream_monitor->IsCurrentlyAudible());
+  audible_metrics_->UpdateAudibleWebContentsState(
+      web_contents(), audio_stream_monitor->IsCurrentlyAudible() &&
+                          !web_contents()->IsAudioMuted());
 }
 
 bool MediaWebContentsObserver::HasActiveEffectivelyFullscreenVideo() const {
diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h
index 5994a56..70a646e 100644
--- a/content/browser/media/media_web_contents_observer.h
+++ b/content/browser/media/media_web_contents_observer.h
@@ -40,6 +40,8 @@
 
 namespace content {
 
+class AudibleMetrics;
+
 // This class manages all RenderFrame based media related managers at the
 // browser side. It receives IPC messages from media RenderFrameObservers and
 // forwards them to the corresponding managers. The managers are responsible
@@ -106,6 +108,10 @@
 
   bool has_video_wake_lock_for_testing() const { return has_video_wake_lock_; }
 
+  void SetAudibleMetricsForTest(AudibleMetrics* audible_metrics) {
+    audible_metrics_ = audible_metrics;
+  }
+
  protected:
   MediaSessionControllersManager* session_controllers_manager() {
     return &session_controllers_manager_;
@@ -181,6 +187,9 @@
   // Convenience method that casts web_contents() to a WebContentsImpl*.
   WebContentsImpl* web_contents_impl() const;
 
+  // Helper class for recording audible metrics.
+  AudibleMetrics* audible_metrics_;
+
   // Tracking variables and associated wake locks for media playback.
   ActiveMediaPlayerMap active_audio_players_;
   ActiveMediaPlayerMap active_video_players_;
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index af197ce..41b32e8 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -10,6 +10,7 @@
 #include "base/numerics/ranges.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
+#include "build/build_config.h"
 #include "content/browser/media/session/audio_focus_delegate.h"
 #include "content/browser/media/session/media_session_controller.h"
 #include "content/browser/media/session/media_session_player_observer.h"
@@ -177,6 +178,17 @@
     services_[rfh]->DidFinishNavigation();
 }
 
+void MediaSessionImpl::OnWebContentsFocused(
+    RenderWidgetHost* render_widget_host) {
+#if !defined(OS_ANDROID)
+  // If we have just gained focus and we have audio focus we should re-request
+  // system audio focus. This will ensure this media session is towards the top
+  // of the stack if we have multiple sessions active at the same time.
+  if (audio_focus_state_ == State::ACTIVE)
+    RequestSystemAudioFocus(desired_audio_focus_type_);
+#endif
+}
+
 void MediaSessionImpl::AddObserver(MediaSessionObserver* observer) {
   observers_.AddObserver(observer);
   NotifyAddedObserver(observer);
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index f649b57..a30cc23 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -134,6 +134,7 @@
   void WebContentsDestroyed() override;
   void RenderFrameDeleted(RenderFrameHost* rfh) override;
   void DidFinishNavigation(NavigationHandle* navigation_handle) override;
+  void OnWebContentsFocused(RenderWidgetHost* render_widget_host) override;
 
   // MediaSessionService-related methods
 
diff --git a/content/browser/media/session/media_session_impl_unittest.cc b/content/browser/media/session/media_session_impl_unittest.cc
index 0ae5db3b..5dfb9f2 100644
--- a/content/browser/media/session/media_session_impl_unittest.cc
+++ b/content/browser/media/session/media_session_impl_unittest.cc
@@ -43,9 +43,12 @@
   ~MockAudioFocusDelegate() override = default;
 
   void AbandonAudioFocus() override {}
+
   AudioFocusResult RequestAudioFocus(AudioFocusType type) override {
+    request_audio_focus_count_++;
     return AudioFocusResult::kSuccess;
   }
+
   base::Optional<AudioFocusType> GetCurrentFocusType() const override {
     return AudioFocusType::kGain;
   }
@@ -59,7 +62,11 @@
     return session_info_->state;
   }
 
+  int request_audio_focus_count() const { return request_audio_focus_count_; }
+
  private:
+  int request_audio_focus_count_ = 0;
+
   MediaSessionInfoPtr session_info_;
 
   DISALLOW_COPY_AND_ASSIGN(MockAudioFocusDelegate);
@@ -409,6 +416,60 @@
   }
 }
 
+TEST_F(MediaSessionImplTest, RequestAudioFocus_OnFocus_Active) {
+  std::unique_ptr<WebContents> web_contents(CreateWebContents());
+  MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get());
+  MockAudioFocusDelegate* delegate = new MockAudioFocusDelegate();
+  SetDelegateForTests(media_session, delegate);
+
+  {
+    MockMediaSessionMojoObserver observer(*media_session);
+    RequestAudioFocus(media_session, AudioFocusType::kGain);
+    FlushForTesting(media_session);
+    observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+  }
+
+  EXPECT_EQ(1, delegate->request_audio_focus_count());
+  media_session->OnWebContentsFocused(nullptr);
+  EXPECT_EQ(2, delegate->request_audio_focus_count());
+}
+
+TEST_F(MediaSessionImplTest, RequestAudioFocus_OnFocus_Inactive) {
+  std::unique_ptr<WebContents> web_contents(CreateWebContents());
+  MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get());
+  MockAudioFocusDelegate* delegate = new MockAudioFocusDelegate();
+  SetDelegateForTests(media_session, delegate);
+  EXPECT_EQ(MediaSessionInfo::SessionState::kInactive, GetState(media_session));
+
+  EXPECT_EQ(0, delegate->request_audio_focus_count());
+  media_session->OnWebContentsFocused(nullptr);
+  EXPECT_EQ(0, delegate->request_audio_focus_count());
+}
+
+TEST_F(MediaSessionImplTest, RequestAudioFocus_OnFocus_Suspended) {
+  std::unique_ptr<WebContents> web_contents(CreateWebContents());
+  MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get());
+  MockAudioFocusDelegate* delegate = new MockAudioFocusDelegate();
+  SetDelegateForTests(media_session, delegate);
+
+  {
+    MockMediaSessionMojoObserver observer(*media_session);
+    RequestAudioFocus(media_session, AudioFocusType::kGain);
+    FlushForTesting(media_session);
+    observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+  }
+
+  {
+    MockMediaSessionMojoObserver observer(*media_session);
+    media_session->Suspend(MediaSession::SuspendType::kSystem);
+    observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+  }
+
+  EXPECT_EQ(1, delegate->request_audio_focus_count());
+  media_session->OnWebContentsFocused(nullptr);
+  EXPECT_EQ(1, delegate->request_audio_focus_count());
+}
+  
 #endif  // !defined(OS_ANDROID)
 
 }  // namespace content
diff --git a/content/browser/quota_dispatcher_host.cc b/content/browser/quota_dispatcher_host.cc
index 350bdf3b..f1626f3 100644
--- a/content/browser/quota_dispatcher_host.cc
+++ b/content/browser/quota_dispatcher_host.cc
@@ -94,7 +94,7 @@
     const url::Origin& origin,
     StorageType storage_type,
     QueryStorageUsageAndQuotaCallback callback) {
-  quota_manager_->GetUsageAndQuotaForWebApps(
+  quota_manager_->GetUsageAndQuotaWithBreakdown(
       origin, storage_type,
       base::BindOnce(&QuotaDispatcherHost::DidQueryStorageUsageAndQuota,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
@@ -140,11 +140,12 @@
 }
 
 void QuotaDispatcherHost::DidQueryStorageUsageAndQuota(
-    RequestStorageQuotaCallback callback,
+    QueryStorageUsageAndQuotaCallback callback,
     blink::mojom::QuotaStatusCode status,
     int64_t usage,
-    int64_t quota) {
-  std::move(callback).Run(status, usage, quota);
+    int64_t quota,
+    blink::mojom::UsageBreakdownPtr usage_breakdown) {
+  std::move(callback).Run(status, usage, quota, std::move(usage_breakdown));
 }
 
 void QuotaDispatcherHost::DidGetPersistentUsageAndQuota(
diff --git a/content/browser/quota_dispatcher_host.h b/content/browser/quota_dispatcher_host.h
index e42257d5..8029c9d 100644
--- a/content/browser/quota_dispatcher_host.h
+++ b/content/browser/quota_dispatcher_host.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "content/public/browser/quota_permission_context.h"
+#include "storage/browser/quota/quota_manager.h"
 #include "third_party/blink/public/mojom/quota/quota_dispatcher_host.mojom.h"
 
 namespace storage {
@@ -49,10 +50,11 @@
                            RequestStorageQuotaCallback callback) override;
 
  private:
-  void DidQueryStorageUsageAndQuota(RequestStorageQuotaCallback callback,
+  void DidQueryStorageUsageAndQuota(QueryStorageUsageAndQuotaCallback callback,
                                     blink::mojom::QuotaStatusCode status,
                                     int64_t usage,
-                                    int64_t quota);
+                                    int64_t quota,
+                                    blink::mojom::UsageBreakdownPtr);
   void DidGetPersistentUsageAndQuota(const url::Origin& origin,
                                      blink::mojom::StorageType storage_type,
                                      uint64_t requested_quota,
diff --git a/content/browser/sandbox_mac_unittest.mm b/content/browser/sandbox_mac_unittest.mm
index 7ca9ec0..215964d 100644
--- a/content/browser/sandbox_mac_unittest.mm
+++ b/content/browser/sandbox_mac_unittest.mm
@@ -16,6 +16,7 @@
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_timeouts.h"
 #include "content/browser/sandbox_parameters_mac.h"
+#include "crypto/openssl_util.h"
 #include "sandbox/mac/seatbelt.h"
 #include "sandbox/mac/seatbelt_exec.h"
 #include "services/service_manager/sandbox/mac/audio.sb.h"
@@ -30,6 +31,7 @@
 #include "services/service_manager/sandbox/switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
+#include "third_party/boringssl/src/include/openssl/rand.h"
 #import "ui/base/clipboard/clipboard_util_mac.h"
 
 namespace content {
@@ -141,7 +143,9 @@
 
     for (ExecuteFuncT execute_func : kExecuteFuncs) {
       (this->*execute_func)(multiprocess_main);
-      after_each.Run();
+      if (!after_each.is_null()) {
+        after_each.Run();
+      }
     }
   }
 
@@ -222,4 +226,18 @@
                                pb));
 }
 
+MULTIPROCESS_TEST_MAIN(SSLProcess) {
+  CheckCreateSeatbeltServer();
+
+  crypto::EnsureOpenSSLInit();
+  // Ensure that RAND_bytes is functional within the sandbox.
+  uint8_t byte;
+  CHECK(RAND_bytes(&byte, 1) == 1);
+  return 0;
+}
+
+TEST_F(SandboxMacTest, SSLInitTest) {
+  ExecuteInAllSandboxTypes("SSLProcess", base::RepeatingClosure());
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc
index e7b2eda..840f2b8 100644
--- a/content/browser/service_worker/service_worker_navigation_loader.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -580,9 +580,9 @@
           response_head_.load_timing.request_start);
 
   // Time spent for service worker startup.
-  UMA_HISTOGRAM_TIMES(
+  UMA_HISTOGRAM_MEDIUM_TIMES(
       "ServiceWorker.LoadTiming.MainFrame.MainResource."
-      "ForwardServiceWorkerToWorkerReady",
+      "ForwardServiceWorkerToWorkerReady2",
       response_head_.service_worker_ready_time -
           response_head_.service_worker_start_time);
 
diff --git a/content/browser/service_worker/service_worker_tls_browsertest.cc b/content/browser/service_worker/service_worker_tls_browsertest.cc
index 42e9efd..79a6f995 100644
--- a/content/browser/service_worker/service_worker_tls_browsertest.cc
+++ b/content/browser/service_worker/service_worker_tls_browsertest.cc
@@ -24,7 +24,7 @@
 }
 
 // Tests TLS + service workers. Inspired by
-// content/browser/shared_worker/worker_browsertest.cc.
+// content/browser/worker_host/worker_browsertest.cc.
 class ServiceWorkerTlsTest
     : public ContentBrowserTest,
       public ::testing::WithParamInterface<ServicifiedFeatures> {
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index b485cde..c9fd76a 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -28,8 +28,8 @@
 #include "content/browser/payments/payment_app_context_impl.h"
 #include "content/browser/push_messaging/push_messaging_context.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
 #include "content/browser/url_loader_factory_getter.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/storage_partition.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
diff --git a/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc b/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc
index 41cfa75..d99caa35 100644
--- a/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc
+++ b/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc
@@ -18,7 +18,6 @@
 
 // See content/testdata/sxg/README on how to generate these data.
 // clang-format off
-constexpr char kSignatureHeaderRSA[] = R"(label; sig=*DDeXzJshGnPT+ei1rS1KmZx+QLwwLTbNKDVSmTb2HjGfgPngv+C+uMbjZiliOmGe0b514JcAlYAM57t0kZY2FPd9JdqwYPIiAWEwxByfV2iXBbsGZNWGtS/AAq1SaPwIMfrzdLXAFbKbtTRhS7B5LHCo/6hEIXu0TJJFbv5fKaLgTTLF0AK5dV0/En0uz+bnVARuBIH/ez2gPEFc6KbGnTTp8LYcCe/YjlHQy/Oac28ACBtn70rP1TerWEaYBwMMDckJ2gfsVyLqMcFtJqV0uGLT6Atb2wBSUZlZDTEZf228362r+EHLrADAuhz4bdSMKFsFgWyceOriDyHhc0PSwQ==*; validity-url="https://example.com/resource.validity.msg"; integrity="digest/mi-sha256-03"; cert-url="https://example.com/cert.msg"; cert-sha256=*tJGJP8ej7KCEW8VnVK3bKwpBza/oLrtWA75z5ZPptuc=*; date=1517892341; expires=1517895941)";
 constexpr char kSignatureHeaderECDSAP256[] = R"(label; sig=*MEUCIQC7tM/B6YxVgrJmgfFawtwBKPev2vFCh7amR+JTDBMgTQIga9LkS51vteYr8NWPTCSZRy10lcLaFNN9m1G3OBS9lBs=*; validity-url="https://example.com/resource.validity.msg"; integrity="digest/mi-sha256-03"; cert-url="https://example.com/cert.msg"; cert-sha256=*KX+BYLSMgDOON8Ju65RoId39Qvajxa12HO+WnD4HpS0=*; date=1517892341; expires=1517895941)";
 constexpr uint8_t kCborHeadersECDSAP256[] = {
   0x82, 0xa1, 0x47, 0x3a, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x43, 0x47,
@@ -56,31 +55,6 @@
     "date=1517892341; expires=1518497142";
 // clang-format on
 
-constexpr char kCertPEMRSA[] = R"(
------BEGIN CERTIFICATE-----
-MIIDyTCCArGgAwIBAgIBBDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
-MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
-A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE3MDYwNTE3
-MTA0NloXDTI3MDYwMzE3MTA0NlowYDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh
-bGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3Qg
-Q0ExEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
-AQoCggEBANOUHzO0uxUyd3rYUArq33olXC0N1AYNM0wFTjUqUrElLiX48+5hERkG
-hGwC8VG5Zr/2Jw/wtarLiDjg2OfPdwyMp3S7MBTgvXWZ989MUHpx6b0cWM298iOg
-/VeinMphFLDfPDHFWZ7RXBqfk6MGLhI5GgvoooYw2jUmP+elnoizIL/OB08sIYra
-AVrwasoRd+yOmyvQnzw3mZNKpWjeX7NhZCg2nG8B8u78agwAYVWupHnJS2GwhLzy
-19AxU/HmaI9kyyMGmRtbRZ0roCyMDOgEEcWUSYNRP33KLi31uKYqOSblvzmC7kA7
-k5yca3VXlgqg4gnjr9tbOMzMcpeqeaMCAwEAAaOBijCBhzAMBgNVHRMBAf8EAjAA
-MB0GA1UdDgQWBBQYDOtRudM2qckEr/kvFPCZZtJ21DAfBgNVHSMEGDAWgBSbJguK
-mKm7HbkfHOMaQDPtjheIqzAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
-GAYDVR0RBBEwD4INKi5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEAvXK0
-UF19i7JkSSdomQwB18WRFaKG8VZpSFsKbEECPRHoxktMl/Pd04wk+W0fZFq433j3
-4D+cjTB6OxAVdPIPSex8U40fYMl9C13K1tejf4o/+rcLxEDdVfv7PUkogrliXzSE
-MCYdcTwruV7hjC2/Ib0t/kdxblRt4dD2I1jdntsFy/VfET/m0J2qRhJWlfYEzCFe
-Hn8H/PZIiIsso5pm2RodTqi9w4/+1r8Yyfmk8TF+EoWDYtbZ+ScgtCH5fldS+onI
-hHgjz/tniqjbY0MRFr9ZxrohmtgOBOvROEKH06c92oOmj2ahyFpM/yU9PL/JvNmF
-SaMW1eOzjHemIWKTMw==
------END CERTIFICATE-----)";
-
 constexpr char kCertPEMECDSAP256[] = R"(
 -----BEGIN CERTIFICATE-----
 MIIC1TCCAb2gAwIBAgIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
@@ -249,34 +223,6 @@
   }
 };
 
-TEST_F(SignedExchangeSignatureVerifierTest, VerifyRSA) {
-  auto signature = SignedExchangeSignatureHeaderField::ParseSignature(
-      kSignatureHeaderRSA, nullptr /* devtools_proxy */);
-  ASSERT_TRUE(signature.has_value());
-  ASSERT_EQ(1u, signature->size());
-
-  net::CertificateList certlist =
-      net::X509Certificate::CreateCertificateListFromBytes(
-          kCertPEMRSA, base::size(kCertPEMRSA),
-          net::X509Certificate::FORMAT_AUTO);
-  ASSERT_EQ(1u, certlist.size());
-
-  SignedExchangeEnvelope envelope;
-  envelope.set_request_method("GET");
-  envelope.set_request_url(GURL("https://test.example.org/test/"));
-  envelope.set_response_code(net::HTTP_OK);
-  envelope.AddResponseHeader("content-type", "text/html; charset=utf-8");
-  envelope.AddResponseHeader("content-encoding", "mi-sha256-03");
-  envelope.AddResponseHeader(
-      "digest", "mi-sha256-03=wmp4dRMYgxP3tSMCwV/I0CWOCiHZpAihKZk19bsN9RI=");
-  envelope.SetSignatureForTesting((*signature)[0]);
-
-  EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kErrUnsupportedCertType,
-            SignedExchangeSignatureVerifier::Verify(
-                envelope, certlist[0], VerificationTime(),
-                nullptr /* devtools_proxy */));
-}
-
 TEST_F(SignedExchangeSignatureVerifierTest, VerifyECDSAP256) {
   auto signature = SignedExchangeSignatureHeaderField::ParseSignature(
       kSignatureHeaderECDSAP256, nullptr /* devtools_proxy */);
diff --git a/content/browser/shared_worker/DEPS b/content/browser/worker_host/DEPS
similarity index 100%
rename from content/browser/shared_worker/DEPS
rename to content/browser/worker_host/DEPS
diff --git a/content/browser/shared_worker/OWNERS b/content/browser/worker_host/OWNERS
similarity index 100%
rename from content/browser/shared_worker/OWNERS
rename to content/browser/worker_host/OWNERS
diff --git a/content/browser/worker_host/README.md b/content/browser/worker_host/README.md
new file mode 100644
index 0000000..878337f
--- /dev/null
+++ b/content/browser/worker_host/README.md
@@ -0,0 +1,8 @@
+# Web Worker Host
+
+`content/browser/worker_host` implements the host side of web workers (dedicated
+workers and shared workers). It tracks the security principal of the worker in
+the renderer and uses it to broker access to mojo interfaces providing powerful
+web APIs. See: [Design doc].
+
+[Design doc]: https://docs.google.com/document/d/1Bg84lQqeJ8D2J-_wOOLlRVAtZNMstEUHmVhJqCxpjUk/edit?usp=sharing
diff --git a/content/browser/dedicated_worker/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
similarity index 98%
rename from content/browser/dedicated_worker/dedicated_worker_host.cc
rename to content/browser/worker_host/dedicated_worker_host.cc
index 9c9271b..9eb741e 100644
--- a/content/browser/dedicated_worker/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -5,7 +5,7 @@
 #include <string>
 #include <utility>
 
-#include "content/browser/dedicated_worker/dedicated_worker_host.h"
+#include "content/browser/worker_host/dedicated_worker_host.h"
 
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/interface_provider_filtering.h"
diff --git a/content/browser/dedicated_worker/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
similarity index 71%
rename from content/browser/dedicated_worker/dedicated_worker_host.h
rename to content/browser/worker_host/dedicated_worker_host.h
index 587ab20..bde3fe59 100644
--- a/content/browser/dedicated_worker/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_DEDICATED_WORKER_DEDICATED_WORKER_HOST_H_
-#define CONTENT_BROWSER_DEDICATED_WORKER_DEDICATED_WORKER_HOST_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_
+#define CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_
 
 #include "third_party/blink/public/platform/dedicated_worker_factory.mojom.h"
 
@@ -21,4 +21,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_DEDICATED_WORKER_DEDICATED_WORKER_HOST_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_DEDICATED_WORKER_HOST_H_
diff --git a/content/browser/shared_worker/mock_shared_worker.cc b/content/browser/worker_host/mock_shared_worker.cc
similarity index 98%
rename from content/browser/shared_worker/mock_shared_worker.cc
rename to content/browser/worker_host/mock_shared_worker.cc
index 826bcb4..4a1a4ae2 100644
--- a/content/browser/shared_worker/mock_shared_worker.cc
+++ b/content/browser/worker_host/mock_shared_worker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/mock_shared_worker.h"
+#include "content/browser/worker_host/mock_shared_worker.h"
 
 #include "content/common/url_loader_factory_bundle.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
diff --git a/content/browser/shared_worker/mock_shared_worker.h b/content/browser/worker_host/mock_shared_worker.h
similarity index 94%
rename from content/browser/shared_worker/mock_shared_worker.h
rename to content/browser/worker_host/mock_shared_worker.h
index 21f76e8..51af99a 100644
--- a/content/browser/shared_worker/mock_shared_worker.h
+++ b/content/browser/worker_host/mock_shared_worker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_MOCK_SHARED_WORKER_H_
-#define CONTENT_BROWSER_SHARED_WORKER_MOCK_SHARED_WORKER_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_MOCK_SHARED_WORKER_H_
+#define CONTENT_BROWSER_WORKER_HOST_MOCK_SHARED_WORKER_H_
 
 #include <memory>
 #include <queue>
@@ -13,7 +13,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "content/browser/shared_worker/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_host.h"
 #include "content/common/service_worker/service_worker_provider.mojom.h"
 #include "content/common/shared_worker/shared_worker_factory.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -137,4 +137,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_MOCK_SHARED_WORKER_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_MOCK_SHARED_WORKER_H_
diff --git a/content/browser/shared_worker/shared_worker_connector_impl.cc b/content/browser/worker_host/shared_worker_connector_impl.cc
similarity index 94%
rename from content/browser/shared_worker/shared_worker_connector_impl.cc
rename to content/browser/worker_host/shared_worker_connector_impl.cc
index 454feba..69e9a94 100644
--- a/content/browser/shared_worker/shared_worker_connector_impl.cc
+++ b/content/browser/worker_host/shared_worker_connector_impl.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/shared_worker_connector_impl.h"
+#include "content/browser/worker_host/shared_worker_connector_impl.h"
 
 #include "base/memory/ptr_util.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
 #include "content/browser/storage_partition_impl.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
diff --git a/content/browser/shared_worker/shared_worker_connector_impl.h b/content/browser/worker_host/shared_worker_connector_impl.h
similarity index 83%
rename from content/browser/shared_worker/shared_worker_connector_impl.h
rename to content/browser/worker_host/shared_worker_connector_impl.h
index bd85820d..2ec5150 100644
--- a/content/browser/shared_worker/shared_worker_connector_impl.h
+++ b/content/browser/worker_host/shared_worker_connector_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_CONNECTOR_IMPL_H_
-#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_CONNECTOR_IMPL_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_CONNECTOR_IMPL_H_
+#define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_CONNECTOR_IMPL_H_
 
 #include "content/common/content_export.h"
 #include "content/common/shared_worker/shared_worker_connector.mojom.h"
@@ -36,4 +36,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_CONNECTOR_IMPL_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_CONNECTOR_IMPL_H_
diff --git a/content/browser/shared_worker/shared_worker_content_settings_proxy_impl.cc b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.cc
similarity index 84%
rename from content/browser/shared_worker/shared_worker_content_settings_proxy_impl.cc
rename to content/browser/worker_host/shared_worker_content_settings_proxy_impl.cc
index 952ef9ab..015618e 100644
--- a/content/browser/shared_worker/shared_worker_content_settings_proxy_impl.cc
+++ b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/shared_worker_content_settings_proxy_impl.h"
+#include "content/browser/worker_host/shared_worker_content_settings_proxy_impl.h"
 
 #include <utility>
 
-#include "content/browser/shared_worker/shared_worker_host.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/browser/shared_worker/shared_worker_content_settings_proxy_impl.h b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.h
similarity index 86%
rename from content/browser/shared_worker/shared_worker_content_settings_proxy_impl.h
rename to content/browser/worker_host/shared_worker_content_settings_proxy_impl.h
index 083ddcb..0b68858 100644
--- a/content/browser/shared_worker/shared_worker_content_settings_proxy_impl.h
+++ b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_CONTENT_SETTING_PROXY_IMPL_H_
-#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_CONTENT_SETTING_PROXY_IMPL_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_CONTENT_SETTINGS_PROXY_IMPL_H_
+#define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_CONTENT_SETTINGS_PROXY_IMPL_H_
 
 #include "base/callback.h"
 #include "base/strings/string16.h"
@@ -48,4 +48,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_CONTENT_SETTING_PROXY_IMPL_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_CONTENT_SETTINGS_PROXY_IMPL_H_
diff --git a/content/browser/shared_worker/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
similarity index 98%
rename from content/browser/shared_worker/shared_worker_host.cc
rename to content/browser/worker_host/shared_worker_host.cc
index 3d4a4b5..bef09c17 100644
--- a/content/browser/shared_worker/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_host.h"
 
 #include <utility>
 
@@ -14,10 +14,10 @@
 #include "content/browser/devtools/shared_worker_devtools_manager.h"
 #include "content/browser/interface_provider_filtering.h"
 #include "content/browser/renderer_interface_binders.h"
-#include "content/browser/shared_worker/shared_worker_content_settings_proxy_impl.h"
-#include "content/browser/shared_worker/shared_worker_instance.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
 #include "content/browser/storage_partition_impl.h"
+#include "content/browser/worker_host/shared_worker_content_settings_proxy_impl.h"
+#include "content/browser/worker_host/shared_worker_instance.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/common/navigation_subresource_loader_params.h"
 #include "content/common/url_loader_factory_bundle.h"
 #include "content/public/browser/browser_context.h"
diff --git a/content/browser/shared_worker/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
similarity index 97%
rename from content/browser/shared_worker/shared_worker_host.h
rename to content/browser/worker_host/shared_worker_host.h
index d93e5b4..272ba1b 100644
--- a/content/browser/shared_worker/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_HOST_H_
-#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_HOST_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_HOST_H_
+#define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_HOST_H_
 
 #include <list>
 #include <memory>
@@ -217,4 +217,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_HOST_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_HOST_H_
diff --git a/content/browser/shared_worker/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
similarity index 97%
rename from content/browser/shared_worker/shared_worker_host_unittest.cc
rename to content/browser/worker_host/shared_worker_host_unittest.cc
index 583d83e..6d3eda4a 100644
--- a/content/browser/shared_worker/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_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 "content/browser/shared_worker/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_host.h"
 
 #include <memory>
 #include <string>
@@ -12,10 +12,10 @@
 #include "base/run_loop.h"
 #include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
-#include "content/browser/shared_worker/mock_shared_worker.h"
-#include "content/browser/shared_worker/shared_worker_connector_impl.h"
-#include "content/browser/shared_worker/shared_worker_instance.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/mock_shared_worker.h"
+#include "content/browser/worker_host/shared_worker_connector_impl.h"
+#include "content/browser/worker_host/shared_worker_instance.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/common/navigation_subresource_loader_params.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
diff --git a/content/browser/shared_worker/shared_worker_instance.cc b/content/browser/worker_host/shared_worker_instance.cc
similarity index 96%
rename from content/browser/shared_worker/shared_worker_instance.cc
rename to content/browser/worker_host/shared_worker_instance.cc
index 67e1d88..3c9a91e 100644
--- a/content/browser/shared_worker/shared_worker_instance.cc
+++ b/content/browser/worker_host/shared_worker_instance.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/browser/worker_host/shared_worker_instance.h"
 
 #include "base/logging.h"
 
diff --git a/content/browser/shared_worker/shared_worker_instance.h b/content/browser/worker_host/shared_worker_instance.h
similarity index 93%
rename from content/browser/shared_worker/shared_worker_instance.h
rename to content/browser/worker_host/shared_worker_instance.h
index eb909da..0e03dfbb 100644
--- a/content/browser/shared_worker/shared_worker_instance.h
+++ b/content/browser/worker_host/shared_worker_instance.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_INSTANCE_H_
-#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_INSTANCE_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_INSTANCE_H_
+#define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_INSTANCE_H_
 
 #include <string>
 
@@ -74,5 +74,4 @@
 
 }  // namespace content
 
-
-#endif  // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_INSTANCE_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_INSTANCE_H_
diff --git a/content/browser/shared_worker/shared_worker_instance_unittest.cc b/content/browser/worker_host/shared_worker_instance_unittest.cc
similarity index 99%
rename from content/browser/shared_worker/shared_worker_instance_unittest.cc
rename to content/browser/worker_host/shared_worker_instance_unittest.cc
index 779d67f..ec5b6c05 100644
--- a/content/browser/shared_worker/shared_worker_instance_unittest.cc
+++ b/content/browser/worker_host/shared_worker_instance_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 "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/browser/worker_host/shared_worker_instance.h"
 
 #include <memory>
 
diff --git a/content/browser/shared_worker/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
similarity index 97%
rename from content/browser/shared_worker/shared_worker_service_impl.cc
rename to content/browser/worker_host/shared_worker_service_impl.cc
index 34fe1ac..79464f48 100644
--- a/content/browser/shared_worker/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 
 #include <stddef.h>
 
@@ -19,12 +19,12 @@
 #include "content/browser/appcache/appcache_navigation_handle.h"
 #include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/file_url_loader_factory.h"
-#include "content/browser/shared_worker/shared_worker_host.h"
-#include "content/browser/shared_worker/shared_worker_instance.h"
-#include "content/browser/shared_worker/worker_script_fetch_initiator.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/worker_host/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_instance.h"
+#include "content/browser/worker_host/worker_script_fetch_initiator.h"
 #include "content/common/content_constants_internal.h"
 #include "content/common/navigation_subresource_loader_params.h"
 #include "content/common/service_worker/service_worker_provider.mojom.h"
diff --git a/content/browser/shared_worker/shared_worker_service_impl.h b/content/browser/worker_host/shared_worker_service_impl.h
similarity index 94%
rename from content/browser/shared_worker/shared_worker_service_impl.h
rename to content/browser/worker_host/shared_worker_service_impl.h
index a1e7471..be02df4 100644
--- a/content/browser/shared_worker/shared_worker_service_impl.h
+++ b/content/browser/worker_host/shared_worker_service_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_SERVICE_IMPL_H_
-#define CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_SERVICE_IMPL_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_SERVICE_IMPL_H_
+#define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_SERVICE_IMPL_H_
 
 #include <memory>
 #include <set>
@@ -13,7 +13,7 @@
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/shared_worker/shared_worker_host.h"
+#include "content/browser/worker_host/shared_worker_host.h"
 #include "content/common/service_worker/service_worker_provider.mojom.h"
 #include "content/common/shared_worker/shared_worker_connector.mojom.h"
 #include "content/common/shared_worker/shared_worker_factory.mojom.h"
@@ -131,4 +131,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_SHARED_WORKER_SERVICE_IMPL_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_SERVICE_IMPL_H_
diff --git a/content/browser/shared_worker/shared_worker_service_impl_unittest.cc b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
similarity index 99%
rename from content/browser/shared_worker/shared_worker_service_impl_unittest.cc
rename to content/browser/worker_host/shared_worker_service_impl_unittest.cc
index 49aa62c3..7279216 100644
--- a/content/browser/shared_worker/shared_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 
 #include <memory>
 #include <queue>
@@ -11,9 +11,9 @@
 
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "content/browser/shared_worker/mock_shared_worker.h"
-#include "content/browser/shared_worker/shared_worker_connector_impl.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/browser/worker_host/mock_shared_worker.h"
+#include "content/browser/worker_host/shared_worker_connector_impl.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_utils.h"
diff --git a/content/browser/shared_worker/worker_browsertest.cc b/content/browser/worker_host/worker_browsertest.cc
similarity index 98%
rename from content/browser/shared_worker/worker_browsertest.cc
rename to content/browser/worker_host/worker_browsertest.cc
index 1735688f..fa0792e 100644
--- a/content/browser/shared_worker/worker_browsertest.cc
+++ b/content/browser/worker_host/worker_browsertest.cc
@@ -244,8 +244,8 @@
   // Generate test URL.
   GURL::Replacements replacements;
   replacements.SetSchemeStr("http");
-  GURL url = ws_server.GetURL(
-      "websocket_shared_worker.html").ReplaceComponents(replacements);
+  GURL url = ws_server.GetURL("websocket_shared_worker.html")
+                 .ReplaceComponents(replacements);
 
   // Run test.
   Shell* window = shell();
diff --git a/content/browser/shared_worker/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
similarity index 97%
rename from content/browser/shared_worker/worker_script_fetch_initiator.cc
rename to content/browser/worker_host/worker_script_fetch_initiator.cc
index 69aa6a20..cf47bcf 100644
--- a/content/browser/shared_worker/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/worker_script_fetch_initiator.h"
+#include "content/browser/worker_host/worker_script_fetch_initiator.h"
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -15,12 +15,12 @@
 #include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/file_url_loader_factory.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/shared_worker/worker_script_fetcher.h"
-#include "content/browser/shared_worker/worker_script_loader.h"
-#include "content/browser/shared_worker/worker_script_loader_factory.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/worker_host/worker_script_fetcher.h"
+#include "content/browser/worker_host/worker_script_loader.h"
+#include "content/browser/worker_host/worker_script_loader_factory.h"
 #include "content/common/content_constants_internal.h"
 #include "content/common/navigation_subresource_loader_params.h"
 #include "content/common/service_worker/service_worker_provider.mojom.h"
diff --git a/content/browser/shared_worker/worker_script_fetch_initiator.h b/content/browser/worker_host/worker_script_fetch_initiator.h
similarity index 94%
rename from content/browser/shared_worker/worker_script_fetch_initiator.h
rename to content/browser/worker_host/worker_script_fetch_initiator.h
index 304cd56..5d232b2 100644
--- a/content/browser/shared_worker/worker_script_fetch_initiator.h
+++ b/content/browser/worker_host/worker_script_fetch_initiator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_FETCH_INITIATOR_H_
-#define CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_FETCH_INITIATOR_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_FETCH_INITIATOR_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_FETCH_INITIATOR_H_
 
 #include <memory>
 #include <set>
@@ -93,4 +93,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_FETCH_INITIATOR_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_FETCH_INITIATOR_H_
diff --git a/content/browser/shared_worker/worker_script_fetcher.cc b/content/browser/worker_host/worker_script_fetcher.cc
similarity index 97%
rename from content/browser/shared_worker/worker_script_fetcher.cc
rename to content/browser/worker_host/worker_script_fetcher.cc
index 74e66ef5..1c686ee 100644
--- a/content/browser/shared_worker/worker_script_fetcher.cc
+++ b/content/browser/worker_host/worker_script_fetcher.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/worker_script_fetcher.h"
+#include "content/browser/worker_host/worker_script_fetcher.h"
 
 #include "base/feature_list.h"
-#include "content/browser/shared_worker/worker_script_loader.h"
-#include "content/browser/shared_worker/worker_script_loader_factory.h"
+#include "content/browser/worker_host/worker_script_loader.h"
+#include "content/browser/worker_host/worker_script_loader_factory.h"
 #include "content/common/throttling_url_loader.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/url_loader_throttle.h"
diff --git a/content/browser/shared_worker/worker_script_fetcher.h b/content/browser/worker_host/worker_script_fetcher.h
similarity index 95%
rename from content/browser/shared_worker/worker_script_fetcher.h
rename to content/browser/worker_host/worker_script_fetcher.h
index ba14ffe..4021f50 100644
--- a/content/browser/shared_worker/worker_script_fetcher.h
+++ b/content/browser/worker_host/worker_script_fetcher.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_FETCHER_H_
-#define CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_FETCHER_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_FETCHER_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_FETCHER_H_
 
 #include "base/callback.h"
 #include "base/optional.h"
@@ -94,4 +94,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_FETCHER_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_FETCHER_H_
diff --git a/content/browser/shared_worker/worker_script_loader.cc b/content/browser/worker_host/worker_script_loader.cc
similarity index 99%
rename from content/browser/shared_worker/worker_script_loader.cc
rename to content/browser/worker_host/worker_script_loader.cc
index 96e2bd74..60e2a6b 100644
--- a/content/browser/shared_worker/worker_script_loader.cc
+++ b/content/browser/worker_host/worker_script_loader.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/shared_worker/worker_script_loader.h"
+#include "content/browser/worker_host/worker_script_loader.h"
 
 #include "content/browser/appcache/appcache_request_handler.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
diff --git a/content/browser/shared_worker/worker_script_loader.h b/content/browser/worker_host/worker_script_loader.h
similarity index 96%
rename from content/browser/shared_worker/worker_script_loader.h
rename to content/browser/worker_host/worker_script_loader.h
index 36ca6fb..a390e6d 100644
--- a/content/browser/shared_worker/worker_script_loader.h
+++ b/content/browser/worker_host/worker_script_loader.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_LOADER_H_
-#define CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_LOADER_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_LOADER_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_LOADER_H_
 
 #include "base/macros.h"
 #include "content/common/navigation_subresource_loader_params.h"
@@ -145,4 +145,4 @@
 };
 
 }  // namespace content
-#endif  // CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_LOADER_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_LOADER_H_
diff --git a/content/browser/shared_worker/worker_script_loader_factory.cc b/content/browser/worker_host/worker_script_loader_factory.cc
similarity index 96%
rename from content/browser/shared_worker/worker_script_loader_factory.cc
rename to content/browser/worker_host/worker_script_loader_factory.cc
index b86fa924..729be651 100644
--- a/content/browser/shared_worker/worker_script_loader_factory.cc
+++ b/content/browser/worker_host/worker_script_loader_factory.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 "content/browser/shared_worker/worker_script_loader_factory.h"
+#include "content/browser/worker_host/worker_script_loader_factory.h"
 
 #include <memory>
 #include "base/feature_list.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "content/browser/shared_worker/worker_script_loader.h"
+#include "content/browser/worker_host/worker_script_loader.h"
 #include "content/public/browser/browser_thread.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/network/public/cpp/features.h"
diff --git a/content/browser/shared_worker/worker_script_loader_factory.h b/content/browser/worker_host/worker_script_loader_factory.h
similarity index 93%
rename from content/browser/shared_worker/worker_script_loader_factory.h
rename to content/browser/worker_host/worker_script_loader_factory.h
index 9ce2be1..b15edbd 100644
--- a/content/browser/shared_worker/worker_script_loader_factory.h
+++ b/content/browser/worker_host/worker_script_loader_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_LOADER_FACTORY_H_
-#define CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_LOADER_FACTORY_H_
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_LOADER_FACTORY_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_LOADER_FACTORY_H_
 
 #include "base/macros.h"
 #include "content/common/navigation_subresource_loader_params.h"
@@ -74,4 +74,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SHARED_WORKER_WORKER_SCRIPT_LOADER_FACTORY_H_
+#endif  // CONTENT_BROWSER_WORKER_HOST_WORKER_SCRIPT_LOADER_FACTORY_H_
diff --git a/content/common/sandbox_mac_system_access_unittest.mm b/content/common/sandbox_mac_system_access_unittest.mm
deleted file mode 100644
index 9a4d64f..0000000
--- a/content/common/sandbox_mac_system_access_unittest.mm
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2012 The Chromium 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 <Cocoa/Cocoa.h>
-#include <stdint.h>
-
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/strings/sys_string_conversions.h"
-#include "content/common/sandbox_mac_unittest_helper.h"
-#include "crypto/openssl_util.h"
-#include "services/service_manager/sandbox/mac/sandbox_mac.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/boringssl/src/include/openssl/rand.h"
-#import "ui/base/clipboard/clipboard_util_mac.h"
-
-namespace content {
-
-//--------------------- OpenSSL Sandboxing ----------------------
-// Test case for checking sandboxing of OpenSSL initialization.
-class MacSandboxedOpenSSLTestCase : public MacSandboxTestCase {
- public:
-  bool SandboxedTest() override;
-};
-
-REGISTER_SANDBOX_TEST_CASE(MacSandboxedOpenSSLTestCase);
-
-bool MacSandboxedOpenSSLTestCase::SandboxedTest() {
-  crypto::EnsureOpenSSLInit();
-
-  // Ensure that RAND_bytes is functional within the sandbox.
-  uint8_t byte;
-  return RAND_bytes(&byte, 1) == 1;
-}
-
-TEST_F(MacSandboxTest, OpenSSLAccess) {
-  EXPECT_TRUE(RunTestInAllSandboxTypes("MacSandboxedOpenSSLTestCase", NULL));
-}
-
-}  // namespace content
diff --git a/content/common/url_schemes.cc b/content/common/url_schemes.cc
index c922d97..e532406 100644
--- a/content/common/url_schemes.cc
+++ b/content/common/url_schemes.cc
@@ -10,6 +10,7 @@
 
 #include "base/no_destructor.h"
 #include "base/strings/string_util.h"
+#include "build/build_config.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/url_constants.h"
 #include "url/url_util.h"
@@ -89,6 +90,11 @@
   for (auto& scheme : schemes.empty_document_schemes)
     url::AddEmptyDocumentScheme(scheme.c_str());
 
+#if defined(OS_ANDROID)
+  if (schemes.allow_non_standard_schemes_in_origins)
+    url::EnableNonStandardSchemesForAndroidWebView();
+#endif
+
   // Prevent future modification of the scheme lists. This is to prevent
   // accidental creation of data races in the program. Add*Scheme aren't
   // threadsafe so must be called when GURL isn't used on any other thread. This
diff --git a/content/public/common/content_client.h b/content/public/common/content_client.h
index b939bc2a..618a328 100644
--- a/content/public/common/content_client.h
+++ b/content/public/common/content_client.h
@@ -139,6 +139,11 @@
     // Registers a URL scheme as strictly empty documents, allowing them to
     // commit synchronously.
     std::vector<std::string> empty_document_schemes;
+#if defined(OS_ANDROID)
+    // Normally, non-standard schemes canonicalize to opaque origins. However,
+    // Android WebView requires non-standard schemes to still be preserved.
+    bool allow_non_standard_schemes_in_origins = false;
+#endif
   };
 
   virtual void AddAdditionalSchemes(Schemes* schemes) {}
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 77df96a..5af168d 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -255,6 +255,17 @@
   // Returns the current visibility of the frame.
   virtual blink::mojom::PageVisibilityState GetVisibilityState() const = 0;
 
+  // Loads specified |html| to this frame. |base_url| is used to resolve
+  // relative urls in the document.
+  // |replace_current_item| should be true if we load html instead of the
+  // existing page. In this case |unreachable_url| might be the original url
+  // which did fail loading.
+  virtual void LoadHTMLString(const std::string& html,
+                              const GURL& base_url,
+                              const std::string& text_encoding,
+                              const GURL& unreachable_url,
+                              bool replace_current_item) = 0;
+
   // If PlzNavigate is enabled, returns true in between teh time that Blink
   // requests navigation until the browser responds with the result.
   virtual bool IsBrowserSideNavigationPending() = 0;
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 9c37de4c..3d29793 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -221,8 +221,9 @@
 void RenderViewTest::LoadHTML(const char* html) {
   std::string url_string = "data:text/html;charset=utf-8,";
   url_string.append(net::EscapeQueryParamValue(html, false));
-  GetMainFrame()->LoadHTMLString(std::string(html),
-                                 blink::WebURL(GURL(url_string)));
+  RenderFrame::FromWebFrame(GetMainFrame())
+      ->LoadHTMLString(html, GURL(url_string), "UTF-8", GURL(),
+                       false /* replace_current_item */);
   // The load actually happens asynchronously, so we pump messages to process
   // the pending continuation.
   FrameLoadWaiter(view_->GetMainRenderFrame()).Wait();
@@ -232,8 +233,9 @@
 
 void RenderViewTest::LoadHTMLWithUrlOverride(const char* html,
                                              const char* url_override) {
-  GetMainFrame()->LoadHTMLString(std::string(html),
-                                 blink::WebURL(GURL(url_override)));
+  RenderFrame::FromWebFrame(GetMainFrame())
+      ->LoadHTMLString(html, GURL(url_override), "UTF-8", GURL(),
+                       false /* replace_current_item */);
   // The load actually happens asynchronously, so we pump messages to process
   // the pending continuation.
   FrameLoadWaiter(view_->GetMainRenderFrame()).Wait();
diff --git a/content/renderer/dom_serializer_browsertest.cc b/content/renderer/dom_serializer_browsertest.cc
index 14e32bb5..34f6615 100644
--- a/content/renderer/dom_serializer_browsertest.cc
+++ b/content/renderer/dom_serializer_browsertest.cc
@@ -131,19 +131,10 @@
                     const GURL& base_url,
                     const WebString encoding_info) {
     FrameLoadWaiter waiter(GetRenderView()->GetMainRenderFrame());
-    // If input encoding is empty, use UTF-8 as default encoding.
-    if (encoding_info.IsEmpty()) {
-      GetMainFrame()->LoadHTMLString(contents, base_url);
-    } else {
-      // Do not use WebFrame.LoadHTMLString because it assumes that input
-      // html contents use UTF-8 encoding.
-      WebData data(contents.data(), contents.length());
-      GetMainFrame()->CommitDataNavigation(
-          blink::WebURLRequest(base_url), data, "text/html", encoding_info,
-          WebURL(), blink::WebFrameLoadType::kStandard, blink::WebHistoryItem(),
-          false /* is_client_redirect */, nullptr /* navigation_params */,
-          nullptr /* navigation_data */);
-    }
+    GetRenderView()->GetMainRenderFrame()->LoadHTMLString(
+        contents, base_url,
+        encoding_info.IsEmpty() ? "UTF-8" : encoding_info.Utf8(), GURL(),
+        false /* replace_current_item */);
     base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
     waiter.Wait();
   }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 4fef95c..84e89a6 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -204,6 +204,7 @@
 #include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_input_method_controller.h"
 #include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_navigation_control.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
 #include "third_party/blink/public/web/web_navigation_timings.h"
 #include "third_party/blink/public/web/web_plugin.h"
@@ -1298,7 +1299,7 @@
       WebString::FromUTF8(replicated_state.name),
       replicated_state.frame_policy.sandbox_flags);
   if (has_committed_real_load)
-    web_frame->SetCommittedFirstRealLoad();
+    render_frame->frame_->SetCommittedFirstRealLoad();
 
   // The RenderViewImpl and its RenderWidget already exist by the time we get
   // here.
@@ -1531,7 +1532,7 @@
   }
 
   if (has_committed_real_load)
-    web_frame->SetCommittedFirstRealLoad();
+    render_frame->frame_->SetCommittedFirstRealLoad();
 
   render_frame->Initialize();
 }
@@ -2914,14 +2915,14 @@
   pending_context_menus_.Remove(request_id);
 }
 
-void RenderFrameImpl::BindToFrame(WebLocalFrame* web_frame) {
+void RenderFrameImpl::BindToFrame(blink::WebNavigationControl* frame) {
   DCHECK(!frame_);
 
   std::pair<FrameMap::iterator, bool> result =
-      g_frame_map.Get().emplace(web_frame, this);
+      g_frame_map.Get().emplace(frame, this);
   CHECK(result.second) << "Inserting a duplicate item.";
 
-  frame_ = web_frame;
+  frame_ = frame;
 }
 
 blink::WebPlugin* RenderFrameImpl::CreatePlugin(
@@ -3383,10 +3384,10 @@
 
   // On load failure, a frame can ask its owner to render fallback content.
   // When that happens, don't load an error page.
-  WebLocalFrame::FallbackContentResult fallback_result =
+  blink::WebNavigationControl::FallbackContentResult fallback_result =
       frame_->MaybeRenderFallbackContent(error);
-  if (fallback_result != WebLocalFrame::NoFallbackContent) {
-    if (fallback_result == WebLocalFrame::NoLoadInProgress) {
+  if (fallback_result != blink::WebNavigationControl::NoFallbackContent) {
+    if (fallback_result == blink::WebNavigationControl::NoLoadInProgress) {
       // If the frame wasn't loading but was fallback-eligible, the fallback
       // content won't be shown. However, showing an error page isn't right
       // either, as the frame has already been populated with something
@@ -7085,6 +7086,20 @@
   return browser_side_navigation_pending_;
 }
 
+void RenderFrameImpl::LoadHTMLString(const std::string& html,
+                                     const GURL& base_url,
+                                     const std::string& text_encoding,
+                                     const GURL& unreachable_url,
+                                     bool replace_current_item) {
+  frame_->CommitDataNavigation(
+      blink::WebURLRequest(base_url), WebData(html.data(), html.length()),
+      "text/html", WebString::FromUTF8(text_encoding), unreachable_url,
+      replace_current_item ? blink::WebFrameLoadType::kReplaceCurrentItem
+                           : blink::WebFrameLoadType::kStandard,
+      blink::WebHistoryItem(), false /* is_client_redirect */,
+      nullptr /* navigation_params */, nullptr /* navigation_data */);
+}
+
 scoped_refptr<base::SingleThreadTaskRunner> RenderFrameImpl::GetTaskRunner(
     blink::TaskType task_type) {
   return GetWebFrame()->GetTaskRunner(task_type);
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 22e36a5..8359b31 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -463,7 +463,7 @@
   int ShowContextMenu(ContextMenuClient* client,
                       const ContextMenuParams& params) override;
   void CancelContextMenu(int request_id) override;
-  void BindToFrame(blink::WebLocalFrame* frame) override;
+  void BindToFrame(blink::WebNavigationControl* frame) override;
   blink::WebPlugin* CreatePlugin(
       const WebPluginInfo& info,
       const blink::WebPluginParams& params,
@@ -505,6 +505,11 @@
   bool IsPasting() const override;
   blink::mojom::PageVisibilityState GetVisibilityState() const override;
   bool IsBrowserSideNavigationPending() override;
+  void LoadHTMLString(const std::string& html,
+                      const GURL& base_url,
+                      const std::string& text_encoding,
+                      const GURL& unreachable_url,
+                      bool replace_current_item) override;
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
       blink::TaskType task_type) override;
   int GetEnabledBindings() const override;
@@ -1298,7 +1303,7 @@
   // constructor until BindToFrame() is called, and it is null after
   // FrameDetached() is called until destruction (which is asynchronous in the
   // case of the main frame, but not subframes).
-  blink::WebLocalFrame* frame_;
+  blink::WebNavigationControl* frame_ = nullptr;
 
   // Boolean value indicating whether this RenderFrameImpl object is for the
   // main frame or not. It remains accurate during destruction, even when
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index c3bc1e6..8decba47 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -2212,8 +2212,8 @@
 // recorded at an appropriate time and is passed in the corresponding message.
 TEST_F(RenderViewImplTest, RendererNavigationStartTransmittedToBrowser) {
   base::TimeTicks lower_bound_navigation_start(base::TimeTicks::Now());
-  frame()->GetWebFrame()->LoadHTMLString(
-      "hello world", blink::WebURL(GURL("data:text/html,")));
+  frame()->LoadHTMLString("hello world", GURL("data:text/html,"), "UTF-8",
+                          GURL(), false /* replace_current_item */);
 
   NavigationState* navigation_state = NavigationState::FromDocumentLoader(
       frame()->GetWebFrame()->GetProvisionalDocumentLoader());
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.cc b/content/renderer/service_worker/service_worker_subresource_loader.cc
index 79e404a..649ef3b 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader.cc
@@ -631,9 +631,9 @@
             fetch_event_timing_->respond_with_settled_time);
 
     // Time spent reading response body.
-    UMA_HISTOGRAM_TIMES(
+    UMA_HISTOGRAM_MEDIUM_TIMES(
         "ServiceWorker.LoadTiming.Subresource."
-        "ResponseReceivedToCompleted",
+        "ResponseReceivedToCompleted2",
         completion_time - response_head_.load_timing.receive_headers_end);
   } else {
     // Mojo message delay (network fallback case). See above for the detail.
diff --git a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
index 6366f8ff..f3d1ed7 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
@@ -637,7 +637,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       1);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 1);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 1);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, Abort) {
@@ -665,7 +665,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       0);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 0);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 0);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, DropController) {
@@ -766,7 +766,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       0);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 0);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 0);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, DropController_RestartFetchEvent) {
@@ -829,7 +829,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       1);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 1);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 1);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, DropController_TooManyRestart) {
@@ -863,7 +863,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       0);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 0);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 0);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, StreamResponse) {
@@ -917,7 +917,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       1);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 1);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 1);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, StreamResponse_Abort) {
@@ -971,7 +971,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       0);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 0);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 0);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, BlobResponse) {
@@ -1022,7 +1022,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       1);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 1);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 1);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, BlobResponseWithoutMetadata) {
@@ -1065,7 +1065,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       1);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 1);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 1);
 }
 
 // Test when the service worker responds with network fallback.
@@ -1125,7 +1125,7 @@
       "ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady",
       0);
   histogram_tester.ExpectTotalCount(
-      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted", 0);
+      "ServiceWorker.LoadTiming.Subresource.ResponseReceivedToCompleted2", 0);
 }
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, RedirectResponse) {
diff --git a/content/shell/renderer/layout_test/blink_test_runner.cc b/content/shell/renderer/layout_test/blink_test_runner.cc
index f2970fe..d9bed3a4 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.cc
+++ b/content/shell/renderer/layout_test/blink_test_runner.cc
@@ -849,12 +849,9 @@
   LayoutTestRenderThreadObserver::GetInstance()->test_interfaces()->ResetAll();
   Reset(true /* for_new_test */);
   // Navigating to about:blank will make sure that no new loads are initiated
-  // by the renderer.
-  main_frame->CommitNavigation(
-      WebURLRequest(GURL(url::kAboutBlankURL)),
-      blink::WebFrameLoadType::kStandard, blink::WebHistoryItem(), false,
-      base::UnguessableToken::Create(), nullptr /* navigation_params */,
-      nullptr /* extra_data */);
+  // by the renderer. We know that about:blank navigation will finish
+  // without going to the network.
+  main_frame->StartNavigation(WebURLRequest(GURL(url::kAboutBlankURL)));
   Send(new ShellViewHostMsg_ResetDone(routing_id()));
 }
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 081f9c8..41e349b3 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -865,7 +865,6 @@
     "../browser/service_worker/service_worker_tls_browsertest.cc",
     "../browser/session_history_browsertest.cc",
     "../browser/shape_detection/shape_detection_browsertest.cc",
-    "../browser/shared_worker/worker_browsertest.cc",
     "../browser/site_per_process_browsertest.cc",
     "../browser/site_per_process_browsertest.h",
     "../browser/site_per_process_hit_test_browsertest.cc",
@@ -910,6 +909,7 @@
     "../browser/webrtc/webrtc_webcam_browsertest.cc",
     "../browser/webrtc/webrtc_webcam_browsertest.h",
     "../browser/webui/web_ui_mojo_browsertest.cc",
+    "../browser/worker_host/worker_browsertest.cc",
     "../renderer/accessibility/render_accessibility_impl_browsertest.cc",
     "../renderer/blink_platform_audio_hardware_browsertest.cc",
     "../renderer/gin_browsertest.cc",
@@ -1612,11 +1612,6 @@
     "../browser/service_worker/service_worker_version_unittest.cc",
     "../browser/service_worker/service_worker_write_to_cache_job_unittest.cc",
     "../browser/shareable_file_reference_unittest.cc",
-    "../browser/shared_worker/mock_shared_worker.cc",
-    "../browser/shared_worker/mock_shared_worker.h",
-    "../browser/shared_worker/shared_worker_host_unittest.cc",
-    "../browser/shared_worker/shared_worker_instance_unittest.cc",
-    "../browser/shared_worker/shared_worker_service_impl_unittest.cc",
     "../browser/site_instance_impl_unittest.cc",
     "../browser/startup_task_runner_unittest.cc",
     "../browser/storage_partition_impl_map_unittest.cc",
@@ -1653,6 +1648,11 @@
     "../browser/webui/url_data_manager_backend_unittest.cc",
     "../browser/webui/web_ui_data_source_unittest.cc",
     "../browser/webui/web_ui_message_handler_unittest.cc",
+    "../browser/worker_host/mock_shared_worker.cc",
+    "../browser/worker_host/mock_shared_worker.h",
+    "../browser/worker_host/shared_worker_host_unittest.cc",
+    "../browser/worker_host/shared_worker_instance_unittest.cc",
+    "../browser/worker_host/shared_worker_service_impl_unittest.cc",
     "../child/blink_platform_impl_unittest.cc",
     "../child/dwrite_font_proxy/dwrite_font_proxy_win_unittest.cc",
     "../child/dwrite_font_proxy/font_fallback_win_unittest.cc",
@@ -1683,7 +1683,6 @@
     "../common/page_zoom_unittest.cc",
     "../common/plugin_list_unittest.cc",
     "../common/sandbox_mac_fontloading_unittest.mm",
-    "../common/sandbox_mac_system_access_unittest.mm",
     "../common/sandbox_mac_unittest_helper.h",
     "../common/sandbox_mac_unittest_helper.mm",
     "../common/service_manager/service_manager_connection_impl_unittest.cc",
diff --git a/content/test/data/sxg/generate-test-certs.sh b/content/test/data/sxg/generate-test-certs.sh
index deadbbd3..3d2021f 100755
--- a/content/test/data/sxg/generate-test-certs.sh
+++ b/content/test/data/sxg/generate-test-certs.sh
@@ -43,10 +43,6 @@
 echo "with the followings:"
 echo "===="
 
-echo 'constexpr char kCertPEMRSA[] = R"('
-sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
-  ../../../../net/data/ssl/certificates/wildcard.pem
-echo ')";'
 echo 'constexpr char kCertPEMECDSAP256[] = R"('
 cat ./prime256v1-sha256.public.pem
 echo ')";'
diff --git a/content/test/data/sxg/generate-test-sxgs.sh b/content/test/data/sxg/generate-test-sxgs.sh
index ae694a81..50f82c4 100755
--- a/content/test/data/sxg/generate-test-sxgs.sh
+++ b/content/test/data/sxg/generate-test-sxgs.sh
@@ -113,23 +113,6 @@
 echo "signed_exchange_signature_verifier_unittest.cc with the followings:"
 echo "===="
 
-sed -ne '/-BEGIN PRIVATE KEY-/,/-END PRIVATE KEY-/p' \
-  ../../../../net/data/ssl/certificates/wildcard.pem \
-  > $tmpdir/wildcard_example.org.private.pem
-sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' \
-  ../../../../net/data/ssl/certificates/wildcard.pem \
-  > $tmpdir/wildcard_example.org.public.pem
-gen-signedexchange \
-  -version 1b2 \
-  -uri https://test.example.org/test/ \
-  -content test.html \
-  -certificate $tmpdir/wildcard_example.org.public.pem \
-  -privateKey $tmpdir/wildcard_example.org.private.pem \
-  -date 2018-02-06T04:45:41Z \
-  -o $tmpdir/out.htxg
-
-dumpSignature kSignatureHeaderRSA $tmpdir/out.htxg
-
 gen-signedexchange \
   -version 1b2 \
   -uri https://test.example.org/test/ \
diff --git a/content/test/fuzzer/merkle_integrity_source_stream_fuzzer.cc b/content/test/fuzzer/merkle_integrity_source_stream_fuzzer.cc
index 8e3a419..5b63bfd 100644
--- a/content/test/fuzzer/merkle_integrity_source_stream_fuzzer.cc
+++ b/content/test/fuzzer/merkle_integrity_source_stream_fuzzer.cc
@@ -27,7 +27,7 @@
   auto mi_stream = std::make_unique<content::MerkleIntegritySourceStream>(
       header, std::move(fuzzed_source_stream));
   while (true) {
-    size_t read_size = data_provider.ConsumeUint32InRange(1, 1024);
+    size_t read_size = data_provider.ConsumeIntegralInRange(1, 1024);
     auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(read_size);
     int result = mi_stream->Read(io_buffer.get(), io_buffer->size(),
                                  callback.callback());
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 06fbaea..3d4a1a1f99 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -65,10 +65,6 @@
     self.Fail('conformance2/textures/misc/tex-subimage3d-canvas-bug.html',
               ['win', 'opengl', 'passthrough'], bug=859400)
 
-    # Failing new test added in https://github.com/KhronosGroup/WebGL/pull/2658
-    self.Fail('conformance2/transform_feedback/' +
-              'same-buffer-two-binding-points.html', bug=866089)
-
     # Too slow (take about one hour to run)
     self.Skip('deqp/functional/gles3/builtinprecision/*.html', bug=619403)
 
diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc
index b3537e5..dfc1222 100644
--- a/content/test/layouttest_support.cc
+++ b/content/test/layouttest_support.cc
@@ -23,7 +23,7 @@
 #include "content/browser/bluetooth/bluetooth_device_chooser_controller.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/browser/shared_worker/shared_worker_service_impl.h"
+#include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/unique_name_helper.h"
 #include "content/public/browser/storage_partition.h"
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index 23282310..ffd6bb4d 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -20,6 +20,7 @@
 #include "services/network/public/cpp/resource_response.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_navigation_control.h"
 
 namespace content {
 
@@ -215,7 +216,7 @@
     // frame. However if the loaded html has a subframe,
     // BeginNavigation will be called from Blink and we should avoid
     // going through browser process in this case.
-    GetWebFrame()->CommitNavigation(
+    frame_->CommitNavigation(
         info->url_request, info->frame_load_type, blink::WebHistoryItem(),
         info->is_client_redirect, base::UnguessableToken::Create(),
         nullptr /* navigation_params */, nullptr /* extra_data */);
diff --git a/docs/speed/benchmark/harnesses/blink_perf.md b/docs/speed/benchmark/harnesses/blink_perf.md
index 107a4f7..f757799 100644
--- a/docs/speed/benchmark/harnesses/blink_perf.md
+++ b/docs/speed/benchmark/harnesses/blink_perf.md
@@ -147,6 +147,28 @@
 
 [simple-blob-measure-async.html](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/perf_tests/test_data/simple-blob-measure-async.html)
 
+## Canvas Tests
+
+The sub-framework [canvas_runner.js](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/perf_tests/canvas/resources/canvas_runner.js) is used for
+tests in the `canvas` directory. This can measure rasterization and GPU time
+using requestAnimationFrame (RAF) and contains a callback framework for video.
+
+Normal tests using `runTest()` work similarly to the asynchronous test above,
+but crucially wait for RAF after completing a single trial of
+`MEASURE_DRAW_TIMES` runs.
+
+RAF tests are triggered by appending the query string `raf` (case insensitive)
+to the test's url. These tests wait for RAF to return before making a
+measurement. This way rasterization and GPU time are included in the
+measurement.
+
+For example:
+
+The test [gpu-bound-shader.html](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/perf_tests/canvas/gpu-bound-shader.html) is just measuring
+CPU, and thus looks extremely fast as the test is just one slow shader.
+
+The url `gpu-bound-shader.html?raf` will measure rasterization and GPU time as
+well, thus giving a more realistic measurement of performance.
 
 ## Running Tests
 
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 9835eaf..582e35e 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -935,7 +935,8 @@
     }
   }
   request_time_tracker_->LogRequestStartTime(
-      request->id, base::TimeTicks::Now(), has_listener);
+      request->id, base::TimeTicks::Now(), has_listener,
+      HasExtraHeadersListener(browser_context, extension_info_map, request));
 
   const bool is_incognito_context = IsIncognitoBrowserContext(browser_context);
 
diff --git a/extensions/browser/api/web_request/web_request_time_tracker.cc b/extensions/browser/api/web_request/web_request_time_tracker.cc
index 326cb9b..b7662de8 100644
--- a/extensions/browser/api/web_request/web_request_time_tracker.cc
+++ b/extensions/browser/api/web_request/web_request_time_tracker.cc
@@ -21,7 +21,8 @@
 void ExtensionWebRequestTimeTracker::LogRequestStartTime(
     int64_t request_id,
     const base::TimeTicks& start_time,
-    bool has_listener) {
+    bool has_listener,
+    bool has_extra_headers_listener) {
   auto iter = request_time_logs_.find(request_id);
   if (iter != request_time_logs_.end())
     return;
@@ -29,6 +30,7 @@
   RequestTimeLog& log = request_time_logs_[request_id];
   log.request_start_time = start_time;
   log.has_listener = has_listener;
+  log.has_extra_headers_listener = has_extra_headers_listener;
 }
 
 void ExtensionWebRequestTimeTracker::LogRequestEndTime(
@@ -53,6 +55,11 @@
                         request_duration);
   }
 
+  if (log.has_extra_headers_listener) {
+    UMA_HISTOGRAM_TIMES("Extensions.WebRequest.TotalExtraHeadersRequestTime",
+                        request_duration);
+  }
+
   if (log.block_duration.is_zero())
     return;
 
diff --git a/extensions/browser/api/web_request/web_request_time_tracker.h b/extensions/browser/api/web_request/web_request_time_tracker.h
index e26bc9c4..01e59ab 100644
--- a/extensions/browser/api/web_request/web_request_time_tracker.h
+++ b/extensions/browser/api/web_request/web_request_time_tracker.h
@@ -21,10 +21,14 @@
   ExtensionWebRequestTimeTracker();
   ~ExtensionWebRequestTimeTracker();
 
-  // Records the time that a request was created.
+  // Records the time that a request was created.  |has_listener| will be true
+  // if there is at least one webRequest listener registered.
+  // |has_extra_headers_listener| will be true if there is at least one listener
+  // with 'extraHeaders' in the extraInfoSpec.
   void LogRequestStartTime(int64_t request_id,
                            const base::TimeTicks& start_time,
-                           bool has_listener);
+                           bool has_listener,
+                           bool has_extra_headers_listener);
 
   // Records the time that a request either completed or encountered an error.
   void LogRequestEndTime(int64_t request_id, const base::TimeTicks& end_time);
@@ -48,6 +52,7 @@
     base::TimeTicks request_start_time;
     base::TimeDelta block_duration;
     bool has_listener = false;
+    bool has_extra_headers_listener = false;
 
     RequestTimeLog();
     ~RequestTimeLog();
diff --git a/extensions/browser/api/web_request/web_request_time_tracker_unittest.cc b/extensions/browser/api/web_request/web_request_time_tracker_unittest.cc
index 11ef7d2..6d65cef 100644
--- a/extensions/browser/api/web_request/web_request_time_tracker_unittest.cc
+++ b/extensions/browser/api/web_request/web_request_time_tracker_unittest.cc
@@ -26,10 +26,10 @@
   ExtensionWebRequestTimeTracker tracker;
   base::TimeTicks start;
 
-  tracker.LogRequestStartTime(1, start, false);
-  tracker.LogRequestStartTime(2, start, true);
-  tracker.LogRequestStartTime(3, start, true);
-  tracker.LogRequestStartTime(4, start, true);
+  tracker.LogRequestStartTime(1, start, false, false);
+  tracker.LogRequestStartTime(2, start, true, false);
+  tracker.LogRequestStartTime(3, start, true, false);
+  tracker.LogRequestStartTime(4, start, true, true);
   tracker.IncrementTotalBlockTime(1, kTinyDelay);
   tracker.IncrementTotalBlockTime(2, kModerateDelay);
   tracker.IncrementTotalBlockTime(2, kModerateDelay);
@@ -66,5 +66,11 @@
   histogram_tester.ExpectTotalCount(
       "Extensions.WebRequest.TotalBlockingRequestTime", 3);
 
+  histogram_tester.ExpectTimeBucketCount(
+      "Extensions.WebRequest.TotalExtraHeadersRequestTime", kLongRequestDelta,
+      1);
+  histogram_tester.ExpectTotalCount(
+      "Extensions.WebRequest.TotalExtraHeadersRequestTime", 1);
+
   EXPECT_TRUE(tracker.request_time_logs_.empty());
 }
diff --git a/extensions/renderer/script_context_browsertest.cc b/extensions/renderer/script_context_browsertest.cc
index 411894a1..a545009 100644
--- a/extensions/renderer/script_context_browsertest.cc
+++ b/extensions/renderer/script_context_browsertest.cc
@@ -45,7 +45,8 @@
   WebLocalFrame* frame = GetMainFrame();
   ASSERT_TRUE(frame);
 
-  frame->LoadHTMLString(frame_html, top_url);
+  content::RenderFrame::FromWebFrame(frame)->LoadHTMLString(
+      frame_html, top_url, "UTF-8", GURL(), false /* replace_current_item */);
   content::FrameLoadWaiter(content::RenderFrame::FromWebFrame(frame)).Wait();
 
   WebLocalFrame* frame1 = frame->FirstChild()->ToWebLocalFrame();
@@ -68,7 +69,9 @@
   ASSERT_EQ("frame3", frame3->AssignedName());
 
   // Load a blank document in a frame from a different origin.
-  frame3->LoadHTMLString(frame3_html, different_url);
+  content::RenderFrame::FromWebFrame(frame3)->LoadHTMLString(
+      frame3_html, different_url, "UTF-8", GURL(),
+      false /* replace_current_item */);
   content::FrameLoadWaiter(content::RenderFrame::FromWebFrame(frame3)).Wait();
 
   WebLocalFrame* frame3_1 = frame3->FirstChild()->ToWebLocalFrame();
diff --git a/gpu/command_buffer/service/buffer_manager.h b/gpu/command_buffer/service/buffer_manager.h
index 72fdd60..c21ea415 100644
--- a/gpu/command_buffer/service/buffer_manager.h
+++ b/gpu/command_buffer/service/buffer_manager.h
@@ -117,6 +117,10 @@
            non_transform_feedback_binding_count_ > 0;
   }
 
+  bool IsDoubleBoundForTransformFeedback() const {
+    return transform_feedback_indexed_binding_count_ > 1;
+  }
+
   void SetReadbackShadowAllocation(scoped_refptr<gpu::Buffer> shm,
                                    uint32_t shm_offset);
   scoped_refptr<gpu::Buffer> TakeReadbackShadowAllocation(void** data);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 5cbea96..e4be47a 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -6456,6 +6456,13 @@
       LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, msg.c_str());
       return;
     }
+    if (buffer->IsDoubleBoundForTransformFeedback()) {
+      std::string msg = base::StringPrintf(
+          "buffer at index %i is bound for multiple transform feedback outputs",
+          static_cast<int>(ii));
+      LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, function_name, msg.c_str());
+      return;
+    }
   }
   transform_feedback->DoBeginTransformFeedback(primitive_mode);
   DCHECK(transform_feedback->active());
diff --git a/gpu/ipc/common/gpu_memory_buffer_support.cc b/gpu/ipc/common/gpu_memory_buffer_support.cc
index 2aa7672c..66ac787 100644
--- a/gpu/ipc/common/gpu_memory_buffer_support.cc
+++ b/gpu/ipc/common/gpu_memory_buffer_support.cc
@@ -20,6 +20,7 @@
 
 #if defined(USE_OZONE)
 #include "ui/ozone/public/client_native_pixmap_factory_ozone.h"
+#include "ui/ozone/public/ozone_platform.h"
 #endif
 
 #if defined(OS_WIN)
@@ -42,13 +43,6 @@
 #endif
 }
 
-#if defined(OS_LINUX) || defined(USE_OZONE)
-GpuMemoryBufferSupport::GpuMemoryBufferSupport(
-    std::unique_ptr<gfx::ClientNativePixmapFactory>
-        client_native_pixmap_factory)
-    : client_native_pixmap_factory_(std::move(client_native_pixmap_factory)) {}
-#endif
-
 GpuMemoryBufferSupport::~GpuMemoryBufferSupport() {}
 
 gfx::GpuMemoryBufferType
@@ -113,7 +107,8 @@
   NOTREACHED();
   return false;
 #elif defined(USE_OZONE)
-  return client_native_pixmap_factory_->IsConfigurationSupported(format, usage);
+  return ui::OzonePlatform::EnsureInstance()->IsNativePixmapConfigSupported(
+      format, usage);
 #elif defined(OS_LINUX)
   return false;  // TODO(julian.isorce): Add linux support.
 #elif defined(OS_WIN)
diff --git a/gpu/ipc/common/gpu_memory_buffer_support.h b/gpu/ipc/common/gpu_memory_buffer_support.h
index aaddd9e..277ffd5a 100644
--- a/gpu/ipc/common/gpu_memory_buffer_support.h
+++ b/gpu/ipc/common/gpu_memory_buffer_support.h
@@ -28,10 +28,6 @@
 class GPU_EXPORT GpuMemoryBufferSupport {
  public:
   GpuMemoryBufferSupport();
-#if defined(OS_LINUX) || defined(USE_OZONE)
-  GpuMemoryBufferSupport(std::unique_ptr<gfx::ClientNativePixmapFactory>
-                             client_native_pixmap_factory);
-#endif
   ~GpuMemoryBufferSupport();
 
   // Returns the native GPU memory buffer factory type. Returns EMPTY_BUFFER
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_features.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_features.mm
index a4c7424b..247e7f1 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_features.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_features.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
 
 #include "base/command_line.h"
+#include "base/feature_list.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -18,6 +19,11 @@
 const char kSafeAreaChoiceValue[] = "safe-area";
 const char kHybridChoiceValue[] = "hybrid";
 const char kSmoothScrollingChoiceValue[] = "smooth";
+
+// Feature used by finch config to enable smooth scrolling when the default
+// viewport adjustment experiment is selected via command line switches.
+const base::Feature kSmoothScrollingDefault{"FullscreenSmoothScrollingDefault",
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
 }
 
 namespace fullscreen {
@@ -55,7 +61,9 @@
     if (viewport_experiment == std::string(kFrameChoiceValue))
       return ViewportAdjustmentExperiment::FRAME;
   }
-  return ViewportAdjustmentExperiment::SMOOTH_SCROLLING;
+  return base::FeatureList::IsEnabled(kSmoothScrollingDefault)
+             ? ViewportAdjustmentExperiment::SMOOTH_SCROLLING
+             : ViewportAdjustmentExperiment::FRAME;
 }
 
 }  // namespace features
diff --git a/ios/web/download/download_task_impl.h b/ios/web/download/download_task_impl.h
index 427f769..2c40468 100644
--- a/ios/web/download/download_task_impl.h
+++ b/ios/web/download/download_task_impl.h
@@ -69,6 +69,7 @@
   int64_t GetReceivedBytes() const override;
   int GetPercentComplete() const override;
   std::string GetContentDisposition() const override;
+  std::string GetOriginalMimeType() const override;
   std::string GetMimeType() const override;
   ui::PageTransition GetTransitionType() const override;
   base::string16 GetSuggestedFilename() const override;
@@ -122,6 +123,7 @@
   int64_t received_bytes_ = 0;
   int percent_complete_ = -1;
   std::string content_disposition_;
+  std::string original_mime_type_;
   std::string mime_type_;
   ui::PageTransition page_transition_ = ui::PAGE_TRANSITION_LINK;
   NSString* identifier_ = nil;
diff --git a/ios/web/download/download_task_impl.mm b/ios/web/download/download_task_impl.mm
index 55dc87a1..f34ad4f 100644
--- a/ios/web/download/download_task_impl.mm
+++ b/ios/web/download/download_task_impl.mm
@@ -172,6 +172,7 @@
     : original_url_(original_url),
       total_bytes_(total_bytes),
       content_disposition_(content_disposition),
+      original_mime_type_(mime_type),
       mime_type_(mime_type),
       page_transition_(page_transition),
       identifier_([identifier copy]),
@@ -292,6 +293,11 @@
   return content_disposition_;
 }
 
+std::string DownloadTaskImpl::GetOriginalMimeType() const {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  return original_mime_type_;
+}
+
 std::string DownloadTaskImpl::GetMimeType() const {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   return mime_type_;
diff --git a/ios/web/download/download_task_impl_unittest.mm b/ios/web/download/download_task_impl_unittest.mm
index be044e2..450c074a 100644
--- a/ios/web/download/download_task_impl_unittest.mm
+++ b/ios/web/download/download_task_impl_unittest.mm
@@ -238,6 +238,7 @@
   EXPECT_EQ(-1, task_->GetPercentComplete());
   EXPECT_EQ(kContentDisposition, task_->GetContentDisposition());
   EXPECT_EQ(kMimeType, task_->GetMimeType());
+  EXPECT_EQ(kMimeType, task_->GetOriginalMimeType());
   EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
       task_->GetTransitionType(), ui::PageTransition::PAGE_TRANSITION_TYPED));
   EXPECT_EQ("file.test", base::UTF16ToUTF8(task_->GetSuggestedFilename()));
@@ -623,7 +624,7 @@
   ASSERT_TRUE(session_task);
   testing::Mock::VerifyAndClearExpectations(&task_observer_);
 
-  // Download has finished with a different MIME type.
+  ASSERT_EQ(kMimeType, task_->GetOriginalMimeType());
   ASSERT_EQ(kMimeType, task_->GetMimeType());
   EXPECT_CALL(task_observer_, OnDownloadUpdated(task_.get()));
   const char kOtherMimeType[] = "application/foo";
@@ -637,6 +638,7 @@
   ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{
     return task_->IsDone();
   }));
+  EXPECT_EQ(kMimeType, task_->GetOriginalMimeType());
   EXPECT_EQ(kOtherMimeType, task_->GetMimeType());
 
   EXPECT_CALL(task_delegate_, OnTaskDestroyed(task_.get()));
@@ -649,8 +651,6 @@
   ASSERT_TRUE(session_task);
   testing::Mock::VerifyAndClearExpectations(&task_observer_);
 
-  // Download has finished with a different MIME type.
-  ASSERT_EQ(kMimeType, task_->GetMimeType());
   EXPECT_CALL(task_observer_, OnDownloadUpdated(task_.get()));
   int kHttpCode = 303;
   session_task.response =
diff --git a/ios/web/public/download/download_task.h b/ios/web/public/download/download_task.h
index 189a419..423054a 100644
--- a/ios/web/public/download/download_task.h
+++ b/ios/web/public/download/download_task.h
@@ -93,6 +93,9 @@
   // Content-Disposition header value from HTTP response.
   virtual std::string GetContentDisposition() const = 0;
 
+  // MIME type that the download request originally attempted to fetch.
+  virtual std::string GetOriginalMimeType() const = 0;
+
   // Effective MIME type of downloaded content.
   virtual std::string GetMimeType() const = 0;
 
diff --git a/ios/web/public/test/fakes/fake_download_task.h b/ios/web/public/test/fakes/fake_download_task.h
index ce86e43..d9daae5 100644
--- a/ios/web/public/test/fakes/fake_download_task.h
+++ b/ios/web/public/test/fakes/fake_download_task.h
@@ -34,6 +34,7 @@
   int64_t GetReceivedBytes() const override;
   int GetPercentComplete() const override;
   std::string GetContentDisposition() const override;
+  std::string GetOriginalMimeType() const override;
   std::string GetMimeType() const override;
   ui::PageTransition GetTransitionType() const override;
   base::string16 GetSuggestedFilename() const override;
@@ -69,6 +70,7 @@
   int64_t total_bytes_ = -1;
   int64_t received_bytes_ = 0;
   int percent_complete_ = -1;
+  std::string original_mime_type_;
   std::string mime_type_;
   ui::PageTransition page_transition_ = ui::PAGE_TRANSITION_LINK;
   base::string16 suggested_file_name_;
diff --git a/ios/web/public/test/fakes/fake_download_task.mm b/ios/web/public/test/fakes/fake_download_task.mm
index e54bffd0..33784f9 100644
--- a/ios/web/public/test/fakes/fake_download_task.mm
+++ b/ios/web/public/test/fakes/fake_download_task.mm
@@ -15,7 +15,10 @@
 
 FakeDownloadTask::FakeDownloadTask(const GURL& original_url,
                                    const std::string& mime_type)
-    : original_url_(original_url), mime_type_(mime_type), identifier_(@"") {}
+    : original_url_(original_url),
+      original_mime_type_(mime_type),
+      mime_type_(mime_type),
+      identifier_(@"") {}
 
 FakeDownloadTask::~FakeDownloadTask() {
   for (auto& observer : observers_)
@@ -78,6 +81,10 @@
   return content_disposition_;
 }
 
+std::string FakeDownloadTask::GetOriginalMimeType() const {
+  return original_mime_type_;
+}
+
 std::string FakeDownloadTask::GetMimeType() const {
   return mime_type_;
 }
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index 5f44a7f9..38e7bb1 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -4,18 +4,42 @@
 
 import("//build/config/linux/pkg_config.gni")
 import("//media/media_options.gni")
-import("//tools/generate_stubs/rules.gni")
 
 # When libpulse is not directly linked, use stubs to allow for dlopening of the
 # binary.
-if (use_pulseaudio && !link_pulseaudio) {
-  generate_stubs("libpulse_stubs") {
+if (!link_pulseaudio) {
+  action("pulse_generate_stubs") {
     extra_header = "pulse/pulse_stub_header.fragment"
-    sigs = [ "pulse/pulse.sigs" ]
-    output_name = "pulse/pulse_stubs"
-    deps = [
-      "//base",
+
+    script = "../../tools/generate_stubs/generate_stubs.py"
+    sources = [
+      "pulse/pulse.sigs",
     ]
+    inputs = [
+      extra_header,
+    ]
+    stubs_filename_root = "pulse_stubs"
+
+    outputs = [
+      "$target_gen_dir/pulse/$stubs_filename_root.cc",
+      "$target_gen_dir/pulse/$stubs_filename_root.h",
+    ]
+    args = [
+      "-i",
+      rebase_path("$target_gen_dir/pulse", root_build_dir),
+      "-o",
+      rebase_path("$target_gen_dir/pulse", root_build_dir),
+      "-t",
+      "posix_stubs",
+      "-e",
+      rebase_path(extra_header, root_build_dir),
+      "-s",
+      stubs_filename_root,
+      "-p",
+      "media/audio/pulse",
+    ]
+
+    args += rebase_path(sources, root_build_dir)
   }
 }
 
@@ -282,7 +306,9 @@
     if (link_pulseaudio) {
       configs += [ ":libpulse" ]
     } else {
-      deps += [ ":libpulse_stubs" ]
+      libs += [ "dl" ]
+      deps += [ ":pulse_generate_stubs" ]
+      sources += get_target_outputs(":pulse_generate_stubs")
     }
   }
 
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 076a911..ac8e8c7 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -8,7 +8,6 @@
 import("//media/gpu/args.gni")
 import("//media/media_options.gni")
 import("//testing/test.gni")
-import("//tools/generate_stubs/rules.gni")
 
 buildflag_header("buildflags") {
   header = "buildflags.h"
@@ -25,13 +24,38 @@
 }
 
 if (is_chromeos && use_v4lplugin) {
-  generate_stubs("libv4l2_stubs") {
+  action("libv4l2_generate_stubs") {
     extra_header = "v4l2/v4l2_stub_header.fragment"
-    sigs = [ "v4l2/v4l2.sig" ]
-    output_name = "v4l2_stubs"
-    deps = [
-      "//base",
+
+    script = "../../tools/generate_stubs/generate_stubs.py"
+    sources = [
+      "v4l2/v4l2.sig",
     ]
+    inputs = [
+      extra_header,
+    ]
+    stubs_filename_root = "v4l2_stubs"
+
+    outputs = [
+      "$target_gen_dir/v4l2/$stubs_filename_root.cc",
+      "$target_gen_dir/v4l2/$stubs_filename_root.h",
+    ]
+    args = [
+      "-i",
+      rebase_path("$target_gen_dir/v4l2", root_build_dir),
+      "-o",
+      rebase_path("$target_gen_dir/v4l2", root_build_dir),
+      "-t",
+      "posix_stubs",
+      "-e",
+      rebase_path(extra_header, root_build_dir),
+      "-s",
+      stubs_filename_root,
+      "-p",
+      "media/gpu/v4l2",
+    ]
+
+    args += rebase_path(sources, root_build_dir)
   }
 }
 
@@ -192,7 +216,8 @@
   }
 
   if (use_v4lplugin) {
-    deps += [ ":libv4l2_stubs" ]
+    sources += get_target_outputs(":libv4l2_generate_stubs")
+    deps += [ ":libv4l2_generate_stubs" ]
   }
 
   if (use_v4l2_codec) {
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 544f747..7296f0c 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -855,6 +855,7 @@
       return PIXEL_FORMAT_I420;
 
     case V4L2_PIX_FMT_YVU420:
+    case V4L2_PIX_FMT_YVU420M:
       return PIXEL_FORMAT_YV12;
 
     case V4L2_PIX_FMT_YUV422M:
@@ -870,20 +871,18 @@
 }
 
 // static
-uint32_t V4L2Device::VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format) {
+uint32_t V4L2Device::VideoPixelFormatToV4L2PixFmt(const VideoPixelFormat format,
+                                                  bool single_planar) {
   switch (format) {
     case PIXEL_FORMAT_NV12:
-      return V4L2_PIX_FMT_NV12M;
-
+      return single_planar ? V4L2_PIX_FMT_NV12 : V4L2_PIX_FMT_NV12M;
     case PIXEL_FORMAT_MT21:
-      return V4L2_PIX_FMT_MT21C;
-
+      // No single plane format for MT21.
+      return single_planar ? 0 : V4L2_PIX_FMT_MT21C;
     case PIXEL_FORMAT_I420:
-      return V4L2_PIX_FMT_YUV420M;
-
+      return single_planar ? V4L2_PIX_FMT_YUV420 : V4L2_PIX_FMT_YUV420M;
     case PIXEL_FORMAT_YV12:
-      return V4L2_PIX_FMT_YVU420;
-
+      return single_planar ? V4L2_PIX_FMT_YVU420 : V4L2_PIX_FMT_YVU420M;
     default:
       LOG(FATAL) << "Add more cases as needed";
       return 0;
@@ -891,6 +890,17 @@
 }
 
 // static
+uint32_t V4L2Device::VideoFrameLayoutToV4L2PixFmt(
+    const VideoFrameLayout& layout) {
+  if (layout.num_buffers() == 0) {
+    VLOGF(1) << "layout.num_buffers() must be more than 0: " << layout;
+    return 0;
+  }
+  return VideoPixelFormatToV4L2PixFmt(layout.format(),
+                                      layout.num_buffers() == 1);
+}
+
+// static
 uint32_t V4L2Device::VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile,
                                                    bool slice_based) {
   if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) {
@@ -1298,6 +1308,18 @@
       std::move(buffer_sizes), buffer_alignment);
 }
 
+// static
+bool V4L2Device::IsMultiPlanarV4L2PixFmt(uint32_t pix_fmt) {
+  constexpr uint32_t kMultiV4L2PixFmts[] = {
+      V4L2_PIX_FMT_NV12M,
+      V4L2_PIX_FMT_MT21C,
+      V4L2_PIX_FMT_YUV420M,
+      V4L2_PIX_FMT_YVU420M,
+  };
+  return std::find(std::cbegin(kMultiV4L2PixFmts), std::cend(kMultiV4L2PixFmts),
+                   pix_fmt) != std::cend(kMultiV4L2PixFmts);
+}
+
 void V4L2Device::GetSupportedResolution(uint32_t pixelformat,
                                         gfx::Size* min_resolution,
                                         gfx::Size* max_resolution) {
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 33bcc88..5b8b3e5 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -287,7 +287,12 @@
  public:
   // Utility format conversion functions
   static VideoPixelFormat V4L2PixFmtToVideoPixelFormat(uint32_t format);
-  static uint32_t VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format);
+  static uint32_t VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format,
+                                               bool single_planar);
+  // Returns v4l2 pixel format from |layout|. If there is no corresponding
+  // single- or multi-planar format or |layout| is invalid, returns 0.
+  static uint32_t VideoFrameLayoutToV4L2PixFmt(const VideoFrameLayout& layout);
+  // If there is no corresponding single- or multi-planar format, returns 0.
   static uint32_t VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile,
                                                 bool slice_based);
   static VideoCodecProfile V4L2VP9ProfileToVideoCodecProfile(uint32_t profile);
@@ -310,6 +315,9 @@
   static base::Optional<VideoFrameLayout> V4L2FormatToVideoFrameLayout(
       const struct v4l2_format& format);
 
+  // Returns whether |pix_fmt| is multi planar.
+  static bool IsMultiPlanarV4L2PixFmt(uint32_t pix_fmt);
+
   enum class Type {
     kDecoder,
     kEncoder,
diff --git a/media/gpu/v4l2/v4l2_image_processor.cc b/media/gpu/v4l2/v4l2_image_processor.cc
index 3b69f36..cb6f192 100644
--- a/media/gpu/v4l2/v4l2_image_processor.cc
+++ b/media/gpu/v4l2/v4l2_image_processor.cc
@@ -186,7 +186,11 @@
     return nullptr;
   }
   const uint32_t input_format_fourcc =
-      V4L2Device::VideoPixelFormatToV4L2PixFmt(input_layout.format());
+      V4L2Device::VideoFrameLayoutToV4L2PixFmt(input_layout);
+  if (!input_format_fourcc) {
+    VLOGF(1) << "Invalid VideoFrameLayout: " << input_layout;
+    return nullptr;
+  }
   if (!device->Open(V4L2Device::Type::kImageProcessor, input_format_fourcc)) {
     VLOGF(1) << "Failed to open device for input format: "
              << VideoPixelFormatToString(input_layout.format())
@@ -223,13 +227,19 @@
     return nullptr;
   }
 
+  const uint32_t output_format_fourcc =
+      V4L2Device::VideoFrameLayoutToV4L2PixFmt(output_layout);
+  if (!output_format_fourcc) {
+    VLOGF(1) << "Invalid VideoFrameLayout: " << output_layout;
+    return nullptr;
+  }
+
   // Try to set output format.
   memset(&format, 0, sizeof(format));
   format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
   format.fmt.pix_mp.width = output_layout.coded_size().width();
   format.fmt.pix_mp.height = output_layout.coded_size().height();
-  format.fmt.pix_mp.pixelformat =
-      V4L2Device::VideoPixelFormatToV4L2PixFmt(output_layout.format());
+  format.fmt.pix_mp.pixelformat = output_format_fourcc;
   if (device->Ioctl(VIDIOC_S_FMT, &format) != 0) {
     VLOGF(1) << "Failed to negotiate output format";
     return nullptr;
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index ae637e5..e947f42 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -68,6 +68,18 @@
 
 namespace media {
 
+namespace {
+
+size_t GetNumPlanesOfV4L2PixFmt(uint32_t pix_fmt) {
+  if (V4L2Device::IsMultiPlanarV4L2PixFmt(pix_fmt)) {
+    return VideoFrame::NumPlanes(
+        V4L2Device::V4L2PixFmtToVideoPixelFormat(pix_fmt));
+  }
+  return 1u;
+}
+
+}  // namespace
+
 // static
 const uint32_t V4L2VideoDecodeAccelerator::supported_input_fourccs_[] = {
     V4L2_PIX_FMT_H264, V4L2_PIX_FMT_VP8, V4L2_PIX_FMT_VP9,
@@ -2357,17 +2369,24 @@
       (output_mode_ == Config::OutputMode::ALLOCATE
            ? ImageProcessor::OutputMode::ALLOCATE
            : ImageProcessor::OutputMode::IMPORT);
-
-  auto input_layout = VideoFrameLayout::Create(
+  size_t num_planes = GetNumPlanesOfV4L2PixFmt(output_format_fourcc_);
+  // It is necessary to set strides and buffers even with dummy values,
+  // because VideoFrameLayout::num_buffers() specifies if
+  // |output_format_fourcc_| is single- or multi-planar.
+  auto input_layout = VideoFrameLayout::CreateWithStrides(
       V4L2Device::V4L2PixFmtToVideoPixelFormat(output_format_fourcc_),
-      coded_size_);
+      coded_size_, std::vector<int32_t>(num_planes) /* strides */,
+      std::vector<size_t>(num_planes) /* buffers */);
   if (!input_layout) {
     VLOGF(1) << "Invalid input layout";
     return false;
   }
-  auto output_layout = VideoFrameLayout::Create(
+
+  num_planes = GetNumPlanesOfV4L2PixFmt(egl_image_format_fourcc_);
+  auto output_layout = VideoFrameLayout::CreateWithStrides(
       V4L2Device::V4L2PixFmtToVideoPixelFormat(egl_image_format_fourcc_),
-      egl_image_size_);
+      egl_image_size_, std::vector<int32_t>(num_planes) /* strides */,
+      std::vector<size_t>(num_planes) /* buffers */);
   if (!output_layout) {
     VLOGF(1) << "Invalid output layout";
     return false;
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index d1fd481..98ffc5c 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -210,14 +210,25 @@
       return false;
     }
 
-    auto input_layout =
-        VideoFrameLayout::Create(config.input_format, visible_size_);
+    // It is necessary to set strides and buffers even with dummy values,
+    // because VideoFrameLayout::num_buffers() specifies v4l2 pix format
+    // associated with |config.input_format| is multi-planar.
+    auto input_layout = VideoFrameLayout::CreateWithStrides(
+        config.input_format, visible_size_,
+        std::vector<int32_t>(
+            VideoFrameLayout::NumPlanes(config.input_format)) /* strides */,
+        std::vector<size_t>(
+            VideoFrameLayout::NumPlanes(config.input_format)) /* buffers */);
     if (!input_layout) {
       VLOGF(1) << "Invalid image processor input layout";
       return false;
     }
-    auto output_layout =
-        VideoFrameLayout::Create(device_input_format_, input_allocated_size_);
+    auto output_layout = VideoFrameLayout::CreateWithStrides(
+        device_input_format_, input_allocated_size_,
+        std::vector<int32_t>(
+            VideoFrameLayout::NumPlanes(device_input_format_)) /* strides */,
+        std::vector<size_t>(
+            VideoFrameLayout::NumPlanes(device_input_format_)) /* buffers */);
     if (!output_layout) {
       VLOGF(1) << "Invalid image processor output layout";
       return false;
@@ -1115,62 +1126,49 @@
   device_input_format_ = PIXEL_FORMAT_UNKNOWN;
   input_planes_count_ = 0;
 
-  uint32_t input_format_fourcc =
-      V4L2Device::VideoPixelFormatToV4L2PixFmt(input_format);
-  if (!input_format_fourcc) {
-    VLOGF(1) << "Unsupported input format" << input_format_fourcc;
-    return false;
-  }
+  const std::vector<uint32_t> pix_fmt_candidates = {
+      // First see if the device can use the provided format directly.
+      // V4L2 VEA only supports multi plane input pixel format.
+      V4L2Device::VideoPixelFormatToV4L2PixFmt(input_format, false),
+      // Second try preferred input format.
+      device_->PreferredInputFormat(V4L2Device::Type::kEncoder),
+  };
 
-  size_t input_planes_count = VideoFrame::NumPlanes(input_format);
-  DCHECK_LE(input_planes_count, static_cast<size_t>(VIDEO_MAX_PLANES));
+  for (const auto pix_fmt : pix_fmt_candidates) {
+    auto trying_format = V4L2Device::V4L2PixFmtToVideoPixelFormat(pix_fmt);
+    DCHECK_NE(trying_format, PIXEL_FORMAT_UNKNOWN);
+    size_t planes_count = VideoFrame::NumPlanes(trying_format);
+    DCHECK_LE(planes_count, static_cast<size_t>(VIDEO_MAX_PLANES));
+    VLOGF(2) << "Trying S_FMT with " << FourccToString(pix_fmt) << " ("
+             << trying_format << ").";
 
-  // First see if we the device can use the provided input_format directly.
-  struct v4l2_format format;
-  memset(&format, 0, sizeof(format));
-  format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-  format.fmt.pix_mp.width = visible_size_.width();
-  format.fmt.pix_mp.height = visible_size_.height();
-  format.fmt.pix_mp.pixelformat = input_format_fourcc;
-  format.fmt.pix_mp.num_planes = input_planes_count;
-  if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
-    // Error or format unsupported by device, try to negotiate a fallback.
-    input_format_fourcc =
-        device_->PreferredInputFormat(V4L2Device::Type::kEncoder);
-    input_format =
-        V4L2Device::V4L2PixFmtToVideoPixelFormat(input_format_fourcc);
-    if (input_format == PIXEL_FORMAT_UNKNOWN) {
-      VLOGF(1) << "Unsupported input format: "
-               << FourccToString(input_format_fourcc);
-      return false;
-    }
-
-    input_planes_count = VideoFrame::NumPlanes(input_format);
-    DCHECK_LE(input_planes_count, static_cast<size_t>(VIDEO_MAX_PLANES));
-
-    // Device might have adjusted parameters, reset them along with the format.
+    struct v4l2_format format;
     memset(&format, 0, sizeof(format));
     format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
     format.fmt.pix_mp.width = visible_size_.width();
     format.fmt.pix_mp.height = visible_size_.height();
-    format.fmt.pix_mp.pixelformat = input_format_fourcc;
-    format.fmt.pix_mp.num_planes = input_planes_count;
-    IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
-    DCHECK_EQ(format.fmt.pix_mp.num_planes, input_planes_count);
+    format.fmt.pix_mp.pixelformat = pix_fmt;
+    format.fmt.pix_mp.num_planes = planes_count;
+    if (device_->Ioctl(VIDIOC_S_FMT, &format) == 0) {
+      VLOGF(2) << "Success: S_FMT with" << FourccToString(pix_fmt);
+      // Take device-adjusted sizes for allocated size. If the size is adjusted
+      // down, it means the input is too big and the hardware does not support
+      // it.
+      auto adjusted_size = V4L2Device::CodedSizeFromV4L2Format(format);
+      if (!gfx::Rect(adjusted_size).Contains(gfx::Rect(visible_size_))) {
+        VLOGF(1) << "Input size too big " << visible_size_.ToString()
+                 << ", adjusted to " << adjusted_size.ToString();
+        return false;
+      }
+
+      device_input_format_ = trying_format;
+      input_planes_count_ = planes_count;
+      input_allocated_size_ = adjusted_size;
+      return true;
+    }
   }
 
-  // Take device-adjusted sizes for allocated size. If the size is adjusted
-  // down, it means the input is too big and the hardware does not support it.
-  input_allocated_size_ = V4L2Device::CodedSizeFromV4L2Format(format);
-  if (!gfx::Rect(input_allocated_size_).Contains(gfx::Rect(visible_size_))) {
-    VLOGF(1) << "Input size too big " << visible_size_.ToString()
-             << ", adjusted to " << input_allocated_size_.ToString();
-    return false;
-  }
-
-  device_input_format_ = input_format;
-  input_planes_count_ = input_planes_count;
-  return true;
+  return false;
 }
 
 bool V4L2VideoEncodeAccelerator::SetFormats(VideoPixelFormat input_format,
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index 679f629..df1051eb 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -5,25 +5,49 @@
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//media/gpu/args.gni")
-import("//tools/generate_stubs/rules.gni")
 import("//ui/gl/features.gni")
 import("//ui/ozone/ozone.gni")
 
 assert(use_vaapi)
 
-generate_stubs("libva_stubs") {
+action("libva_generate_stubs") {
   extra_header = "va_stub_header.fragment"
-  sigs = [ "va.sigs" ]
+
+  script = "//tools/generate_stubs/generate_stubs.py"
+  sources = [
+    "va.sigs",
+  ]
+  inputs = [
+    extra_header,
+  ]
   if (use_x11) {
-    sigs += [ "va_x11.sigs" ]
+    sources += [ "va_x11.sigs" ]
   }
   if (is_linux) {
-    sigs += [ "va_drm.sigs" ]
+    sources += [ "va_drm.sigs" ]
   }
-  output_name = "va_stubs"
-  deps = [
-    "//base",
+  stubs_filename_root = "va_stubs"
+
+  outputs = [
+    "$target_gen_dir/$stubs_filename_root.cc",
+    "$target_gen_dir/$stubs_filename_root.h",
   ]
+  args = [
+    "-i",
+    rebase_path("$target_gen_dir", root_build_dir),
+    "-o",
+    rebase_path("$target_gen_dir", root_build_dir),
+    "-t",
+    "posix_stubs",
+    "-e",
+    rebase_path(extra_header, root_build_dir),
+    "-s",
+    stubs_filename_root,
+    "-p",
+    "media/gpu/vaapi",
+  ]
+
+  args += rebase_path(sources, root_build_dir)
 }
 
 source_set("vaapi") {
@@ -64,11 +88,12 @@
     "vp8_encoder.cc",
     "vp8_encoder.h",
   ]
+  sources += get_target_outputs(":libva_generate_stubs")
 
   configs += [ "//third_party/libyuv:libyuv_config" ]
 
   deps = [
-    ":libva_stubs",
+    ":libva_generate_stubs",
     "//gpu/ipc/service",
     "//media",
     "//media/gpu:common",
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc
index 5b8e183..a16c6a9 100644
--- a/media/gpu/vp9_decoder.cc
+++ b/media/gpu/vp9_decoder.cc
@@ -92,7 +92,10 @@
       // Not kDecoding, so we need a resume point (a keyframe), as we are after
       // reset or at the beginning of the stream. Drop anything that is not
       // a keyframe in such case, and continue looking for a keyframe.
-      if (curr_frame_hdr_->IsKeyframe()) {
+      // Only exception is when the stream/sequence starts with an Intra only
+      // frame.
+      if (curr_frame_hdr_->IsKeyframe() ||
+          (curr_frame_hdr_->IsIntra() && pic_size_.IsEmpty())) {
         state_ = kDecoding;
       } else {
         curr_frame_hdr_.reset();
@@ -136,11 +139,14 @@
     if (new_pic_size != pic_size_) {
       DVLOG(1) << "New resolution: " << new_pic_size.ToString();
 
-      if (!curr_frame_hdr_->IsKeyframe()) {
+      if (!curr_frame_hdr_->IsKeyframe() &&
+          (curr_frame_hdr_->IsIntra() && pic_size_.IsEmpty())) {
         // TODO(posciak): This is doable, but requires a few modifications to
         // VDA implementations to allow multiple picture buffer sets in flight.
         // http://crbug.com/832264
-        DVLOG(1) << "Resolution change currently supported for keyframes only";
+        DVLOG(1) << "Resolution change currently supported for keyframes and "
+                    "sequence begins with Intra only when there is no prior "
+                    "frames in the context";
         if (++size_change_failure_counter_ > kVPxMaxNumOfSizeChangeFailures) {
           SetError();
           return kDecodeError;
diff --git a/net/data/fuzzer_dictionaries/net_http_server_fuzzer.dict b/net/data/fuzzer_dictionaries/net_http_server_fuzzer.dict
index 89aea18..d28bae852 100644
--- a/net/data/fuzzer_dictionaries/net_http_server_fuzzer.dict
+++ b/net/data/fuzzer_dictionaries/net_http_server_fuzzer.dict
@@ -46,6 +46,6 @@
 # String terminator for FuzzedDataProvider::ConsumeRandomLengthString.
 "\\ "
 
-# There is a lot of use of ConsumeUint32InRange clients, like ConsumeBool,
+# There is a lot of use of ConsumeIntegralInRange clients, like ConsumeBool,
 # so try make it easy to produce lots of inputs for these.
 "\x00\x00\x00\x00"
diff --git a/net/dns/fuzzed_host_resolver.cc b/net/dns/fuzzed_host_resolver.cc
index 8290c82a..587afd5 100644
--- a/net/dns/fuzzed_host_resolver.cc
+++ b/net/dns/fuzzed_host_resolver.cc
@@ -168,14 +168,14 @@
   DnsConfig config;
 
   // Fuzz name servers.
-  uint32_t num_nameservers = data_provider_->ConsumeUint32InRange(0, 4);
+  uint32_t num_nameservers = data_provider_->ConsumeIntegralInRange(0, 4);
   for (uint32_t i = 0; i < num_nameservers; ++i) {
     config.nameservers.push_back(
         IPEndPoint(FuzzIPAddress(data_provider_), FuzzPort(data_provider_)));
   }
 
   // Fuzz suffix search list.
-  switch (data_provider_->ConsumeUint32InRange(0, 3)) {
+  switch (data_provider_->ConsumeIntegralInRange(0, 3)) {
     case 3:
       config.search.push_back("foo.com");
       FALLTHROUGH;
@@ -204,8 +204,8 @@
   config.unhandled_options = data_provider_->ConsumeBool();
   config.append_to_multi_label_name = data_provider_->ConsumeBool();
   config.randomize_ports = data_provider_->ConsumeBool();
-  config.ndots = data_provider_->ConsumeInt32InRange(0, 3);
-  config.attempts = data_provider_->ConsumeInt32InRange(1, 3);
+  config.ndots = data_provider_->ConsumeIntegralInRange(0, 3);
+  config.attempts = data_provider_->ConsumeIntegralInRange(1, 3);
 
   // Timeouts don't really work for fuzzing. Even a timeout of 0 milliseconds
   // will be increased after the first timeout, resulting in inconsistent
@@ -218,7 +218,7 @@
 
   std::unique_ptr<DnsClient> dns_client = DnsClient::CreateClientForTesting(
       net_log_, &socket_factory_,
-      base::Bind(&base::FuzzedDataProvider::ConsumeInt32InRange,
+      base::Bind(&base::FuzzedDataProvider::ConsumeIntegralInRange<int32_t>,
                  base::Unretained(data_provider_)));
   dns_client->SetConfig(config);
   SetDnsClient(std::move(dns_client));
diff --git a/net/dns/host_resolver_impl_fuzzer.cc b/net/dns/host_resolver_impl_fuzzer.cc
index 06c87e7..2718106a 100644
--- a/net/dns/host_resolver_impl_fuzzer.cc
+++ b/net/dns/host_resolver_impl_fuzzer.cc
@@ -64,8 +64,8 @@
       std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
     if (dns_requests->empty())
       return;
-    uint32_t index =
-        data_provider->ConsumeUint32InRange(0, dns_requests->size() - 1);
+    uint32_t index = data_provider->ConsumeIntegralInRange<uint32_t>(
+        0, dns_requests->size() - 1);
 
     // Remove the request from the list before waiting on it - this prevents one
     // of the other callbacks from deleting the callback being waited on.
@@ -83,8 +83,8 @@
       std::vector<std::unique_ptr<DnsRequest>>* dns_requests) {
     if (dns_requests->empty())
       return;
-    uint32_t index =
-        data_provider->ConsumeUint32InRange(0, dns_requests->size() - 1);
+    uint32_t index = data_provider->ConsumeIntegralInRange<uint32_t>(
+        0, dns_requests->size() - 1);
     auto request = dns_requests->begin() + index;
     (*request)->Cancel();
     dns_requests->erase(request);
@@ -112,7 +112,7 @@
 
     while (true) {
       bool done = false;
-      switch (data_provider_->ConsumeInt32InRange(0, 2)) {
+      switch (data_provider_->ConsumeIntegralInRange(0, 2)) {
         case 0:
           // Quit on 0, or when no data is left.
           done = true;
@@ -141,9 +141,9 @@
     if (data_provider_->ConsumeBool())
       info.set_host_resolver_flags(net::HOST_RESOLVER_CANONNAME);
 
-    net::RequestPriority priority =
-        static_cast<net::RequestPriority>(data_provider_->ConsumeInt32InRange(
-            net::MINIMUM_PRIORITY, net::MAXIMUM_PRIORITY));
+    net::RequestPriority priority = static_cast<net::RequestPriority>(
+        data_provider_->ConsumeIntegralInRange<int32_t>(net::MINIMUM_PRIORITY,
+                                                        net::MAXIMUM_PRIORITY));
 
     // Decide if should be a cache-only resolution.
     if (data_provider_->ConsumeBool()) {
@@ -206,7 +206,8 @@
     net::TestNetLog net_log;
 
     net::HostResolver::Options options;
-    options.max_concurrent_resolves = data_provider.ConsumeUint32InRange(1, 8);
+    options.max_concurrent_resolves =
+        data_provider.ConsumeIntegralInRange(1, 8);
     options.enable_caching = data_provider.ConsumeBool();
     net::FuzzedHostResolver host_resolver(options, &net_log, &data_provider);
     host_resolver.SetDnsClientEnabled(data_provider.ConsumeBool());
@@ -214,7 +215,7 @@
     std::vector<std::unique_ptr<DnsRequest>> dns_requests;
     bool done = false;
     while (!done) {
-      switch (data_provider.ConsumeInt32InRange(0, 3)) {
+      switch (data_provider.ConsumeIntegralInRange(0, 3)) {
         case 0:
           // Quit on 0, or when no data is left.
           done = true;
diff --git a/net/filter/fuzzed_source_stream.cc b/net/filter/fuzzed_source_stream.cc
index 108d134..3d8e037 100644
--- a/net/filter/fuzzed_source_stream.cc
+++ b/net/filter/fuzzed_source_stream.cc
@@ -40,7 +40,7 @@
   DCHECK_LE(0, buf_len);
 
   bool sync = data_provider_->ConsumeBool();
-  int result = data_provider_->ConsumeUint32InRange(0, buf_len);
+  int result = data_provider_->ConsumeIntegralInRange(0, buf_len);
   std::string data = data_provider_->ConsumeBytesAsString(result);
   result = data.size();
 
diff --git a/net/ntlm/ntlm_client_fuzzer.cc b/net/ntlm/ntlm_client_fuzzer.cc
index 6be44099..e553ff3 100644
--- a/net/ntlm/ntlm_client_fuzzer.cc
+++ b/net/ntlm/ntlm_client_fuzzer.cc
@@ -24,9 +24,7 @@
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   base::FuzzedDataProvider fdp(data, size);
   bool is_v2 = fdp.ConsumeBool();
-  uint64_t client_time =
-      (static_cast<uint64_t>(fdp.ConsumeUint32InRange(0, 0xffffffffu)) << 32) |
-      static_cast<uint64_t>(fdp.ConsumeUint32InRange(0, 0xffffffffu));
+  uint64_t client_time = fdp.ConsumeUint64();
   net::ntlm::NtlmClient client((net::ntlm::NtlmFeatures(is_v2)));
 
   // Generate the input strings and challenge message. The strings will have a
diff --git a/net/socket/fuzzed_datagram_client_socket.cc b/net/socket/fuzzed_datagram_client_socket.cc
index 5dec51e..852bb1c 100644
--- a/net/socket/fuzzed_datagram_client_socket.cc
+++ b/net/socket/fuzzed_datagram_client_socket.cc
@@ -140,7 +140,7 @@
 
   // Get contents of response.
   std::string data = data_provider_->ConsumeRandomLengthString(
-      data_provider_->ConsumeUint32InRange(0, buf_len));
+      data_provider_->ConsumeIntegralInRange(0, buf_len));
 
   int result;
   if (data.size() > 0) {
diff --git a/net/socket/socks_client_socket_fuzzer.cc b/net/socket/socks_client_socket_fuzzer.cc
index feaa182..cd44a023 100644
--- a/net/socket/socks_client_socket_fuzzer.cc
+++ b/net/socket/socks_client_socket_fuzzer.cc
@@ -36,7 +36,7 @@
   scoped_refptr<net::RuleBasedHostResolverProc> rules(
       new net::RuleBasedHostResolverProc(nullptr));
   mock_host_resolver.set_synchronous_mode(data_provider.ConsumeBool());
-  switch (data_provider.ConsumeInt32InRange(0, 2)) {
+  switch (data_provider.ConsumeIntegralInRange(0, 2)) {
     case 0:
       rules->AddRule("*", "127.0.0.1");
       break;
diff --git a/net/third_party/http2/decoder/http2_frame_decoder_fuzzer.cc b/net/third_party/http2/decoder/http2_frame_decoder_fuzzer.cc
index 27cb94c..7e8942e 100644
--- a/net/third_party/http2/decoder/http2_frame_decoder_fuzzer.cc
+++ b/net/third_party/http2/decoder/http2_frame_decoder_fuzzer.cc
@@ -15,7 +15,7 @@
   base::FuzzedDataProvider fuzzed_data_provider(data, size);
   http2::Http2FrameDecoder decoder;
   while (fuzzed_data_provider.remaining_bytes() > 0) {
-    size_t chunk_size = fuzzed_data_provider.ConsumeUint32InRange(1, 32);
+    size_t chunk_size = fuzzed_data_provider.ConsumeIntegralInRange(1, 32);
     std::vector<char> chunk =
         fuzzed_data_provider.ConsumeBytes<char>(chunk_size);
 
diff --git a/net/third_party/http2/hpack/decoder/hpack_decoder_fuzzer.cc b/net/third_party/http2/hpack/decoder/hpack_decoder_fuzzer.cc
index e10f21d..2e1c09e 100644
--- a/net/third_party/http2/hpack/decoder/hpack_decoder_fuzzer.cc
+++ b/net/third_party/http2/hpack/decoder/hpack_decoder_fuzzer.cc
@@ -18,12 +18,12 @@
 
   base::FuzzedDataProvider fuzzed_data_provider(data, size);
   size_t max_string_size =
-      fuzzed_data_provider.ConsumeUint32InRange(1, 10 * size);
+      fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 10 * size);
   http2::HpackDecoder decoder(http2::HpackDecoderNoOpListener::NoOpListener(),
                               max_string_size);
   decoder.StartDecodingBlock();
   while (fuzzed_data_provider.remaining_bytes() > 0) {
-    size_t chunk_size = fuzzed_data_provider.ConsumeUint32InRange(1, 32);
+    size_t chunk_size = fuzzed_data_provider.ConsumeIntegralInRange(1, 32);
     std::vector<char> chunk =
         fuzzed_data_provider.ConsumeBytes<char>(chunk_size);
 
diff --git a/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc b/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
index d10a561..2485805 100644
--- a/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
+++ b/net/third_party/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
@@ -38,8 +38,9 @@
 
   // Process up to 64 kB fragments at a time.  Too small upper bound might not
   // provide enough coverage, too large would make fuzzing less efficient.
-  auto fragment_size_generator = std::bind(
-      &QuicFuzzedDataProvider::ConsumeUint32InRange, &provider, 1, 64 * 1024);
+  auto fragment_size_generator =
+      std::bind(&QuicFuzzedDataProvider::ConsumeIntegralInRange<uint32_t>,
+                &provider, 1, 64 * 1024);
 
   QpackDecode(
       &handler, fragment_size_generator,
diff --git a/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc b/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
index b39aede..3fe9de8 100644
--- a/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
+++ b/net/third_party/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
@@ -119,8 +119,9 @@
 
   // Process up to 64 kB fragments at a time.  Too small upper bound might not
   // provide enough coverage, too large would make fuzzing less efficient.
-  auto fragment_size_generator = std::bind(
-      &QuicFuzzedDataProvider::ConsumeUint32InRange, &provider, 1, 64 * 1024);
+  auto fragment_size_generator =
+      std::bind(&QuicFuzzedDataProvider::ConsumeIntegralInRange<uint32_t>,
+                &provider, 1, 64 * 1024);
 
   // Encode header list.
   QuicString encoded_header_block =
diff --git a/net/url_request/url_request_data_job_fuzzer.cc b/net/url_request/url_request_data_job_fuzzer.cc
index f83a94a..29d0ddb 100644
--- a/net/url_request/url_request_data_job_fuzzer.cc
+++ b/net/url_request/url_request_data_job_fuzzer.cc
@@ -47,7 +47,7 @@
     read_lengths_.clear();
 
     // Allocate an IOBuffer with fuzzed size.
-    int buf_size = provider.ConsumeUint32InRange(1, 127);  // 7 bits.
+    int buf_size = provider.ConsumeIntegralInRange(1, 127);  // 7 bits.
     buf_ = base::MakeRefCounted<net::IOBufferWithSize>(buf_size);
 
     // Generate a range header, and a bool determining whether to use it.
@@ -63,7 +63,7 @@
     size_t simulated_bytes_read = 0;
     while (simulated_bytes_read < provider.remaining_bytes() &&
            read_lengths_.size() < 20000u) {
-      size_t read_length = provider.ConsumeUint32InRange(1, buf_size);
+      size_t read_length = provider.ConsumeIntegralInRange(1, buf_size);
       read_lengths_.push_back(read_length);
       simulated_bytes_read += read_length;
     }
diff --git a/net/websockets/websocket_deflate_stream_fuzzer.cc b/net/websockets/websocket_deflate_stream_fuzzer.cc
index 712804c..a3432ea2 100644
--- a/net/websockets/websocket_deflate_stream_fuzzer.cc
+++ b/net/websockets/websocket_deflate_stream_fuzzer.cc
@@ -67,9 +67,10 @@
  private:
   std::unique_ptr<WebSocketFrame> CreateFrame() {
     WebSocketFrameHeader::OpCode opcode =
-        fuzzed_data_provider_->ConsumeUint32InRange(
-            WebSocketFrameHeader::kOpCodeContinuation,
-            WebSocketFrameHeader::kOpCodeControlUnused);
+        fuzzed_data_provider_
+            ->ConsumeIntegralInRange<WebSocketFrameHeader::OpCode>(
+                WebSocketFrameHeader::kOpCodeContinuation,
+                WebSocketFrameHeader::kOpCodeControlUnused);
     auto frame = std::make_unique<WebSocketFrame>(opcode);
     // Bad news: ConsumeBool actually consumes a whole byte per call, so do
     // something hacky to conserve precious bits.
@@ -80,7 +81,7 @@
     frame->header.reserved3 = (flags >> 3) & 0x1;
     frame->header.masked = (flags >> 4) & 0x1;
     uint64_t payload_length =
-        fuzzed_data_provider_->ConsumeUint32InRange(0, 64);
+        fuzzed_data_provider_->ConsumeIntegralInRange(0, 64);
     std::vector<char> payload =
         fuzzed_data_provider_->ConsumeBytes<char>(payload_length);
     frame->data = base::MakeRefCounted<IOBufferWithSize>(payload.size());
diff --git a/net/websockets/websocket_frame_parser_fuzzer.cc b/net/websockets/websocket_frame_parser_fuzzer.cc
index 112a8db3..90c5d92bc 100644
--- a/net/websockets/websocket_frame_parser_fuzzer.cc
+++ b/net/websockets/websocket_frame_parser_fuzzer.cc
@@ -16,7 +16,7 @@
   net::WebSocketFrameParser parser;
   std::vector<std::unique_ptr<net::WebSocketFrameChunk>> frame_chunks;
   while (fuzzed_data_provider.remaining_bytes() > 0) {
-    size_t chunk_size = fuzzed_data_provider.ConsumeUint32InRange(1, 32);
+    size_t chunk_size = fuzzed_data_provider.ConsumeIntegralInRange(1, 32);
     std::vector<char> chunk =
         fuzzed_data_provider.ConsumeBytes<char>(chunk_size);
     parser.Decode(chunk.data(), chunk.size(), &frame_chunks);
diff --git a/services/network/network_service_proxy_delegate.cc b/services/network/network_service_proxy_delegate.cc
index a1345bb..d377290f 100644
--- a/services/network/network_service_proxy_delegate.cc
+++ b/services/network/network_service_proxy_delegate.cc
@@ -102,7 +102,12 @@
     mojom::CustomProxyConfigClientRequest config_client_request)
     : proxy_config_(std::move(initial_config)),
       binding_(this, std::move(config_client_request)),
-      should_use_alternate_proxy_list_cache_(kMaxCacheSize) {}
+      should_use_alternate_proxy_list_cache_(kMaxCacheSize) {
+  // Make sure there is always a valid proxy config so we don't need to null
+  // check it.
+  if (!proxy_config_)
+    proxy_config_ = mojom::CustomProxyConfig::New();
+}
 
 void NetworkServiceProxyDelegate::OnBeforeStartTransaction(
     net::URLRequest* request,
diff --git a/services/network/network_service_proxy_delegate_unittest.cc b/services/network/network_service_proxy_delegate_unittest.cc
index 47da01e..4815f44f 100644
--- a/services/network/network_service_proxy_delegate_unittest.cc
+++ b/services/network/network_service_proxy_delegate_unittest.cc
@@ -50,6 +50,16 @@
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
+TEST_F(NetworkServiceProxyDelegateTest, NullConfigDoesNotCrash) {
+  mojom::CustomProxyConfigClientPtr client;
+  auto delegate = std::make_unique<NetworkServiceProxyDelegate>(
+      nullptr, mojo::MakeRequest(&client));
+
+  net::HttpRequestHeaders headers;
+  auto request = CreateRequest(GURL(kHttpUrl));
+  delegate->OnBeforeStartTransaction(request.get(), &headers);
+}
+
 TEST_F(NetworkServiceProxyDelegateTest, AddsHeadersBeforeCache) {
   auto config = mojom::CustomProxyConfig::New();
   config->rules.ParseFromString("http=proxy");
diff --git a/storage/browser/quota/quota_callbacks.h b/storage/browser/quota/quota_callbacks.h
index ac33358..1d4025f 100644
--- a/storage/browser/quota/quota_callbacks.h
+++ b/storage/browser/quota/quota_callbacks.h
@@ -14,7 +14,6 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/flat_map.h"
 #include "base/optional.h"
 #include "base/stl_util.h"
 #include "storage/browser/quota/quota_client.h"
@@ -38,7 +37,7 @@
 using UsageCallback = base::OnceCallback<void(int64_t usage)>;
 using UsageWithBreakdownCallback =
     base::OnceCallback<void(int64_t usage,
-                            base::flat_map<QuotaClient::ID, int64_t>)>;
+                            blink::mojom::UsageBreakdownPtr usage_breakdown)>;
 using AvailableSpaceCallback =
     base::OnceCallback<void(blink::mojom::QuotaStatusCode, int64_t)>;
 using StatusCallback = base::OnceCallback<void(blink::mojom::QuotaStatusCode)>;
@@ -59,9 +58,7 @@
     return (callbacks_.size() == 1);
   }
 
-  bool HasCallbacks() const {
-    return !callbacks_.empty();
-  }
+  bool HasCallbacks() const { return !callbacks_.empty(); }
 
   // Runs the callbacks added to the queue and clears the queue.
   void Run(Args... args) {
@@ -75,9 +72,7 @@
     callbacks_.swap(other->callbacks_);
   }
 
-  size_t size() const {
-    return callbacks_.size();
-  }
+  size_t size() const { return callbacks_.size(); }
 
  private:
   std::vector<CallbackType> callbacks_;
@@ -98,9 +93,7 @@
     return base::ContainsKey(callback_map_, key);
   }
 
-  bool HasAnyCallbacks() const {
-    return !callback_map_.empty();
-  }
+  bool HasAnyCallbacks() const { return !callback_map_.empty(); }
 
   iterator Begin() { return callback_map_.begin(); }
   iterator End() { return callback_map_.end(); }
diff --git a/storage/browser/quota/quota_manager.cc b/storage/browser/quota/quota_manager.cc
index 6384c7e..e46bb60 100644
--- a/storage/browser/quota/quota_manager.cc
+++ b/storage/browser/quota/quota_manager.cc
@@ -208,7 +208,7 @@
     blink::mojom::QuotaStatusCode status,
     int64_t usage,
     int64_t quota,
-    base::flat_map<QuotaClient::ID, int64_t> usage_breakdown) {
+    blink::mojom::UsageBreakdownPtr usage_breakdown) {
   std::move(callback).Run(status, usage, quota);
 }
 
@@ -273,8 +273,11 @@
 
   void Aborted() override {
     weak_factory_.InvalidateWeakPtrs();
-    std::move(callback_).Run(blink::mojom::QuotaStatusCode::kErrorAbort, 0, 0,
-                             base::flat_map<QuotaClient::ID, int64_t>());
+    std::move(callback_).Run(
+        blink::mojom::QuotaStatusCode::kErrorAbort, /*status*/
+        0,                                          /*usage*/
+        0,                                          /*quota*/
+        nullptr);                                   /*usage_breakdown*/
     DeleteSoon();
   }
 
@@ -327,10 +330,9 @@
     barrier_closure.Run();
   }
 
-  void OnGotHostUsage(
-      const base::Closure& barrier_closure,
-      int64_t usage,
-      base::flat_map<QuotaClient::ID, int64_t> usage_breakdown) {
+  void OnGotHostUsage(const base::Closure& barrier_closure,
+                      int64_t usage,
+                      blink::mojom::UsageBreakdownPtr usage_breakdown) {
     host_usage_ = usage;
     host_usage_breakdown_ = std::move(usage_breakdown);
     barrier_closure.Run();
@@ -355,7 +357,7 @@
   int64_t total_space_ = 0;
   int64_t desired_host_quota_ = 0;
   int64_t host_usage_ = 0;
-  base::flat_map<QuotaClient::ID, int64_t> host_usage_breakdown_;
+  blink::mojom::UsageBreakdownPtr host_usage_breakdown_;
   QuotaSettings settings_;
   base::WeakPtrFactory<UsageAndQuotaHelper> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaHelper);
@@ -866,8 +868,11 @@
     UsageAndQuotaWithBreakdownCallback callback) {
   if (!IsSupportedType(type) ||
       (is_incognito_ && !IsSupportedIncognitoType(type))) {
-    std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorNotSupported,
-                            0, 0, base::flat_map<QuotaClient::ID, int64_t>());
+    std::move(callback).Run(
+        blink::mojom::QuotaStatusCode::kErrorNotSupported, /*status*/
+        0,                                                 /*usage*/
+        0,                                                 /*quota*/
+        nullptr);                                          /*usage_breakdown*/
     return;
   }
   LazyInitialize();
diff --git a/storage/browser/quota/quota_manager.h b/storage/browser/quota/quota_manager.h
index 651a759..83e39d2 100644
--- a/storage/browser/quota/quota_manager.h
+++ b/storage/browser/quota/quota_manager.h
@@ -17,7 +17,6 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -38,19 +37,18 @@
 class SequencedTaskRunner;
 class SingleThreadTaskRunner;
 class TaskRunner;
-}
+}  // namespace base
 
 namespace quota_internals {
 class QuotaInternalsProxy;
-}
+}  // namespace quota_internals
 
 namespace content {
 class MockQuotaManager;
 class MockStorageClient;
 class QuotaManagerTest;
 class StorageMonitorTest;
-
-}
+}  // namespace content
 
 namespace storage {
 
@@ -112,11 +110,12 @@
  public:
   using UsageAndQuotaCallback = base::OnceCallback<
       void(blink::mojom::QuotaStatusCode, int64_t usage, int64_t quota)>;
-  using UsageAndQuotaWithBreakdownCallback = base::OnceCallback<void(
-      blink::mojom::QuotaStatusCode,
-      int64_t usage,
-      int64_t quota,
-      base::flat_map<QuotaClient::ID, int64_t> usage_breakdown)>;
+
+  using UsageAndQuotaWithBreakdownCallback =
+      base::OnceCallback<void(blink::mojom::QuotaStatusCode,
+                              int64_t usage,
+                              int64_t quota,
+                              blink::mojom::UsageBreakdownPtr usage_breakdown)>;
 
   static const int64_t kNoLimit;
 
@@ -140,7 +139,6 @@
   virtual void GetUsageAndQuotaForWebApps(const url::Origin& origin,
                                           blink::mojom::StorageType type,
                                           UsageAndQuotaCallback callback);
-
   // Called by DevTools.
   // This method is declared as virtual to allow test code to override it.
   virtual void GetUsageAndQuotaWithBreakdown(
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index 4f505de..4f24ca88 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -76,7 +76,6 @@
 url::Origin ToOrigin(const std::string& url) {
   return url::Origin::Create(GURL(url));
 }
-
 }  // namespace
 
 class QuotaManagerTest : public testing::Test {
@@ -152,7 +151,7 @@
     quota_status_ = QuotaStatusCode::kUnknown;
     usage_ = -1;
     quota_ = -1;
-    usage_breakdown_.clear();
+    usage_breakdown_ = nullptr;
     quota_manager_->GetUsageAndQuotaWithBreakdown(
         origin, type,
         base::BindOnce(&QuotaManagerTest::DidGetUsageAndQuotaWithBreakdown,
@@ -361,7 +360,7 @@
       QuotaStatusCode status,
       int64_t usage,
       int64_t quota,
-      base::flat_map<QuotaClient::ID, int64_t> usage_breakdown) {
+      blink::mojom::UsageBreakdownPtr usage_breakdown) {
     quota_status_ = status;
     usage_ = usage;
     quota_ = quota;
@@ -397,7 +396,7 @@
 
   void DidGetHostUsageBreakdown(
       int64_t usage,
-      base::flat_map<QuotaClient::ID, int64_t> usage_breakdown) {
+      blink::mojom::UsageBreakdownPtr usage_breakdown) {
     usage_ = usage;
     usage_breakdown_ = std::move(usage_breakdown);
   }
@@ -456,8 +455,8 @@
   QuotaStatusCode status() const { return quota_status_; }
   const UsageInfoEntries& usage_info() const { return usage_info_; }
   int64_t usage() const { return usage_; }
-  const base::flat_map<QuotaClient::ID, int64_t>& usage_breakdown() const {
-    return usage_breakdown_;
+  const blink::mojom::UsageBreakdown& usage_breakdown() const {
+    return *usage_breakdown_;
   }
   int64_t limited_usage() const { return limited_usage_; }
   int64_t unlimited_usage() const { return unlimited_usage_; }
@@ -497,7 +496,7 @@
   QuotaStatusCode quota_status_;
   UsageInfoEntries usage_info_;
   int64_t usage_;
-  base::flat_map<QuotaClient::ID, int64_t> usage_breakdown_;
+  blink::mojom::UsageBreakdownPtr usage_breakdown_;
   int64_t limited_usage_;
   int64_t unlimited_usage_;
   int64_t quota_;
@@ -741,7 +740,8 @@
 }
 
 TEST_F(QuotaManagerTest, GetUsageWithBreakdown_Simple) {
-  base::flat_map<QuotaClient::ID, int64_t> usage_breakdown_expected;
+  blink::mojom::UsageBreakdown usage_breakdown_expected =
+      blink::mojom::UsageBreakdown();
   static const MockOriginData kData1[] = {
       {"http://foo.com/", kTemp, 1}, {"http://foo.com/", kPerm, 80},
   };
@@ -765,58 +765,60 @@
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(80, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 80;
-  usage_breakdown_expected[QuotaClient::kDatabase] = 0;
-  usage_breakdown_expected[QuotaClient::kAppcache] = 0;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 80;
+  usage_breakdown_expected.webSql = 0;
+  usage_breakdown_expected.appcache = 0;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://foo.com/"), kTemp);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(1 + 4 + 8, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 1;
-  usage_breakdown_expected[QuotaClient::kDatabase] = 4;
-  usage_breakdown_expected[QuotaClient::kAppcache] = 8;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 1;
+  usage_breakdown_expected.webSql = 4;
+  usage_breakdown_expected.appcache = 8;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://bar.com/"), kTemp);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(0, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 0;
-  usage_breakdown_expected[QuotaClient::kDatabase] = 0;
-  usage_breakdown_expected[QuotaClient::kAppcache] = 0;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 0;
+  usage_breakdown_expected.webSql = 0;
+  usage_breakdown_expected.appcache = 0;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 }
 
 TEST_F(QuotaManagerTest, GetUsageWithBreakdown_NoClient) {
-  base::flat_map<QuotaClient::ID, int64_t> usage_breakdown_expected;
+  blink::mojom::UsageBreakdown usage_breakdown_expected =
+      blink::mojom::UsageBreakdown();
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://foo.com/"), kTemp);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(0, usage());
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://foo.com/"), kPerm);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(0, usage());
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetHostUsageBreakdown("foo.com", kTemp);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(0, usage());
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetHostUsageBreakdown("foo.com", kPerm);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(0, usage());
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 }
 
 TEST_F(QuotaManagerTest, GetUsageWithBreakdown_MultiOrigins) {
-  base::flat_map<QuotaClient::ID, int64_t> usage_breakdown_expected;
+  blink::mojom::UsageBreakdown usage_breakdown_expected =
+      blink::mojom::UsageBreakdown();
   static const MockOriginData kData[] = {
       {"http://foo.com/", kTemp, 10}, {"http://foo.com:8080/", kTemp, 20},
       {"http://bar.com/", kTemp, 5},  {"https://bar.com/", kTemp, 7},
@@ -829,19 +831,20 @@
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(10 + 20, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 10 + 20;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 10 + 20;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://bar.com/"), kTemp);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(5 + 7, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 5 + 7;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 5 + 7;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 }
 
 TEST_F(QuotaManagerTest, GetUsageWithBreakdown_MultipleClients) {
-  base::flat_map<QuotaClient::ID, int64_t> usage_breakdown_expected;
+  blink::mojom::UsageBreakdown usage_breakdown_expected =
+      blink::mojom::UsageBreakdown();
   static const MockOriginData kData1[] = {
       {"http://foo.com/", kTemp, 1},
       {"http://bar.com/", kTemp, 2},
@@ -863,33 +866,33 @@
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(1 + 128, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 1;
-  usage_breakdown_expected[QuotaClient::kDatabase] = 128;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 1;
+  usage_breakdown_expected.webSql = 128;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://bar.com/"), kPerm);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(4, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 4;
-  usage_breakdown_expected[QuotaClient::kDatabase] = 0;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 4;
+  usage_breakdown_expected.webSql = 0;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://unlimited/"), kTemp);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(512, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 0;
-  usage_breakdown_expected[QuotaClient::kDatabase] = 512;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 0;
+  usage_breakdown_expected.webSql = 512;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 
   GetUsageAndQuotaWithBreakdown(ToOrigin("http://unlimited/"), kPerm);
   scoped_task_environment_.RunUntilIdle();
   EXPECT_EQ(QuotaStatusCode::kOk, status());
   EXPECT_EQ(8, usage());
-  usage_breakdown_expected[QuotaClient::kFileSystem] = 8;
-  usage_breakdown_expected[QuotaClient::kDatabase] = 0;
-  EXPECT_EQ(usage_breakdown_expected, usage_breakdown());
+  usage_breakdown_expected.fileSystem = 8;
+  usage_breakdown_expected.webSql = 0;
+  EXPECT_TRUE(usage_breakdown_expected.Equals(usage_breakdown()));
 }
 
 void QuotaManagerTest::GetUsage_WithModifyTestBody(const StorageType type) {
diff --git a/storage/browser/quota/usage_tracker.cc b/storage/browser/quota/usage_tracker.cc
index 8a0bb17..85a4072 100644
--- a/storage/browser/quota/usage_tracker.cc
+++ b/storage/browser/quota/usage_tracker.cc
@@ -27,7 +27,7 @@
 void StripUsageWithBreakdownCallback(
     UsageCallback callback,
     int64_t usage,
-    base::flat_map<QuotaClient::ID, int64_t> usage_breakdown) {
+    blink::mojom::UsageBreakdownPtr usage_breakdown) {
   std::move(callback).Run(usage);
 }
 
@@ -245,7 +245,35 @@
   if (info->usage < 0)
     info->usage = 0;
 
-  info->usage_breakdown[client] += usage;
+  switch (client) {
+    case QuotaClient::kUnknown:
+      break;
+    case QuotaClient::kFileSystem:
+      info->usage_breakdown->fileSystem += usage;
+      break;
+    case QuotaClient::kDatabase:
+      info->usage_breakdown->webSql += usage;
+      break;
+    case QuotaClient::kAppcache:
+      info->usage_breakdown->appcache += usage;
+      break;
+    case QuotaClient::kIndexedDatabase:
+      info->usage_breakdown->indexedDatabase += usage;
+      break;
+    case QuotaClient::kServiceWorkerCache:
+      info->usage_breakdown->serviceWorkerCache += usage;
+      break;
+    case QuotaClient::kServiceWorker:
+      info->usage_breakdown->serviceWorker += usage;
+      break;
+    case QuotaClient::kBackgroundFetch:
+      info->usage_breakdown->backgroundFetch += usage;
+      break;
+    case QuotaClient::kAllClientsMask:
+      NOTREACHED();
+      break;
+  }
+
   barrier.Run();
 }
 
@@ -261,8 +289,9 @@
       << "host_usage_callbacks_ should only have non-empty callback lists";
   host_usage_callbacks_.erase(host_it);
 
-  for (auto& callback : pending_callbacks)
-    std::move(callback).Run(info->usage, info->usage_breakdown);
+  for (auto& callback : pending_callbacks) {
+    std::move(callback).Run(info->usage, info->usage_breakdown->Clone());
+  }
 }
 
 }  // namespace storage
diff --git a/storage/browser/quota/usage_tracker.h b/storage/browser/quota/usage_tracker.h
index 93a4f1e8..9bbd6c699 100644
--- a/storage/browser/quota/usage_tracker.h
+++ b/storage/browser/quota/usage_tracker.h
@@ -14,7 +14,6 @@
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "storage/browser/quota/quota_callbacks.h"
 #include "storage/browser/quota/quota_client.h"
@@ -71,7 +70,8 @@
     int pending_clients = 0;
     int64_t usage = 0;
     int64_t unlimited_usage = 0;
-    base::flat_map<QuotaClient::ID, int64_t> usage_breakdown;
+    blink::mojom::UsageBreakdownPtr usage_breakdown =
+        blink::mojom::UsageBreakdown::New();
   };
 
   friend class ClientUsageTracker;
diff --git a/storage/browser/quota/usage_tracker_unittest.cc b/storage/browser/quota/usage_tracker_unittest.cc
index 4bb6162..dbb4897 100644
--- a/storage/browser/quota/usage_tracker_unittest.cc
+++ b/storage/browser/quota/usage_tracker_unittest.cc
@@ -45,18 +45,6 @@
   *usage_out = usage;
 }
 
-void DidGetUsageBreakdown(
-    bool* done,
-    int64_t* usage_out,
-    base::flat_map<QuotaClient::ID, int64_t>* usage_breakdown_out,
-    int64_t usage,
-    base::flat_map<QuotaClient::ID, int64_t> usage_breakdown) {
-  EXPECT_FALSE(*done);
-  *done = true;
-  *usage_out = usage;
-  *usage_breakdown_out = usage_breakdown;
-}
-
 }  // namespace
 
 class MockQuotaClient : public QuotaClient {
@@ -149,6 +137,18 @@
     return &usage_tracker_;
   }
 
+  static void DidGetUsageBreakdown(
+      bool* done,
+      int64_t* usage_out,
+      blink::mojom::UsageBreakdownPtr* usage_breakdown_out,
+      int64_t usage,
+      blink::mojom::UsageBreakdownPtr usage_breakdown) {
+    EXPECT_FALSE(*done);
+    *usage_out = usage;
+    *usage_breakdown_out = std::move(usage_breakdown);
+    *done = true;
+  }
+
   void UpdateUsage(const url::Origin& origin, int64_t delta) {
     quota_client_.UpdateUsage(origin, delta);
     usage_tracker_.UpdateUsageCache(quota_client_.id(), origin, delta);
@@ -187,17 +187,18 @@
     EXPECT_TRUE(done);
   }
 
-  void GetHostUsageBreakdown(
-      const std::string& host,
-      int64_t* usage,
-      base::flat_map<QuotaClient::ID, int64_t>* usage_breakdown) {
+  std::pair<int64_t, blink::mojom::UsageBreakdownPtr> GetHostUsageBreakdown(
+      const std::string& host) {
+    int64_t usage;
+    blink::mojom::UsageBreakdownPtr usage_breakdown;
     bool done = false;
-    usage_tracker_.GetHostUsageWithBreakdown(
-        host,
-        base::BindOnce(&DidGetUsageBreakdown, &done, usage, usage_breakdown));
-    base::RunLoop().RunUntilIdle();
 
+    usage_tracker_.GetHostUsageWithBreakdown(
+        host, base::BindOnce(&UsageTrackerTest::DidGetUsageBreakdown, &done,
+                             &usage, &usage_breakdown));
+    base::RunLoop().RunUntilIdle();
     EXPECT_TRUE(done);
+    return std::make_pair(usage, std::move(usage_breakdown));
   }
 
   void GrantUnlimitedStoragePolicy(const url::Origin& origin) {
@@ -241,8 +242,8 @@
   int64_t usage = 0;
   int64_t unlimited_usage = 0;
   int64_t host_usage = 0;
-  base::flat_map<QuotaClient::ID, int64_t> host_usage_breakdown;
-  base::flat_map<QuotaClient::ID, int64_t> host_usage_breakdown_expected;
+  blink::mojom::UsageBreakdownPtr host_usage_breakdown_expected =
+      blink::mojom::UsageBreakdown::New();
   GetGlobalUsage(&usage, &unlimited_usage);
   EXPECT_EQ(0, usage);
   EXPECT_EQ(0, unlimited_usage);
@@ -257,9 +258,10 @@
   EXPECT_EQ(100, usage);
   EXPECT_EQ(0, unlimited_usage);
   EXPECT_EQ(100, host_usage);
-  host_usage_breakdown_expected[QuotaClient::kFileSystem] = 100;
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  host_usage_breakdown_expected->fileSystem = 100;
+  std::pair<int64_t, blink::mojom::UsageBreakdownPtr> host_usage_breakdown =
+      GetHostUsageBreakdown(host);
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 
   GrantUnlimitedStoragePolicy(origin);
   GetGlobalUsage(&usage, &unlimited_usage);
@@ -267,8 +269,8 @@
   EXPECT_EQ(100, usage);
   EXPECT_EQ(100, unlimited_usage);
   EXPECT_EQ(100, host_usage);
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  host_usage_breakdown = GetHostUsageBreakdown(host);
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 
   RevokeUnlimitedStoragePolicy(origin);
   GetGlobalUsage(&usage, &unlimited_usage);
@@ -276,16 +278,16 @@
   EXPECT_EQ(100, usage);
   EXPECT_EQ(0, unlimited_usage);
   EXPECT_EQ(100, host_usage);
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  GetHostUsageBreakdown(host);
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 }
 
 TEST_F(UsageTrackerTest, CacheDisabledClientTest) {
   int64_t usage = 0;
   int64_t unlimited_usage = 0;
   int64_t host_usage = 0;
-  base::flat_map<QuotaClient::ID, int64_t> host_usage_breakdown;
-  base::flat_map<QuotaClient::ID, int64_t> host_usage_breakdown_expected;
+  blink::mojom::UsageBreakdownPtr host_usage_breakdown_expected =
+      blink::mojom::UsageBreakdown::New();
 
   const url::Origin origin = url::Origin::Create(GURL("http://example.com"));
   const std::string host(net::GetHostOrSpecFromURL(origin.GetURL()));
@@ -296,9 +298,10 @@
   EXPECT_EQ(100, usage);
   EXPECT_EQ(0, unlimited_usage);
   EXPECT_EQ(100, host_usage);
-  host_usage_breakdown_expected[QuotaClient::kFileSystem] = 100;
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  host_usage_breakdown_expected->fileSystem = 100;
+  std::pair<int64_t, blink::mojom::UsageBreakdownPtr> host_usage_breakdown =
+      GetHostUsageBreakdown(host);
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 
   UpdateUsageWithoutNotification(origin, 100);
   GetGlobalUsage(&usage, &unlimited_usage);
@@ -306,8 +309,8 @@
   EXPECT_EQ(100, usage);
   EXPECT_EQ(0, unlimited_usage);
   EXPECT_EQ(100, host_usage);
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  host_usage_breakdown = GetHostUsageBreakdown(host);
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 
   GrantUnlimitedStoragePolicy(origin);
   UpdateUsageWithoutNotification(origin, 100);
@@ -319,9 +322,9 @@
   EXPECT_EQ(400, usage);
   EXPECT_EQ(400, unlimited_usage);
   EXPECT_EQ(400, host_usage);
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  host_usage_breakdown_expected[QuotaClient::kFileSystem] = 400;
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  host_usage_breakdown = GetHostUsageBreakdown(host);
+  host_usage_breakdown_expected->fileSystem = 400;
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 
   RevokeUnlimitedStoragePolicy(origin);
   GetGlobalUsage(&usage, &unlimited_usage);
@@ -329,8 +332,8 @@
   EXPECT_EQ(400, usage);
   EXPECT_EQ(0, unlimited_usage);
   EXPECT_EQ(400, host_usage);
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  host_usage_breakdown = GetHostUsageBreakdown(host);
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 
   SetUsageCacheEnabled(origin, true);
   UpdateUsage(origin, 100);
@@ -340,9 +343,9 @@
   EXPECT_EQ(500, usage);
   EXPECT_EQ(0, unlimited_usage);
   EXPECT_EQ(500, host_usage);
-  GetHostUsageBreakdown(host, &host_usage, &host_usage_breakdown);
-  host_usage_breakdown_expected[QuotaClient::kFileSystem] = 500;
-  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown);
+  host_usage_breakdown = GetHostUsageBreakdown(host);
+  host_usage_breakdown_expected->fileSystem = 500;
+  EXPECT_EQ(host_usage_breakdown_expected, host_usage_breakdown.second);
 }
 
 TEST_F(UsageTrackerTest, LimitedGlobalUsageTest) {
diff --git a/storage/browser/test/mock_quota_manager.h b/storage/browser/test/mock_quota_manager.h
index 0c106ae7..f196ab1 100644
--- a/storage/browser/test/mock_quota_manager.h
+++ b/storage/browser/test/mock_quota_manager.h
@@ -125,6 +125,7 @@
     ~StorageInfo();
     int64_t usage;
     int64_t quota;
+    blink::mojom::UsageBreakdownPtr usage_breakdown;
   };
 
   // This must be called via MockQuotaManagerProxy.
diff --git a/third_party/analytics/OWNERS b/third_party/analytics/OWNERS
deleted file mode 100644
index b7a2985..0000000
--- a/third_party/analytics/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-smckay@chromium.org
diff --git a/third_party/analytics/README.chromium b/third_party/analytics/README.chromium
deleted file mode 100644
index 27806bc..0000000
--- a/third_party/analytics/README.chromium
+++ /dev/null
@@ -1,22 +0,0 @@
-Name: Chrome Platform Analytics
-URL: https://github.com/GoogleChrome/chrome-platform-analytics
-Version: 1.6.0c
-Date: 3/24/2015
-License: Apache 2.0
-License File: NOT_SHIPPED
-Security Critical: yes
-
-Description:
-This package supports the use of Google Analytics (GA) in Chrome Platform
-Applications and Extensions.  
-
-The library is available from GitHub both as closure JS source and as a
-"compiled" bundle (traditional JS). The bundle is included here so that
-traditional JS apps (non-closure-compiled) can employ the library.
-
-The SHA1 hash for this version of GoogleChrome/chrome-platform-analytics is
-472ed678a2322e9a18b10eadb5adba2cde68529a.
-
-Local Modifications:
-Added externs.js allowing apps that do use closure compiler to compile code
-referencing this library.
diff --git a/third_party/analytics/externs.js b/third_party/analytics/externs.js
deleted file mode 100644
index d4bcce9..0000000
--- a/third_party/analytics/externs.js
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var goog = {};
-goog.async = {};
-
-/**
- * @constructor
- * @template VALUE
- */
-goog.async.Deferred;
-
-/**
- * @param {!function(this:T,VALUE):?} cb
- * @param {T=} opt_scope
- * @return {!goog.async.Deferred}
- * @template T
- */
-goog.async.Deferred.prototype.addCallback;
-
-/** @param {VALUE=} opt_result */
-goog.async.Deferred.prototype.callback;
-
-/**
- * @param {?(function(this:THIS, VALUE):
- *             (RESULT|IThenable<RESULT>|Thenable))=} opt_onFulfilled
- * @param {?(function(this:THIS, *): *)=} opt_onRejected
- * @param {THIS=} opt_context
- * @return {!Promise<RESULT>}
- * @template RESULT,THIS
- */
-goog.async.Deferred.prototype.then;
-
-var analytics = {};
-
-/** @typedef {string} */
-analytics.HitType;
-
-/** @enum {analytics.HitType} */
-analytics.HitTypes = {
-  APPVIEW: 'appview',
-  EVENT: 'event',
-  SOCIAL: 'social',
-  TRANSACTION: 'transaction',
-  ITEM: 'item',
-  TIMING: 'timing',
-  EXCEPTION: 'exception'
-};
-
-/**
- * @typedef {{
- *   id: string,
- *   name: string,
- *   valueType: analytics.ValueType,
- *   maxLength: (number|undefined),
- *   defaultValue: (string|undefined)
- * }}
- */
-analytics.Parameter;
-
-/** @typedef {string|number|boolean} */
-analytics.Value;
-
-/** @typedef {string} */
-analytics.ValueType;
-
-
-/**
- * @param {string} appName
- * @param {string=} opt_appVersion
- * @return {!analytics.GoogleAnalytics}
- */
-analytics.getService;
-
-
-/** @interface */
-analytics.GoogleAnalytics;
-
-/**
- * @param {string} trackingId
- * @return {!analytics.Tracker}
- */
-analytics.GoogleAnalytics.prototype.getTracker;
-
-/** @return {!goog.async.Deferred.<!analytics.Config>} */
-analytics.GoogleAnalytics.prototype.getConfig;
-
-
-/** @interface */
-analytics.Tracker;
-
-/** @typedef {function(!analytics.Tracker.Hit)} */
-analytics.Tracker.Filter;
-
-/**
- * @param {!analytics.HitType|!analytics.EventBuilder} hitType
- * @param {(!analytics.ParameterMap|
- *     !Object.<string, !analytics.Value>)=} opt_extraParams
- * @return {!goog.async.Deferred}
- */
-analytics.Tracker.prototype.send;
-
-/**
- * @param {string} description
- * @return {!goog.async.Deferred}
- */
-analytics.Tracker.prototype.sendAppView;
-
-/**
- * @param {string} category
- * @param {string} action
- * @param {string=} opt_label
- * @param {number=} opt_value
- * @return {!goog.async.Deferred}
- */
-analytics.Tracker.prototype.sendEvent;
-
-/**
- * @param {string} network Specifies the social network, for example Facebook
- *     or Google Plus.
- * @param {string} action Specifies the social interaction action.
- *     For example on Google Plus when a user clicks the +1 button,
- *     the social action is 'plus'.
- * @param {string} target Specifies the target of a social interaction.
- *     This value is typically a URL but can be any text.
- * @return {!goog.async.Deferred}
- */
-analytics.Tracker.prototype.sendSocial;
-
-/**
- * @param {string=} opt_description Specifies the description of an exception.
- * @param {boolean=} opt_fatal Was the exception fatal.
- * @return {!goog.async.Deferred}
- */
-analytics.Tracker.prototype.sendException;
-
-/**
- * @param {string} category Specifies the category of the timing.
- * @param {string} variable Specifies the variable name of the timing.
- * @param {number} value Specifies the value of the timing.
- * @param {string=} opt_label Specifies the optional label of the timing.
- * @param {number=} opt_sampleRate
- * @return {!goog.async.Deferred}
- */
-analytics.Tracker.prototype.sendTiming;
-
-analytics.Tracker.prototype.forceSessionStart;
-
-/**
- * @param {string} category
- * @param {string} variable
- * @param {string=} opt_label
- * @param {number=} opt_sampleRate
- * @return {!analytics.Tracker.Timing}
- */
-analytics.Tracker.prototype.startTiming;
-
-/** @interface */
-analytics.Tracker.Timing;
-
-/** @return {!goog.async.Deferred} */
-analytics.Tracker.Timing.prototype.send;
-
-/** @param {!analytics.Tracker.Filter} filter */
-analytics.Tracker.prototype.addFilter;
-
-/** @interface */
-analytics.Tracker.Hit;
-
-/** @return {!analytics.HitType} */
-analytics.Tracker.Hit.prototype.getHitType;
-
-/** @return {!analytics.ParameterMap} */
-analytics.Tracker.Hit.prototype.getParameters;
-
-analytics.Tracker.Hit.prototype.cancel;
-
-
-/** @interface */
-analytics.Config = function() {};
-
-/** @param {boolean} permitted */
-analytics.Config.prototype.setTrackingPermitted;
-
-/** @return {boolean} */
-analytics.Config.prototype.isTrackingPermitted;
-
-/** @param {number} sampleRate */
-analytics.Config.prototype.setSampleRate;
-
-/** @return {!goog.async.Deferred} Settles once the id has been reset. */
-analytics.Config.prototype.resetUserId;
-
-
-/** @interface */
-analytics.ParameterMap;
-
-/**
- * @typedef {{
- *   key: !analytics.Parameter,
- *   value: !analytics.Value
- * }}
- */
-analytics.ParameterMap.Entry;
-
-/**
- * @param {!analytics.Parameter} param
- * @param {!analytics.Value} value
- */
-analytics.ParameterMap.prototype.set;
-
-/**
- * @param {!analytics.Parameter} param
- * @return {?analytics.Value}
- */
-analytics.ParameterMap.prototype.get;
-
-/** @param {!analytics.Parameter} param */
-analytics.ParameterMap.prototype.remove;
-
-/** @return {!Object.<string, analytics.Value>} */
-analytics.ParameterMap.prototype.toObject;
-
-
-/** @interface */
-analytics.EventBuilder;
-
-/** @typedef {{ index: number, value: string }} */
-analytics.EventBuilder.Dimension;
-
-/** @typedef {{ index: number, value: number }} */
-analytics.EventBuilder.Metric;
-
-/** @return {!analytics.EventBuilder} */
-analytics.EventBuilder.builder;
-
-/**
- * @param {string} category
- * @return {!analytics.EventBuilder}
- */
-analytics.EventBuilder.prototype.category;
-
-/**
- * @param {string} action
- * @return {!analytics.EventBuilder}
- */
-analytics.EventBuilder.prototype.action;
-
-/**
- * @param {string} label
- * @return {!analytics.EventBuilder}
- */
-analytics.EventBuilder.prototype.label;
-
-/**
- * @param {number} value
- * @return {!analytics.EventBuilder}
- */
-analytics.EventBuilder.prototype.value;
-
-/**
- * @param {!analytics.EventBuilder.Dimension} dimension
- * @return {!analytics.EventBuilder}
- */
-analytics.EventBuilder.prototype.dimension;
-
-/**
- * @param {!analytics.EventBuilder.Metric} metric
- * @return {!analytics.EventBuilder}
- */
-analytics.EventBuilder.prototype.metric;
-
-/**
- * @param {!analytics.Tracker} tracker
- * @return {!goog.async.Deferred}
- */
-analytics.EventBuilder.prototype.send;
-
-/** @param {!analytics.ParameterMap} parameters */
-analytics.EventBuilder.prototype.collect;
diff --git a/third_party/analytics/google-analytics-bundle.js b/third_party/analytics/google-analytics-bundle.js
deleted file mode 100644
index dd825ac..0000000
--- a/third_party/analytics/google-analytics-bundle.js
+++ /dev/null
@@ -1,90 +0,0 @@
-(function() { 'use strict';var h,aa=aa||{},k=this,m=function(a){return void 0!==a},ba=function(){},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&
-"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},n=function(a){return"array"==ca(a)},da=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},p=function(a){return"string"==typeof a},ea=function(a){return"number"==typeof a},q=function(a){return"function"==ca(a)},r=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},fa=
-function(a,b,c){return a.call.apply(a.bind,arguments)},ga=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}},t=function(a,b,c){t=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?fa:ga;return t.apply(null,arguments)},ha=function(a,b){var c=Array.prototype.slice.call(arguments,
-1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},u=Date.now||function(){return+new Date},v=function(a,b){var c=a.split("."),d=k;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)!c.length&&m(b)?d[e]=b:d=d[e]?d[e]:d[e]={}},w=function(a,b){function c(){}c.prototype=b.prototype;a.P=b.prototype;a.prototype=new c;a.ie=function(a,c,f){for(var g=Array(arguments.length-2),l=2;l<arguments.length;l++)g[l-2]=arguments[l];return b.prototype[c].apply(a,
-g)}};Function.prototype.bind=Function.prototype.bind||function(a,b){if(1<arguments.length){var c=Array.prototype.slice.call(arguments,1);c.unshift(this,a);return t.apply(null,c)}return t(this,a)};var x=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,x);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};w(x,Error);x.prototype.name="CustomError";var ia=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},ja=function(a,b){return a<b?-1:a>b?1:0};var y=Array.prototype,ka=y.indexOf?function(a,b,c){return y.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(p(a))return p(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},la=y.forEach?function(a,b,c){y.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},ma=y.some?function(a,b,c){return y.some.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):
-a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1},na=y.every?function(a,b,c){return y.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=p(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return!1;return!0},pa=function(a){var b;a:{b=oa;for(var c=a.length,d=p(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:p(a)?a.charAt(b):a[b]},qa=function(a,b){var c=ka(a,b),d;(d=0<=c)&&y.splice.call(a,c,1);return d},ra=function(a){return y.concat.apply(y,
-arguments)},sa=function(a,b,c){return 2>=arguments.length?y.slice.call(a,b):y.slice.call(a,b,c)};var ta="StopIteration"in k?k.StopIteration:Error("StopIteration"),ua=function(){};ua.prototype.next=function(){throw ta;};ua.prototype.Sb=function(){return this};var va=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},wa=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},xa=function(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b},ya=function(a,b){var c;a:{for(c in a)if(b.call(void 0,a[c],c,a))break a;c=void 0}return c&&a[c]},za="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Aa=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<za.length;f++)c=
-za[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}},Ba=function(a){var b=arguments.length;if(1==b&&n(arguments[0]))return Ba.apply(null,arguments[0]);for(var c={},d=0;d<b;d++)c[arguments[d]]=!0;return c};var z=function(a,b){this.p={};this.b=[];this.Ja=this.j=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.ha(a)};z.prototype.t=function(){Ca(this);for(var a=[],b=0;b<this.b.length;b++)a.push(this.p[this.b[b]]);return a};z.prototype.H=function(){Ca(this);return this.b.concat()};z.prototype.T=function(a){return A(this.p,a)};
-z.prototype.remove=function(a){return A(this.p,a)?(delete this.p[a],this.j--,this.Ja++,this.b.length>2*this.j&&Ca(this),!0):!1};var Ca=function(a){if(a.j!=a.b.length){for(var b=0,c=0;b<a.b.length;){var d=a.b[b];A(a.p,d)&&(a.b[c++]=d);b++}a.b.length=c}if(a.j!=a.b.length){for(var e={},c=b=0;b<a.b.length;)d=a.b[b],A(e,d)||(a.b[c++]=d,e[d]=1),b++;a.b.length=c}};h=z.prototype;h.get=function(a,b){return A(this.p,a)?this.p[a]:b};
-h.set=function(a,b){A(this.p,a)||(this.j++,this.b.push(a),this.Ja++);this.p[a]=b};h.ha=function(a){var b;a instanceof z?(b=a.H(),a=a.t()):(b=xa(a),a=wa(a));for(var c=0;c<b.length;c++)this.set(b[c],a[c])};h.forEach=function(a,b){for(var c=this.H(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};h.clone=function(){return new z(this)};h.Nb=function(){Ca(this);for(var a={},b=0;b<this.b.length;b++){var c=this.b[b];a[c]=this.p[c]}return a};
-h.Sb=function(a){Ca(this);var b=0,c=this.b,d=this.p,e=this.Ja,f=this,g=new ua;g.next=function(){for(;;){if(e!=f.Ja)throw Error("The map has changed since the iterator was created");if(b>=c.length)throw ta;var g=c[b++];return a?g:d[g]}};return g};var A=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var Da,Ea,Fa={id:"hitType",name:"t",valueType:"text",maxLength:void 0,defaultValue:void 0},Ga={id:"sessionControl",name:"sc",valueType:"text",maxLength:void 0,defaultValue:void 0},Ha={id:"description",name:"cd",valueType:"text",maxLength:2048,defaultValue:void 0},Ia={id:"eventCategory",name:"ec",valueType:"text",maxLength:150,defaultValue:void 0},Ja={id:"eventAction",name:"ea",valueType:"text",maxLength:500,defaultValue:void 0},Ka={id:"eventLabel",name:"el",valueType:"text",maxLength:500,defaultValue:void 0},
-La={id:"eventValue",name:"ev",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ma={zd:Fa,$c:{id:"anonymizeIp",name:"aip",valueType:"boolean",maxLength:void 0,defaultValue:void 0},Kd:{id:"queueTime",name:"qt",valueType:"integer",maxLength:void 0,defaultValue:void 0},fd:{id:"cacheBuster",name:"z",valueType:"text",maxLength:void 0,defaultValue:void 0},Qd:Ga,Rd:{id:"sessionGroup",name:"sg",valueType:"text",maxLength:void 0,defaultValue:void 0},ge:{id:"userId",name:"uid",valueType:"text",maxLength:void 0,
-defaultValue:void 0},Hd:{id:"nonInteraction",name:"ni",valueType:"boolean",maxLength:void 0,defaultValue:void 0},qd:Ha,$d:{id:"title",name:"dt",valueType:"text",maxLength:1500,defaultValue:void 0},bd:{id:"appId",name:"aid",valueType:"text",maxLength:150,defaultValue:void 0},cd:{id:"appInstallerId",name:"aiid",valueType:"text",maxLength:150,defaultValue:void 0},td:Ia,sd:Ja,ud:Ka,vd:La,Td:{id:"socialNetwork",name:"sn",valueType:"text",maxLength:50,defaultValue:void 0},Sd:{id:"socialAction",name:"sa",
-valueType:"text",maxLength:50,defaultValue:void 0},Ud:{id:"socialTarget",name:"st",valueType:"text",maxLength:2048,defaultValue:void 0},ce:{id:"transactionId",name:"ti",valueType:"text",maxLength:500,defaultValue:void 0},be:{id:"transactionAffiliation",name:"ta",valueType:"text",maxLength:500,defaultValue:void 0},de:{id:"transactionRevenue",name:"tr",valueType:"currency",maxLength:void 0,defaultValue:void 0},ee:{id:"transactionShipping",name:"ts",valueType:"currency",maxLength:void 0,defaultValue:void 0},
-fe:{id:"transactionTax",name:"tt",valueType:"currency",maxLength:void 0,defaultValue:void 0},od:{id:"currencyCode",name:"cu",valueType:"text",maxLength:10,defaultValue:void 0},Dd:{id:"itemPrice",name:"ip",valueType:"currency",maxLength:void 0,defaultValue:void 0},Ed:{id:"itemQuantity",name:"iq",valueType:"integer",maxLength:void 0,defaultValue:void 0},Bd:{id:"itemCode",name:"ic",valueType:"text",maxLength:500,defaultValue:void 0},Cd:{id:"itemName",name:"in",valueType:"text",maxLength:500,defaultValue:void 0},
-Ad:{id:"itemCategory",name:"iv",valueType:"text",maxLength:500,defaultValue:void 0},md:{id:"campaignSource",name:"cs",valueType:"text",maxLength:100,defaultValue:void 0},kd:{id:"campaignMedium",name:"cm",valueType:"text",maxLength:50,defaultValue:void 0},ld:{id:"campaignName",name:"cn",valueType:"text",maxLength:100,defaultValue:void 0},jd:{id:"campaignKeyword",name:"ck",valueType:"text",maxLength:500,defaultValue:void 0},gd:{id:"campaignContent",name:"cc",valueType:"text",maxLength:500,defaultValue:void 0},
-hd:{id:"campaignId",name:"ci",valueType:"text",maxLength:100,defaultValue:void 0},yd:{id:"gclid",name:"gclid",valueType:"text",maxLength:void 0,defaultValue:void 0},pd:{id:"dclid",name:"dclid",valueType:"text",maxLength:void 0,defaultValue:void 0},Jd:{id:"pageLoadTime",name:"plt",valueType:"integer",maxLength:void 0,defaultValue:void 0},rd:{id:"dnsTime",name:"dns",valueType:"integer",maxLength:void 0,defaultValue:void 0},Vd:{id:"tcpConnectTime",name:"tcp",valueType:"integer",maxLength:void 0,defaultValue:void 0},
-Pd:{id:"serverResponseTime",name:"srt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Id:{id:"pageDownloadTime",name:"pdt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Ld:{id:"redirectResponseTime",name:"rrt",valueType:"integer",maxLength:void 0,defaultValue:void 0},Wd:{id:"timingCategory",name:"utc",valueType:"text",maxLength:150,defaultValue:void 0},Zd:{id:"timingVar",name:"utv",valueType:"text",maxLength:500,defaultValue:void 0},Yd:{id:"timingValue",name:"utt",valueType:"integer",
-maxLength:void 0,defaultValue:void 0},Xd:{id:"timingLabel",name:"utl",valueType:"text",maxLength:500,defaultValue:void 0},wd:{id:"exDescription",name:"exd",valueType:"text",maxLength:150,defaultValue:void 0},xd:{id:"exFatal",name:"exf",valueType:"boolean",maxLength:void 0,defaultValue:"1"}},Na=function(a){if(1>a||200<a)throw Error("Expected dimension index range 1-200, but was : "+a);return{id:"dimension"+a,name:"cd"+a,valueType:"text",maxLength:150,defaultValue:void 0}},Oa=function(a){if(1>a||200<
-a)throw Error("Expected metric index range 1-200, but was : "+a);return{id:"metric"+a,name:"cm"+a,valueType:"integer",maxLength:void 0,defaultValue:void 0}};var Pa=function(a){if(1>a)return"0";if(3>a)return"1-2";a=Math.floor(Math.log(a-1)/Math.log(2));return Math.pow(2,a)+1+"-"+Math.pow(2,a+1)},Qa=function(a,b){for(var c=0,d=a.length-1,e=0;c<=d;){var f=Math.floor((c+d)/2),e=a[f];if(b<=e){d=0==f?0:a[f-1];if(b>d)return(d+1).toString()+"-"+e.toString();d=f-1}else if(b>e){if(f>=a.length-1)return(a[a.length-1]+1).toString()+"+";c=f+1}}return"<= 0"};var B=function(){this.gb=[]},Ra=function(){return new B};h=B.prototype;h.when=function(a){this.gb.push(a);return this};h.Rb=function(a){var b=arguments;this.when(function(a){return 0<=ka(b,a.ub())});return this};h.Yc=function(a,b){var c=sa(arguments,1);this.when(function(b){b=b.V().get(a);return 0<=ka(c,b)});return this};h.mb=function(a,b){if(r(this.e))throw Error("Filter has already been set.");this.e=r(b)?t(a,b):a;return this};
-h.ia=function(){if(0==this.gb.length)throw Error("Must specify at least one predicate using #when or a helper method.");if(!r(this.e))throw Error("Must specify a delegate filter using #applyFilter.");return t(function(a){na(this.gb,function(b){return b(a)})&&this.e(a)},this)};var C=function(){this.lb=!1;this.zb="";this.Kb=!1;this.ya=null};C.prototype.Wb=function(a){this.lb=!0;this.zb=a||" - ";return this};C.prototype.Sc=function(){this.Kb=!0;return this};C.prototype.Dc=function(){return Sa(this,Pa)};C.prototype.Ec=function(a){return Sa(this,ha(Qa,a))};
-var Sa=function(a,b){if(null!=a.ya)throw Error("LabelerBuilder: Only one labeling strategy may be used.");a.ya=t(function(a){var d=a.V().get(La),e=a.V().get(Ka);ea(d)&&(d=b(d),null!=e&&this.lb&&(d=e+this.zb+d),a.V().set(Ka,d))},a);return a};C.prototype.ia=function(){if(null==this.ya)throw Error("LabelerBuilder: a labeling strategy must be specified prior to calling build().");return Ra().Rb("event").mb(t(function(a){this.ya(a);this.Kb&&a.V().remove(La)},this)).ia()};var Ua=function(a,b){var c=Array.prototype.slice.call(arguments),d=c.shift();if("undefined"==typeof d)throw Error("[goog.string.format] Template required");return d.replace(/%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g,function(a,b,d,l,D,N,Y,Z){if("%"==N)return"%";var Nb=c.shift();if("undefined"==typeof Nb)throw Error("[goog.string.format] Not enough arguments");arguments[0]=Nb;return Ta[N].apply(null,arguments)})},Ta={s:function(a,b,c){return isNaN(c)||""==c||a.length>=c?a:a=-1<b.indexOf("-",0)?a+Array(c-
-a.length+1).join(" "):Array(c-a.length+1).join(" ")+a},f:function(a,b,c,d,e){d=a.toString();isNaN(e)||""==e||(d=parseFloat(a).toFixed(e));var f;f=0>a?"-":0<=b.indexOf("+")?"+":0<=b.indexOf(" ")?" ":"";0<=a&&(d=f+d);if(isNaN(c)||d.length>=c)return d;d=isNaN(e)?Math.abs(a).toString():Math.abs(a).toFixed(e);a=c-d.length-f.length;return d=0<=b.indexOf("-",0)?f+d+Array(a+1).join(" "):f+Array(a+1).join(0<=b.indexOf("0",0)?"0":" ")+d},d:function(a,b,c,d,e,f,g,l){return Ta.f(parseInt(a,10),b,c,d,0,f,g,l)}};
-Ta.i=Ta.d;Ta.u=Ta.d;var Va=function(a){if("function"==typeof a.t)return a.t();if(p(a))return a.split("");if(da(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return wa(a)},Wa=function(a,b){if("function"==typeof a.forEach)a.forEach(b,void 0);else if(da(a)||p(a))la(a,b,void 0);else{var c;if("function"==typeof a.H)c=a.H();else if("function"!=typeof a.t)if(da(a)||p(a)){c=[];for(var d=a.length,e=0;e<d;e++)c.push(e)}else c=xa(a);else c=void 0;for(var d=Va(a),e=d.length,f=0;f<e;f++)b.call(void 0,d[f],c&&c[f],
-a)}};var E=function(a){this.w=new z;if(0<arguments.length%2)throw Error("Uneven number of arguments to ParameterMap constructor.");for(var b=arguments,c=0;c<b.length;c+=2)this.set(b[c],b[c+1])};E.prototype.set=function(a,b){if(null==b)throw Error("undefined-or-null value for key: "+a.name);this.w.set(a.name,{key:a,value:b})};E.prototype.remove=function(a){this.w.remove(a.name)};E.prototype.get=function(a){a=this.w.get(a.name,null);return null===a?null:a.value};E.prototype.ha=function(a){this.w.ha(a.w)};
-var Xa=function(a,b){la(a.w.t(),function(a){b(a.key,a.value)})};E.prototype.Nb=function(){var a={};Xa(this,function(b,c){a[b.id]=c});return a};E.prototype.clone=function(){var a=new E;a.w=this.w.clone();return a};E.prototype.toString=function(){var a={};Xa(this,function(b,c){a[b.id]=c});return JSON.stringify(a)};var F=function(a){this.e=a};h=F.prototype;h.Yb=function(a){var b=new F(t(this.M,this));b.I=Ia;b.Q=a;return b};h.action=function(a){var b=new F(t(this.M,this));b.I=Ja;b.Q=a;return b};h.label=function(a){var b=new F(t(this.M,this));b.I=Ka;b.Q=a;return b};h.value=function(a){var b=new F(t(this.M,this));b.I=La;b.Q=a;return b};h.fc=function(a){var b=new F(t(this.M,this));b.I=Na(a.index);b.Q=a.value;return b};h.vc=function(a){var b=new F(t(this.M,this));b.I=Oa(a.index);b.Q=a.value;return b};
-h.send=function(a){var b=new E;this.M(b);return a.send("event",b)};h.M=function(a){null!=this.I&&null!=this.Q&&!a.w.T(this.I.name)&&a.set(this.I,this.Q);r(this.e)&&this.e(a)};var Ya=new F(ba);var G=function(){this.aa=this.aa;this.Ca=this.Ca};G.prototype.aa=!1;G.prototype.ma=function(){this.aa||(this.aa=!0,this.o())};G.prototype.o=function(){if(this.Ca)for(;this.Ca.length;)this.Ca.shift()()};var Za=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.X=!1;this.Hb=!0};Za.prototype.preventDefault=function(){this.defaultPrevented=!0;this.Hb=!1};var $a=function(a){$a[" "](a);return a};$a[" "]=ba;var H;a:{var ab=k.navigator;if(ab){var bb=ab.userAgent;if(bb){H=bb;break a}}H=""};var cb=function(){return-1!=H.indexOf("Edge")||-1!=H.indexOf("Trident")||-1!=H.indexOf("MSIE")};var I=function(){return-1!=H.indexOf("Edge")};var db=-1!=H.indexOf("Opera")||-1!=H.indexOf("OPR"),J=cb(),eb=-1!=H.indexOf("Gecko")&&!(-1!=H.toLowerCase().indexOf("webkit")&&!I())&&!(-1!=H.indexOf("Trident")||-1!=H.indexOf("MSIE"))&&!I(),fb=-1!=H.toLowerCase().indexOf("webkit")&&!I(),gb=function(){var a=H;if(eb)return/rv\:([^\);]+)(\)|;)/.exec(a);if(J&&I())return/Edge\/([\d\.]+)/.exec(a);if(J)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(fb)return/WebKit\/(\S+)/.exec(a)},hb=function(){var a=k.document;return a?a.documentMode:void 0},ib=
-function(){if(db&&k.opera){var a=k.opera.version;return q(a)?a():a}var a="",b=gb();b&&(a=b?b[1]:"");return J&&!I()&&(b=hb(),b>parseFloat(a))?String(b):a}(),jb={},K=function(a){var b;if(!(b=jb[a])){b=0;for(var c=ia(String(ib)).split("."),d=ia(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",l=d[f]||"",D=/(\d*)(\D*)/g,N=/(\d*)(\D*)/g;do{var Y=D.exec(g)||["","",""],Z=N.exec(l)||["","",""];if(0==Y[0].length&&0==Z[0].length)break;b=ja(0==Y[1].length?0:parseInt(Y[1],
-10),0==Z[1].length?0:parseInt(Z[1],10))||ja(0==Y[2].length,0==Z[2].length)||ja(Y[2],Z[2])}while(0==b)}b=jb[a]=0<=b}return b},kb=k.document,lb=hb(),mb=!kb||!J||!lb&&I()?void 0:lb||("CSS1Compat"==kb.compatMode?parseInt(ib,10):5);var nb=!J||J&&(I()||9<=mb),ob=J&&!K("9"),pb=!fb||K("528"),qb=eb&&K("1.9b")||J&&K("8")||db&&K("9.5")||fb&&K("528"),rb=eb&&!K("8")||J&&!K("9");var sb=function(a,b){Za.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.sb=this.state=null;if(a){var c=this.type=a.type;this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(eb){var e;a:{try{$a(d.nodeName);e=!0;break a}catch(f){}e=!1}e||(d=null)}}else"mouseover"==
-c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=fb||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=fb||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;
-this.metaKey=a.metaKey;this.state=a.state;this.sb=a;a.defaultPrevented&&this.preventDefault()}};w(sb,Za);sb.prototype.preventDefault=function(){sb.P.preventDefault.call(this);var a=this.sb;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,ob)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var tb="closure_listenable_"+(1E6*Math.random()|0),ub=function(a){return!(!a||!a[tb])},vb=0;var wb=function(a,b,c,d,e){this.O=a;this.proxy=null;this.src=b;this.type=c;this.ka=!!d;this.sa=e;this.key=++vb;this.removed=this.ja=!1},xb=function(a){a.removed=!0;a.O=null;a.proxy=null;a.src=null;a.sa=null};var L=function(a){this.src=a;this.k={};this.fa=0};L.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.k[f];a||(a=this.k[f]=[],this.fa++);var g=yb(a,b,d,e);-1<g?(b=a[g],c||(b.ja=!1)):(b=new wb(b,this.src,f,!!d,e),b.ja=c,a.push(b));return b};L.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.k))return!1;var e=this.k[a];b=yb(e,b,c,d);return-1<b?(xb(e[b]),y.splice.call(e,b,1),0==e.length&&(delete this.k[a],this.fa--),!0):!1};
-var zb=function(a,b){var c=b.type;if(!(c in a.k))return!1;var d=qa(a.k[c],b);d&&(xb(b),0==a.k[c].length&&(delete a.k[c],a.fa--));return d};L.prototype.removeAll=function(a){a=a&&a.toString();var b=0,c;for(c in this.k)if(!a||c==a){for(var d=this.k[c],e=0;e<d.length;e++)++b,xb(d[e]);delete this.k[c];this.fa--}return b};L.prototype.ba=function(a,b,c,d){a=this.k[a.toString()];var e=-1;a&&(e=yb(a,b,c,d));return-1<e?a[e]:null};
-var yb=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.O==b&&f.ka==!!c&&f.sa==d)return e}return-1};var Ab="closure_lm_"+(1E6*Math.random()|0),Bb={},Cb=0,Db=function(a,b,c,d,e){if(n(b)){for(var f=0;f<b.length;f++)Db(a,b[f],c,d,e);return null}c=Eb(c);return ub(a)?a.listen(b,c,d,e):Fb(a,b,c,!1,d,e)},Fb=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var g=!!e,l=Gb(a);l||(a[Ab]=l=new L(a));c=l.add(b,c,d,e,f);if(c.proxy)return c;d=Hb();c.proxy=d;d.src=a;d.O=c;a.addEventListener?a.addEventListener(b.toString(),d,g):a.attachEvent(Ib(b.toString()),d);Cb++;return c},Hb=function(){var a=Jb,
-b=nb?function(c){return a.call(b.src,b.O,c)}:function(c){c=a.call(b.src,b.O,c);if(!c)return c};return b},Kb=function(a,b,c,d,e){if(n(b)){for(var f=0;f<b.length;f++)Kb(a,b[f],c,d,e);return null}c=Eb(c);return ub(a)?a.cb(b,c,d,e):Fb(a,b,c,!0,d,e)},Lb=function(a,b,c,d,e){if(n(b))for(var f=0;f<b.length;f++)Lb(a,b[f],c,d,e);else c=Eb(c),ub(a)?a.jb(b,c,d,e):a&&(a=Gb(a))&&(b=a.ba(b,c,!!d,e))&&Mb(b)},Mb=function(a){if(ea(a)||!a||a.removed)return!1;var b=a.src;if(ub(b))return zb(b.B,a);var c=a.type,d=a.proxy;
-b.removeEventListener?b.removeEventListener(c,d,a.ka):b.detachEvent&&b.detachEvent(Ib(c),d);Cb--;(c=Gb(b))?(zb(c,a),0==c.fa&&(c.src=null,b[Ab]=null)):xb(a);return!0},Ib=function(a){return a in Bb?Bb[a]:Bb[a]="on"+a},Pb=function(a,b,c,d){var e=!0;if(a=Gb(a))if(b=a.k[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.ka==c&&!f.removed&&(f=Ob(f,d),e=e&&!1!==f)}return e},Ob=function(a,b){var c=a.O,d=a.sa||a.src;a.ja&&Mb(a);return c.call(d,b)},Jb=function(a,b){if(a.removed)return!0;if(!nb){var c;
-if(!(c=b))a:{c=["window","event"];for(var d=k,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break a}c=d}e=c;c=new sb(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,l=e.length-1;!c.X&&0<=l;l--){c.currentTarget=e[l];var D=Pb(e[l],f,!0,c),d=d&&D}for(l=0;!c.X&&l<e.length;l++)c.currentTarget=e[l],D=Pb(e[l],f,!1,
-c),d=d&&D}return d}return Ob(a,new sb(b,this))},Gb=function(a){a=a[Ab];return a instanceof L?a:null},Qb="__closure_events_fn_"+(1E9*Math.random()>>>0),Eb=function(a){if(q(a))return a;a[Qb]||(a[Qb]=function(b){return a.handleEvent(b)});return a[Qb]};var M=function(){G.call(this);this.B=new L(this);this.Tb=this;this.fb=null};w(M,G);M.prototype[tb]=!0;h=M.prototype;h.addEventListener=function(a,b,c,d){Db(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){Lb(this,a,b,c,d)};
-h.dispatchEvent=function(a){var b,c=this.fb;if(c){b=[];for(var d=1;c;c=c.fb)b.push(c),++d}c=this.Tb;d=a.type||a;if(p(a))a=new Za(a,c);else if(a instanceof Za)a.target=a.target||c;else{var e=a;a=new Za(d,c);Aa(a,e)}var e=!0,f;if(b)for(var g=b.length-1;!a.X&&0<=g;g--)f=a.currentTarget=b[g],e=Rb(f,d,!0,a)&&e;a.X||(f=a.currentTarget=c,e=Rb(f,d,!0,a)&&e,a.X||(e=Rb(f,d,!1,a)&&e));if(b)for(g=0;!a.X&&g<b.length;g++)f=a.currentTarget=b[g],e=Rb(f,d,!1,a)&&e;return e};
-h.o=function(){M.P.o.call(this);this.B&&this.B.removeAll(void 0);this.fb=null};h.listen=function(a,b,c,d){return this.B.add(String(a),b,!1,c,d)};h.cb=function(a,b,c,d){return this.B.add(String(a),b,!0,c,d)};h.jb=function(a,b,c,d){return this.B.remove(String(a),b,c,d)};var Rb=function(a,b,c,d){b=a.B.k[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var g=b[f];if(g&&!g.removed&&g.ka==c){var l=g.O,D=g.sa||g.src;g.ja&&zb(a.B,g);e=!1!==l.call(D,d)&&e}}return e&&0!=d.Hb};
-M.prototype.ba=function(a,b,c,d){return this.B.ba(String(a),b,c,d)};var Sb=function(a,b,c){this.tc=c;this.dc=a;this.Gc=b;this.Ba=0;this.ta=null};Sb.prototype.get=function(){var a;0<this.Ba?(this.Ba--,a=this.ta,this.ta=a.next,a.next=null):a=this.dc();return a};Sb.prototype.put=function(a){this.Gc(a);this.Ba<this.tc&&(this.Ba++,a.next=this.ta,this.ta=a)};var Tb=function(a){k.setTimeout(function(){throw a;},0)},Ub,Vb=function(){var a=k.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&-1==H.indexOf("Presto")&&(a=function(){var a=document.createElement("IFRAME");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+
-"//"+b.location.host,a=t(function(a){if(("*"==d||a.origin==d)&&a.data==c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!cb()){var b=new a,c={},d=c;b.port1.onmessage=function(){if(m(c.next)){c=c.next;var a=c.ob;c.ob=null;a()}};return function(a){d.next={ob:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("SCRIPT")?
-function(a){var b=document.createElement("SCRIPT");b.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){k.setTimeout(a,0)}};var Wb=function(){this.Ka=this.Z=null},Yb=new Sb(function(){return new Xb},function(a){a.reset()},100);Wb.prototype.add=function(a,b){var c=Yb.get();c.set(a,b);this.Ka?this.Ka.next=c:this.Z=c;this.Ka=c};Wb.prototype.remove=function(){var a=null;this.Z&&(a=this.Z,this.Z=this.Z.next,this.Z||(this.Ka=null),a.next=null);return a};var Xb=function(){this.next=this.scope=this.Wa=null};Xb.prototype.set=function(a,b){this.Wa=a;this.scope=b;this.next=null};
-Xb.prototype.reset=function(){this.next=this.scope=this.Wa=null};var cc=function(a,b){Zb||$b();ac||(Zb(),ac=!0);bc.add(a,b)},Zb,$b=function(){if(k.Promise&&k.Promise.resolve){var a=k.Promise.resolve();Zb=function(){a.then(dc)}}else Zb=function(){var a=dc;!q(k.setImmediate)||k.Window&&k.Window.prototype&&k.Window.prototype.setImmediate==k.setImmediate?(Ub||(Ub=Vb()),Ub(a)):k.setImmediate(a)}},ac=!1,bc=new Wb,dc=function(){for(var a=null;a=bc.remove();){try{a.Wa.call(a.scope)}catch(b){Tb(b)}Yb.put(a)}ac=!1};var ec=function(a){a.prototype.then=a.prototype.then;a.prototype.$goog_Thenable=!0},fc=function(a){if(!a)return!1;try{return!!a.$goog_Thenable}catch(b){return!1}};var P=function(a,b){this.m=0;this.D=void 0;this.S=this.G=this.l=null;this.ra=this.Va=!1;if(a==gc)O(this,2,b);else try{var c=this;a.call(b,function(a){O(c,2,a)},function(a){O(c,3,a)})}catch(d){O(this,3,d)}},hc=function(){this.next=this.context=this.da=this.Da=this.L=null;this.Na=!1};hc.prototype.reset=function(){this.context=this.da=this.Da=this.L=null;this.Na=!1};
-var ic=new Sb(function(){return new hc},function(a){a.reset()},100),jc=function(a,b,c){var d=ic.get();d.Da=a;d.da=b;d.context=c;return d},gc=function(){};P.prototype.then=function(a,b,c){return kc(this,q(a)?a:null,q(b)?b:null,c)};ec(P);P.prototype.cancel=function(a){0==this.m&&cc(function(){var b=new lc(a);mc(this,b)},this)};
-var mc=function(a,b){if(0==a.m)if(a.l){var c=a.l;if(c.G){for(var d=0,e=null,f=null,g=c.G;g&&(g.Na||(d++,g.L==a&&(e=g),!(e&&1<d)));g=g.next)e||(f=g);e&&(0==c.m&&1==d?mc(c,b):(f?(d=f,d.next==c.S&&(c.S=d),d.next=d.next.next):nc(c),oc(c,e,3,b)))}a.l=null}else O(a,3,b)},qc=function(a,b){a.G||2!=a.m&&3!=a.m||pc(a);a.S?a.S.next=b:a.G=b;a.S=b},kc=function(a,b,c,d){var e=jc(null,null,null);e.L=new P(function(a,g){e.Da=b?function(c){try{var e=b.call(d,c);a(e)}catch(N){g(N)}}:a;e.da=c?function(b){try{var e=
-c.call(d,b);!m(e)&&b instanceof lc?g(b):a(e)}catch(N){g(N)}}:g});e.L.l=a;qc(a,e);return e.L};P.prototype.Pb=function(a){this.m=0;O(this,2,a)};P.prototype.Qb=function(a){this.m=0;O(this,3,a)};
-var O=function(a,b,c){if(0==a.m){if(a==c)b=3,c=new TypeError("Promise cannot resolve to itself");else{if(fc(c)){a.m=1;b=c;c=a.Pb;var d=a.Qb;b instanceof P?qc(b,jc(c||ba,d||null,a)):b.then(c,d,a);return}if(r(c))try{if(d=c.then,q(d)){rc(a,c,d);return}}catch(e){b=3,c=e}}a.D=c;a.m=b;a.l=null;pc(a);3!=b||c instanceof lc||sc(a,c)}},rc=function(a,b,c){a.m=1;var d=!1,e=function(b){d||(d=!0,a.Pb(b))},f=function(b){d||(d=!0,a.Qb(b))};try{c.call(b,e,f)}catch(g){f(g)}},pc=function(a){a.Va||(a.Va=!0,cc(a.gc,a))},
-nc=function(a){var b=null;a.G&&(b=a.G,a.G=b.next,b.next=null);a.G||(a.S=null);return b};P.prototype.gc=function(){for(var a=null;a=nc(this);)oc(this,a,this.m,this.D);this.Va=!1};var oc=function(a,b,c,d){b.L&&(b.L.l=null);if(2==c)b.Da.call(b.context,d);else if(null!=b.da){if(!b.Na)for(;a&&a.ra;a=a.l)a.ra=!1;b.da.call(b.context,d)}ic.put(b)},sc=function(a,b){a.ra=!0;cc(function(){a.ra&&tc.call(null,b)})},tc=Tb,lc=function(a){x.call(this,a)};w(lc,x);lc.prototype.name="cancel";/*
- Portions of this code are from MochiKit, received by
- The Closure Authors under the MIT license. All other code is Copyright
- 2005-2009 The Closure Authors. All Rights Reserved.
-*/
-var Q=function(a,b){this.Fa=[];this.Cb=a;this.rb=b||null;this.ca=this.C=!1;this.D=void 0;this.hb=this.Xb=this.Oa=!1;this.Ia=0;this.l=null;this.Qa=0};Q.prototype.cancel=function(a){if(this.C)this.D instanceof Q&&this.D.cancel();else{if(this.l){var b=this.l;delete this.l;a?b.cancel(a):(b.Qa--,0>=b.Qa&&b.cancel())}this.Cb?this.Cb.call(this.rb,this):this.hb=!0;this.C||this.A(new uc)}};Q.prototype.qb=function(a,b){this.Oa=!1;vc(this,a,b)};
-var vc=function(a,b,c){a.C=!0;a.D=c;a.ca=!b;wc(a)},yc=function(a){if(a.C){if(!a.hb)throw new xc;a.hb=!1}};Q.prototype.v=function(a){yc(this);vc(this,!0,a)};Q.prototype.A=function(a){yc(this);vc(this,!1,a)};Q.prototype.n=function(a,b){return zc(this,a,null,b)};var zc=function(a,b,c,d){a.Fa.push([b,c,d]);a.C&&wc(a);return a};Q.prototype.then=function(a,b,c){var d,e,f=new P(function(a,b){d=a;e=b});zc(this,d,function(a){a instanceof uc?f.cancel():e(a)});return f.then(a,b,c)};ec(Q);
-var Ac=function(a){var b=new Q;zc(a,b.v,b.A,b);return b},Bc=function(a){return ma(a.Fa,function(a){return q(a[1])})},wc=function(a){if(a.Ia&&a.C&&Bc(a)){var b=a.Ia,c=Cc[b];c&&(k.clearTimeout(c.ua),delete Cc[b]);a.Ia=0}a.l&&(a.l.Qa--,delete a.l);for(var b=a.D,d=c=!1;a.Fa.length&&!a.Oa;){var e=a.Fa.shift(),f=e[0],g=e[1],e=e[2];if(f=a.ca?g:f)try{var l=f.call(e||a.rb,b);m(l)&&(a.ca=a.ca&&(l==b||l instanceof Error),a.D=b=l);fc(b)&&(d=!0,a.Oa=!0)}catch(D){b=D,a.ca=!0,Bc(a)||(c=!0)}}a.D=b;d&&(l=t(a.qb,a,
-!0),d=t(a.qb,a,!1),b instanceof Q?(zc(b,l,d),b.Xb=!0):b.then(l,d));c&&(b=new Dc(b),Cc[b.ua]=b,a.Ia=b.ua)},Ec=function(a){var b=new Q;b.v(a);return b},Gc=function(){var a=Fc,b=new Q;b.A(a);return b},xc=function(){x.call(this)};w(xc,x);xc.prototype.message="Deferred has already fired";xc.prototype.name="AlreadyCalledError";var uc=function(){x.call(this)};w(uc,x);uc.prototype.message="Deferred was canceled";uc.prototype.name="CanceledError";
-var Dc=function(a){this.ua=k.setTimeout(t(this.Tc,this),0);this.na=a};Dc.prototype.Tc=function(){delete Cc[this.ua];throw this.na;};var Cc={};var Hc=function(a){this.qa=[];this.e=a};Hc.prototype.R=function(a){if(!q(a))throw Error("Invalid filter. Must be a function.");this.qa.push(a)};Hc.prototype.send=function(a,b){if(0==this.qa.length)return this.e.send(a,b);var c=new R(a,b);return Ic(this,0,c).n(function(){if(!c.Sa)return this.e.send(a,b)},this)};var Ic=function(a,b,c){return Ec().n(function(){return this.qa[b](c)},a).n(function(){if(++b<this.qa.length&&!c.Sa)return Ic(this,b,c)},a)},R=function(a,b){this.Wc=a;this.Cc=b;this.Sa=!1};
-R.prototype.ub=function(){return this.Wc};R.prototype.V=function(){return this.Cc};R.prototype.cancel=function(){this.Sa=!0};Ba("area base br col command embed hr img input keygen link meta param source track wbr".split(" "));var Jc=function(a,b){this.width=a;this.height=b};Jc.prototype.clone=function(){return new Jc(this.width,this.height)};Jc.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};!eb&&!J||J&&J&&(I()||9<=mb)||eb&&K("1.9.1");J&&K("9");var Kc={id:"apiVersion",name:"v",valueType:"text",maxLength:void 0,defaultValue:void 0},Lc={id:"appName",name:"an",valueType:"text",maxLength:100,defaultValue:void 0},Mc={id:"appVersion",name:"av",valueType:"text",maxLength:100,defaultValue:void 0},Nc={id:"clientId",name:"cid",valueType:"text",maxLength:void 0,defaultValue:void 0},Oc={id:"language",name:"ul",valueType:"text",maxLength:20,defaultValue:void 0},Pc={id:"libVersion",name:"_v",valueType:"text",maxLength:void 0,defaultValue:void 0},Qc={id:"sampleRateOverride",
-name:"usro",valueType:"integer",maxLength:void 0,defaultValue:void 0},Rc={id:"screenColors",name:"sd",valueType:"text",maxLength:20,defaultValue:void 0},Sc={id:"screenResolution",name:"sr",valueType:"text",maxLength:20,defaultValue:void 0},Tc={id:"trackingId",name:"tid",valueType:"text",maxLength:void 0,defaultValue:void 0},Uc={id:"viewportSize",name:"vp",valueType:"text",maxLength:20,defaultValue:void 0},Vc={ad:Kc,dd:Lc,ed:Mc,nd:Nc,Fd:Oc,Gd:Pc,Md:Qc,Nd:Rc,Od:Sc,ae:Tc,he:Uc},Xc=function(a){if(!p(a))return a;
-var b=Wc(a,Ma);if(r(b))return b;b=Wc(a,Vc);if(r(b))return b;b=/^dimension(\d+)$/.exec(a);if(null!==b)return Na(parseInt(b[1],10));b=/^metric(\d+)$/.exec(a);if(null!==b)return Oa(parseInt(b[1],10));throw Error(a+" is not a valid parameter name.");},Wc=function(a,b){var c=ya(b,function(b){return b.id==a&&"metric"!=a&&"dimension"!=a});return r(c)?c:null};var S=function(a,b){this.ac=b;this.q=b.Xa();this.Fb=new E;this.ib=!1};h=S.prototype;h.set=function(a,b){if(null==b)throw Error("Value must be defined and not null. Parameter="+a.id);var c=Xc(a);this.Fb.set(c,b)};h.R=function(a){this.ac.R(a)};h.send=function(a,b){if(a instanceof F)return a.send(this);var c=this.Fb.clone();b instanceof E?c.ha(b):r(b)&&va(b,function(a,b){null!=a&&c.set(Xc(b),a)},this);this.ib&&(this.ib=!1,c.set(Ga,"start"));return this.q.send(a,c)};
-h.Hc=function(a){var b={description:a};this.set(Ha,a);return this.send("appview",b)};h.Ic=function(a,b,c,d){return this.send("event",{eventCategory:a,eventAction:b,eventLabel:c,eventValue:d})};h.Kc=function(a,b,c){return this.send("social",{socialNetwork:a,socialAction:b,socialTarget:c})};h.Jc=function(a,b){return this.send("exception",{exDescription:a,exFatal:b})};h.Ib=function(a,b,c,d,e){return this.send("timing",{timingCategory:a,timingVar:b,timingLabel:d,timingValue:c,sampleRateOverride:e})};
-h.jc=function(){this.ib=!0};h.Rc=function(a,b,c,d){return new Yc(this,a,b,c,d)};var Yc=function(a,b,c,d,e){this.Ob=a;this.Zb=b;this.Xc=c;this.rc=d;this.Ea=e;this.Qc=u()};Yc.prototype.send=function(){var a=this.Ob.Ib(this.Zb,this.Xc,u()-this.Qc,this.rc,this.Ea);this.Ob=null;return a};var Zc=function(a,b,c,d,e){this.sc=a;this.Ub=b;this.Vb=c;this.g=d;this.$b=e};
-Zc.prototype.mc=function(a){var b=new S(0,this.$b.create());b.set(Pc,this.sc);b.set(Kc,1);b.set(Lc,this.Ub);b.set(Mc,this.Vb);b.set(Tc,a);(a=navigator.language||navigator.browserLanguage)&&b.set(Oc,a);(a=screen.colorDepth+"-bit")&&b.set(Rc,a);(a=[screen.width,screen.height].join("x"))&&b.set(Sc,a);a=window.document;a="CSS1Compat"==a.compatMode?a.documentElement:a.body;a=new Jc(a.clientWidth,a.clientHeight);(a=[a.width,a.height].join("x"))&&b.set(Uc,a);return b};Zc.prototype.kc=function(){return Ac(this.g.ea)};var $c=function(a,b,c,d,e,f){Q.call(this,e,f);this.bb=a;this.Ta=[];this.tb=!!b;this.ic=!!c;this.cc=!!d;for(b=this.Bb=0;b<a.length;b++)zc(a[b],t(this.vb,this,b,!0),t(this.vb,this,b,!1));0!=a.length||this.tb||this.v(this.Ta)};w($c,Q);$c.prototype.vb=function(a,b,c){this.Bb++;this.Ta[a]=[b,c];this.C||(this.tb&&b?this.v([a,c]):this.ic&&!b?this.A(c):this.Bb==this.bb.length&&this.v(this.Ta));this.cc&&!b&&(c=null);return c};$c.prototype.A=function(a){$c.P.A.call(this,a);for(a=0;a<this.bb.length;a++)this.bb[a].cancel()};
-var ad=function(a){return(new $c(a,!1,!0)).n(function(a){for(var c=[],d=0;d<a.length;d++)c[d]=a[d][1];return c})};var bd=function(){for(var a="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split(""),b=0,c=a.length;b<c;b++)switch(a[b]){case "x":a[b]=Math.floor(16*Math.random()).toString(16);break;case "y":a[b]=(Math.floor(4*Math.random())+8).toString(16)}return a.join("")};var T=function(a){this.J=a;this.Ea=100;this.pb=[];this.W=this.ga=null;this.ea=cd(this);this.ea.n(function(){this.Jb=Db(this.J,"a",t(this.nc,this))},this)},cd=function(a){return dd(a).n(function(){return this},a)},dd=function(a){return ad([ed(a),fd(a)])};T.prototype.nc=function(){U(this);var a=gd(this),b=this.xa();dd(this).n(function(){a!=gd(this)&&hd(this,"analytics.user-id");b!=this.xa()&&hd(this,"analytics.tracking-permitted")},this)};var id=function(a,b){U(a);a.pb.push(b)};
-T.prototype.Oc=function(a){U(this);var b=this.W!=a;this.W=a;this.J.set("analytics.tracking-permitted",a.toString());b&&hd(this,"analytics.tracking-permitted")};T.prototype.xa=function(){U(this);var a;if(a=this.W)a=k._gaUserPrefs,a=!(a&&a.ioo&&a.ioo());return a};
-var ed=function(a){return a.J.get("analytics.tracking-permitted").n(function(a){this.W=!0;if(m(a))switch(a){case "true":this.W=!0;break;case "false":this.W=!1}},a)},gd=function(a){U(a);if(!p(a.ga))throw Error("Invalid state. UserID is not a string.");return a.ga},fd=function(a){return a.J.get("analytics.user-id").n(function(a){m(a)?this.ga=a:jd(this)},a)},jd=function(a){a.ga=bd();return a.J.set("analytics.user-id",a.ga).n(function(){hd(this,"analytics.user-id")},a)};
-T.prototype.Nc=function(a){U(this);this.Ea=a};var kd=function(a){U(a);return a.Ea};T.prototype.Fc=function(){return jd(this)};var hd=function(a,b){la(a.pb,function(a){a(b)})};T.prototype.ma=function(){null!=this.Jb&&Mb(this.Jb)};var U=function(a){if(!Ac(a.ea).C)throw Error("Settings object accessed prior to entering ready state.");};var ld=function(){M.call(this);this.eb="google-analytics";this.J=chrome.storage.local;chrome.storage.onChanged.addListener(t(this.Ac,this))};w(ld,M);ld.prototype.Ac=function(a){md(this,a)&&this.dispatchEvent("a")};var md=function(a,b){return ma(xa(b),function(a){return 0==a.lastIndexOf(this.eb,0)},a)};ld.prototype.get=function(a){var b=new Q,c=this.eb+"."+a;this.J.get(c,function(a){chrome.runtime.lastError?b.A(chrome.runtime.lastError):(a=a[c],b.v(null!=a?a.toString():void 0))});return b};
-ld.prototype.set=function(a,b){var c=new Q,d={};d[this.eb+"."+a]=b;this.J.set(d,function(){chrome.runtime.lastError?c.A(chrome.runtime.lastError):c.v()});return c};var V=function(){};V.lc=function(){return V.yb?V.yb:V.yb=new V};V.prototype.send=function(){return Ec()};var nd=function(a){this.ec=a};nd.prototype.send=function(a,b){this.ec.push({pc:a,Bc:b});return Ec()};var od=function(a,b,c){this.g=a;this.U=[];this.K={enabled:new nd(this.U),disabled:c};this.q=this.K.enabled;zc(Ac(this.g.ea),ha(this.zc,b),this.yc,this)};od.prototype.zc=function(a){if(null===this.U)throw Error("Channel setup already completed.");this.K.enabled=a();pd(this);la(this.U,function(a){this.send(a.pc,a.Bc)},this);this.U=null;id(this.g,t(this.xc,this))};
-od.prototype.yc=function(){if(null===this.U)throw Error("Channel setup already completed.");this.q=this.K.enabled=this.K.disabled;this.U=null};od.prototype.send=function(a,b){return this.q.send(a,b)};var pd=function(a){a.q=a.g.xa()?a.K.enabled:a.K.disabled};od.prototype.xc=function(a){switch(a){case "analytics.tracking-permitted":pd(this)}};var qd=function(a,b){this.Ra=[];var c=t(function(){this.pa=new Hc(b.Xa());la(this.Ra,function(a){this.pa.R(a)},this);this.Ra=null;return this.pa},this);this.q=new od(a,c,V.lc())};qd.prototype.Xa=function(){return this.q};qd.prototype.R=function(a){this.pa?this.pa.R(a):this.Ra.push(a)};var rd=function(a,b){this.g=a;this.Pc=b};rd.prototype.create=function(){return new qd(this.g,this.Pc)};var sd=function(a,b){M.call(this);this.wa=a||1;this.Y=b||k;this.Pa=t(this.Uc,this);this.ab=u()};w(sd,M);h=sd.prototype;h.enabled=!1;h.h=null;h.Uc=function(){if(this.enabled){var a=u()-this.ab;0<a&&a<.8*this.wa?this.h=this.Y.setTimeout(this.Pa,this.wa-a):(this.h&&(this.Y.clearTimeout(this.h),this.h=null),this.dispatchEvent("tick"),this.enabled&&(this.h=this.Y.setTimeout(this.Pa,this.wa),this.ab=u()))}};h.start=function(){this.enabled=!0;this.h||(this.h=this.Y.setTimeout(this.Pa,this.wa),this.ab=u())};
-h.stop=function(){this.enabled=!1;this.h&&(this.Y.clearTimeout(this.h),this.h=null)};h.o=function(){sd.P.o.call(this);this.stop();delete this.Y};var td=function(a,b,c){if(q(a))c&&(a=t(a,c));else if(a&&"function"==typeof a.handleEvent)a=t(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647<b?-1:k.setTimeout(a,b||0)};var W=function(a){G.call(this);this.Ya=a;this.b={}};w(W,G);var ud=[];W.prototype.listen=function(a,b,c,d){n(b)||(b&&(ud[0]=b.toString()),b=ud);for(var e=0;e<b.length;e++){var f=Db(a,b[e],c||this.handleEvent,d||!1,this.Ya||this);if(!f)break;this.b[f.key]=f}return this};W.prototype.cb=function(a,b,c,d){return vd(this,a,b,c,d)};var vd=function(a,b,c,d,e,f){if(n(c))for(var g=0;g<c.length;g++)vd(a,b,c[g],d,e,f);else{b=Kb(b,c,d||a.handleEvent,e,f||a.Ya||a);if(!b)return a;a.b[b.key]=b}return a};
-W.prototype.jb=function(a,b,c,d,e){if(n(b))for(var f=0;f<b.length;f++)this.jb(a,b[f],c,d,e);else c=c||this.handleEvent,e=e||this.Ya||this,c=Eb(c),d=!!d,b=ub(a)?a.ba(b,c,d,e):a?(a=Gb(a))?a.ba(b,c,d,e):null:null,b&&(Mb(b),delete this.b[b.key]);return this};W.prototype.removeAll=function(){va(this.b,Mb);this.b={}};W.prototype.o=function(){W.P.o.call(this);this.removeAll()};W.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var wd=function(){M.call(this);this.oa=new W(this);pb&&(qb?this.oa.listen(rb?document.body:window,["online","offline"],this.wb):(this.Eb=pb?navigator.onLine:!0,this.h=new sd(250),this.oa.listen(this.h,"tick",this.oc),this.h.start()))};w(wd,M);wd.prototype.oc=function(){var a=pb?navigator.onLine:!0;a!=this.Eb&&(this.Eb=a,this.wb())};wd.prototype.wb=function(){this.dispatchEvent((pb?navigator.onLine:1)?"online":"offline")};
-wd.prototype.o=function(){wd.P.o.call(this);this.oa.ma();this.oa=null;this.h&&(this.h.ma(),this.h=null)};var xd=function(a,b){this.g=a;this.e=b};xd.prototype.send=function(a,b){b.set(Nc,gd(this.g));return this.e.send(a,b)};var yd=function(a){this.e=a};yd.prototype.send=function(a,b){zd(b);Ad(b);return this.e.send(a,b)};var zd=function(a){Xa(a,function(b,c){m(b.maxLength)&&"text"==b.valueType&&0<b.maxLength&&c.length>b.maxLength&&a.set(b,c.substring(0,b.maxLength))})},Ad=function(a){Xa(a,function(b,c){m(b.defaultValue)&&c==b.defaultValue&&a.remove(b)})};var Fc={status:"device-offline",la:void 0},Bd={status:"rate-limited",la:void 0},Cd={status:"sampled-out",la:void 0},Dd={status:"sent",la:void 0};var Ed=function(a,b){this.Vc=a;this.e=b};Ed.prototype.send=function(a,b){var c;c=this.Vc;var d=c.Lb(),e=Math.floor((d-c.Ab)*c.hc);0<e&&(c.$=Math.min(c.$+e,c.uc),c.Ab=d);1>c.$?c=!1:(--c.$,c=!0);return c||"item"==a||"transaction"==a?this.e.send(a,b):Ec(Bd)};var Fd=function(){this.$=60;this.uc=500;this.hc=5E-4;this.Lb=function(){return(new Date).getTime()};this.Ab=this.Lb()};var Gd=function(a,b){this.g=a;this.e=b};Gd.prototype.send=function(a,b){var c=b.get(Nc),c=parseInt(c.split("-")[1],16),d;"timing"!=a?d=kd(this.g):((d=b.get(Qc))&&b.remove(Qc),d=d||kd(this.g));return c<655.36*d?this.e.send(a,b):Ec(Cd)};var Hd=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/,Jd=function(a){if(Id){Id=!1;var b=k.location;if(b){var c=b.href;if(c&&(c=(c=Jd(c)[3]||null)?decodeURI(c):c)&&c!=b.hostname)throw Id=!0,Error();}}return a.match(Hd)},Id=fb,Kd=function(a,b){for(var c=a.split("&"),d=0;d<c.length;d++){var e=c[d].indexOf("="),f=null,g=null;0<=e?(f=c[d].substring(0,e),g=c[d].substring(e+1)):f=c[d];b(f,g?decodeURIComponent(g.replace(/\+/g," ")):"")}};var Ld=function(){};Ld.prototype.nb=null;var Nd=function(a){var b;(b=a.nb)||(b={},Md(a)&&(b[0]=!0,b[1]=!0),b=a.nb=b);return b};var Od,Pd=function(){};w(Pd,Ld);var Qd=function(a){return(a=Md(a))?new ActiveXObject(a):new XMLHttpRequest},Md=function(a){if(!a.xb&&"undefined"==typeof XMLHttpRequest&&"undefined"!=typeof ActiveXObject){for(var b=["MSXML2.XMLHTTP.6.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],c=0;c<b.length;c++){var d=b[c];try{return new ActiveXObject(d),a.xb=d}catch(e){}}throw Error("Could not create ActiveXObject. ActiveX might be disabled, or MSXML might not be installed");}return a.xb};Od=new Pd;var X=function(a){M.call(this);this.headers=new z;this.Ma=a||null;this.F=!1;this.La=this.a=null;this.za=this.$a="";this.N=this.Za=this.va=this.Ua=!1;this.Ha=0;this.Ga=null;this.Gb="";this.kb=this.Zc=!1};w(X,M);var Rd=/^https?$/i,Sd=["POST","PUT"],Td=[],Ud=function(a,b,c){var d=new X;Td.push(d);b&&d.listen("complete",b);d.cb("ready",d.bc);d.send(a,"POST",c,void 0)};X.prototype.bc=function(){this.ma();qa(Td,this)};
-X.prototype.send=function(a,b,c,d){if(this.a)throw Error("[goog.net.XhrIo] Object is active with another request="+this.$a+"; newUri="+a);b=b?b.toUpperCase():"GET";this.$a=a;this.za="";this.Ua=!1;this.F=!0;this.a=this.Ma?Qd(this.Ma):Qd(Od);this.La=this.Ma?Nd(this.Ma):Nd(Od);this.a.onreadystatechange=t(this.Db,this);try{this.Za=!0,this.a.open(b,String(a),!0),this.Za=!1}catch(e){this.na(5,e);return}a=c||"";var f=this.headers.clone();d&&Wa(d,function(a,b){f.set(b,a)});d=pa(f.H());c=k.FormData&&a instanceof
-k.FormData;!(0<=ka(Sd,b))||d||c||f.set("Content-Type","application/x-www-form-urlencoded;charset=utf-8");f.forEach(function(a,b){this.a.setRequestHeader(b,a)},this);this.Gb&&(this.a.responseType=this.Gb);"withCredentials"in this.a&&(this.a.withCredentials=this.Zc);try{Vd(this),0<this.Ha&&((this.kb=Wd(this.a))?(this.a.timeout=this.Ha,this.a.ontimeout=t(this.Mb,this)):this.Ga=td(this.Mb,this.Ha,this)),this.va=!0,this.a.send(a),this.va=!1}catch(g){this.na(5,g)}};
-var Wd=function(a){return J&&K(9)&&ea(a.timeout)&&m(a.ontimeout)},oa=function(a){return"content-type"==a.toLowerCase()};X.prototype.Mb=function(){"undefined"!=typeof aa&&this.a&&(this.za="Timed out after "+this.Ha+"ms, aborting",this.dispatchEvent("timeout"),this.abort(8))};X.prototype.na=function(a,b){this.F=!1;this.a&&(this.N=!0,this.a.abort(),this.N=!1);this.za=b;Xd(this);Yd(this)};var Xd=function(a){a.Ua||(a.Ua=!0,a.dispatchEvent("complete"),a.dispatchEvent("error"))};
-X.prototype.abort=function(){this.a&&this.F&&(this.F=!1,this.N=!0,this.a.abort(),this.N=!1,this.dispatchEvent("complete"),this.dispatchEvent("abort"),Yd(this))};X.prototype.o=function(){this.a&&(this.F&&(this.F=!1,this.N=!0,this.a.abort(),this.N=!1),Yd(this,!0));X.P.o.call(this)};X.prototype.Db=function(){this.aa||(this.Za||this.va||this.N?Zd(this):this.wc())};X.prototype.wc=function(){Zd(this)};
-var Zd=function(a){if(a.F&&"undefined"!=typeof aa&&(!a.La[1]||4!=$d(a)||2!=ae(a)))if(a.va&&4==$d(a))td(a.Db,0,a);else if(a.dispatchEvent("readystatechange"),4==$d(a)){a.F=!1;try{var b=ae(a),c;a:switch(b){case 200:case 201:case 202:case 204:case 206:case 304:case 1223:c=!0;break a;default:c=!1}var d;if(!(d=c)){var e;if(e=0===b){var f=Jd(String(a.$a))[1]||null;if(!f&&self.location)var g=self.location.protocol,f=g.substr(0,g.length-1);e=!Rd.test(f?f.toLowerCase():"")}d=e}if(d)a.dispatchEvent("complete"),
-a.dispatchEvent("success");else{var l;try{l=2<$d(a)?a.a.statusText:""}catch(D){l=""}a.za=l+" ["+ae(a)+"]";Xd(a)}}finally{Yd(a)}}},Yd=function(a,b){if(a.a){Vd(a);var c=a.a,d=a.La[0]?ba:null;a.a=null;a.La=null;b||a.dispatchEvent("ready");try{c.onreadystatechange=d}catch(e){}}},Vd=function(a){a.a&&a.kb&&(a.a.ontimeout=null);ea(a.Ga)&&(k.clearTimeout(a.Ga),a.Ga=null)},$d=function(a){return a.a?a.a.readyState:0},ae=function(a){try{return 2<$d(a)?a.a.status:-1}catch(b){return-1}};var be=function(a,b,c){this.r=a||null;this.qc=!!c},ce=function(a){a.c||(a.c=new z,a.j=0,a.r&&Kd(a.r,function(b,c){a.add(decodeURIComponent(b.replace(/\+/g," ")),c)}))};h=be.prototype;h.c=null;h.j=null;h.add=function(a,b){ce(this);this.r=null;a=de(this,a);var c=this.c.get(a);c||this.c.set(a,c=[]);c.push(b);this.j++;return this};h.remove=function(a){ce(this);a=de(this,a);return this.c.T(a)?(this.r=null,this.j-=this.c.get(a).length,this.c.remove(a)):!1};h.T=function(a){ce(this);a=de(this,a);return this.c.T(a)};
-h.H=function(){ce(this);for(var a=this.c.t(),b=this.c.H(),c=[],d=0;d<b.length;d++)for(var e=a[d],f=0;f<e.length;f++)c.push(b[d]);return c};h.t=function(a){ce(this);var b=[];if(p(a))this.T(a)&&(b=ra(b,this.c.get(de(this,a))));else{a=this.c.t();for(var c=0;c<a.length;c++)b=ra(b,a[c])}return b};h.set=function(a,b){ce(this);this.r=null;a=de(this,a);this.T(a)&&(this.j-=this.c.get(a).length);this.c.set(a,[b]);this.j++;return this};
-h.get=function(a,b){var c=a?this.t(a):[];return 0<c.length?String(c[0]):b};h.toString=function(){if(this.r)return this.r;if(!this.c)return"";for(var a=[],b=this.c.H(),c=0;c<b.length;c++)for(var d=b[c],e=encodeURIComponent(String(d)),d=this.t(d),f=0;f<d.length;f++){var g=e;""!==d[f]&&(g+="="+encodeURIComponent(String(d[f])));a.push(g)}return this.r=a.join("&")};h.clone=function(){var a=new be;a.r=this.r;this.c&&(a.c=this.c.clone(),a.j=this.j);return a};
-var de=function(a,b){var c=String(b);a.qc&&(c=c.toLowerCase());return c};var ee=function(a,b){this.Mc=a;this.Aa=b};ee.prototype.send=function(a,b){if(pb&&!navigator.onLine)return Gc();var c=new Q,d=fe(a,b);d.length>this.Aa?c.A({status:"payload-too-big",la:Ua("Encoded hit length == %s, but should be <= %s.",d.length,this.Aa)}):Ud(this.Mc,function(){c.v(Dd)},d);return c};var fe=function(a,b){var c=new be;c.add(Fa.name,a);Xa(b,function(a,b){c.add(a.name,b.toString())});return c.toString()};var ge=function(a,b,c){this.g=a;this.Lc=b;this.Aa=c};ge.prototype.Xa=function(){if(!this.q){if(!Ac(this.g.ea).C)throw Error("Cannot construct shared channel prior to settings being ready.");new wd;var a=new yd(new ee(this.Lc,this.Aa)),b=new Fd;this.q=new xd(this.g,new Gd(this.g,new Ed(b,a)))}return this.q};var he=new z,ie=function(){Da||(Da=new T(new ld));return Da};v("goog.async.Deferred",Q);v("goog.async.Deferred.prototype.addCallback",Q.prototype.n);v("goog.async.Deferred.prototype.callback",Q.prototype.v);v("goog.async.Deferred.prototype.then",Q.prototype.then);v("goog.events.EventTarget",M);v("goog.events.EventTarget.prototype.listen",M.prototype.listen);
-v("analytics.getService",function(a,b){var c=he.get(a,null),d=b||chrome.runtime.getManifest().version;if(null===c){c=ie();if(!Ea){var e=ie();Ea=new rd(e,new ge(e,"https://www.google-analytics.com/collect",8192))}c=new Zc("ca1.6.0",a,d,c,Ea);he.set(a,c)}return c});v("analytics.internal.GoogleAnalyticsService",Zc);v("analytics.internal.GoogleAnalyticsService.prototype.getTracker",Zc.prototype.mc);v("analytics.internal.GoogleAnalyticsService.prototype.getConfig",Zc.prototype.kc);
-v("analytics.internal.ServiceSettings",T);v("analytics.internal.ServiceSettings.prototype.setTrackingPermitted",T.prototype.Oc);v("analytics.internal.ServiceSettings.prototype.isTrackingPermitted",T.prototype.xa);v("analytics.internal.ServiceSettings.prototype.setSampleRate",T.prototype.Nc);v("analytics.internal.ServiceSettings.prototype.resetUserId",T.prototype.Fc);v("analytics.internal.ServiceTracker",S);v("analytics.internal.ServiceTracker.prototype.send",S.prototype.send);
-v("analytics.internal.ServiceTracker.prototype.sendAppView",S.prototype.Hc);v("analytics.internal.ServiceTracker.prototype.sendEvent",S.prototype.Ic);v("analytics.internal.ServiceTracker.prototype.sendSocial",S.prototype.Kc);v("analytics.internal.ServiceTracker.prototype.sendException",S.prototype.Jc);v("analytics.internal.ServiceTracker.prototype.sendTiming",S.prototype.Ib);v("analytics.internal.ServiceTracker.prototype.startTiming",S.prototype.Rc);v("analytics.internal.ServiceTracker.Timing",Yc);
-v("analytics.internal.ServiceTracker.Timing.prototype.send",Yc.prototype.send);v("analytics.internal.ServiceTracker.prototype.forceSessionStart",S.prototype.jc);v("analytics.internal.ServiceTracker.prototype.addFilter",S.prototype.R);v("analytics.internal.FilterChannel.Hit",R);v("analytics.internal.FilterChannel.Hit.prototype.getHitType",R.prototype.ub);v("analytics.internal.FilterChannel.Hit.prototype.getParameters",R.prototype.V);v("analytics.internal.FilterChannel.Hit.prototype.cancel",R.prototype.cancel);
-v("analytics.ParameterMap",E);v("analytics.ParameterMap.Entry",E.Entry);v("analytics.ParameterMap.prototype.set",E.prototype.set);v("analytics.ParameterMap.prototype.get",E.prototype.get);v("analytics.ParameterMap.prototype.remove",E.prototype.remove);v("analytics.ParameterMap.prototype.toObject",E.prototype.Nb);v("analytics.HitTypes.APPVIEW","appview");v("analytics.HitTypes.EVENT","event");v("analytics.HitTypes.SOCIAL","social");v("analytics.HitTypes.TRANSACTION","transaction");
-v("analytics.HitTypes.ITEM","item");v("analytics.HitTypes.TIMING","timing");v("analytics.HitTypes.EXCEPTION","exception");va(Ma,function(a){var b=a.id.replace(/[A-Z]/,"_$&").toUpperCase();v("analytics.Parameters."+b,a)});v("analytics.filters.EventLabelerBuilder",C);v("analytics.filters.EventLabelerBuilder.prototype.appendToExistingLabel",C.prototype.Wb);v("analytics.filters.EventLabelerBuilder.prototype.stripValue",C.prototype.Sc);v("analytics.filters.EventLabelerBuilder.prototype.powersOfTwo",C.prototype.Dc);
-v("analytics.filters.EventLabelerBuilder.prototype.rangeBounds",C.prototype.Ec);v("analytics.filters.EventLabelerBuilder.prototype.build",C.prototype.ia);v("analytics.filters.FilterBuilder",B);v("analytics.filters.FilterBuilder.builder",Ra);v("analytics.filters.FilterBuilder.prototype.when",B.prototype.when);v("analytics.filters.FilterBuilder.prototype.whenHitType",B.prototype.Rb);v("analytics.filters.FilterBuilder.prototype.whenValue",B.prototype.Yc);
-v("analytics.filters.FilterBuilder.prototype.applyFilter",B.prototype.mb);v("analytics.filters.FilterBuilder.prototype.build",B.prototype.ia);v("analytics.EventBuilder",F);v("analytics.EventBuilder.builder",function(){return Ya});v("analytics.EventBuilder.prototype.category",F.prototype.Yb);v("analytics.EventBuilder.prototype.action",F.prototype.action);v("analytics.EventBuilder.prototype.label",F.prototype.label);v("analytics.EventBuilder.prototype.value",F.prototype.value);
-v("analytics.EventBuilder.prototype.dimension",F.prototype.fc);v("analytics.EventBuilder.prototype.metric",F.prototype.vc);v("analytics.EventBuilder.prototype.send",F.prototype.send); }).call(this);
diff --git a/third_party/blink/perf_tests/canvas/resources/canvas_runner.js b/third_party/blink/perf_tests/canvas/resources/canvas_runner.js
index 34843e8..f585f33 100644
--- a/third_party/blink/perf_tests/canvas/resources/canvas_runner.js
+++ b/third_party/blink/perf_tests/canvas/resources/canvas_runner.js
@@ -1,98 +1,171 @@
-// CanvasRunner is a wrapper of PerformanceTests/resources/runner.js for canvas tests.
+/*
+Runs canvas performance tests to calculate runs/sec for test.doRun().
+This module works in two different ways, depending on requestAnimationFrame
+(RAF). The query string `RAF` in the url determines which test is run.
+*/
 (function () {
-    var MEASURE_DRAW_TIMES = 50;
-    var MAX_MEASURE_DRAW_TIMES = 1000;
-    var MAX_MEASURE_TIME_PER_FRAME = 1000; // 1 sec
-    var currentTest = null;
-    var isTestDone = false;
+  var MEASURE_DRAW_TIMES = 50;
+  var MAX_MEASURE_DRAW_TIMES = 1000;
+  var MAX_MEASURE_TIME_PER_FRAME = 1000; // 1 sec
+  var IS_RAF_TEST = (
+    document.location.search.substr(1,).toUpperCase() === "RAF");
+  var currentTest = null;
+  var isTestDone = false;
 
-    var CanvasRunner = {};
+  var CanvasRunner = {};
 
-    CanvasRunner.start = function (test) {
-        PerfTestRunner.startMeasureValuesAsync({
-            unit: 'runs/s',
-            description: test.description,
-            done: testDone,
-            run: function() {
-                if (!test.doRun) {
-                    CanvasRunner.logFatalError("doRun must be set.");
-                    return;
-                }
-                currentTest = test;
-                runTest();
-            }});
-    }
-
-    function runTest() {
-        try {
-            if (currentTest.preRun)
-                currentTest.preRun();
-
-            var start = PerfTestRunner.now();
-            var count = 0;
-            while ((PerfTestRunner.now() - start <= MAX_MEASURE_TIME_PER_FRAME) && (count * MEASURE_DRAW_TIMES < MAX_MEASURE_DRAW_TIMES)) {
-                for (var i = 0; i < MEASURE_DRAW_TIMES; i++) {
-                    currentTest.doRun();
-                }
-                count++;
-            }
-            if (currentTest.ensureComplete)
-                currentTest.ensureComplete();
-            var elapsedTime = PerfTestRunner.now() - start;
-            if (currentTest.postRun)
-                currentTest.postRun();
-
-            PerfTestRunner.measureValueAsync(MEASURE_DRAW_TIMES * count * 1000 / elapsedTime);
-        } catch(err) {
-            CanvasRunner.logFatalError("test fails due to GPU issue. " + err);
-            return;
+  CanvasRunner.start = function (test) {
+    PerfTestRunner.startMeasureValuesAsync({
+      unit: 'runs/s',
+      description: test.description,
+      done: testDone,
+      run: function() {
+        if (!test.doRun) {
+          CanvasRunner.logFatalError("doRun must be set.");
+          return;
         }
+        currentTest = test;
+        if (IS_RAF_TEST === true) {
+          runTestRAF();
+        } else {
+          runTest();
+        }
+      }});
+  }
 
-        if (!isTestDone)
-            requestAnimationFrame(runTest);
+  // Times the CPU on the main thread
+  function runTest() {
+    try {
+      if (currentTest.preRun)
+        currentTest.preRun();
+
+      var start = PerfTestRunner.now();
+      var count = 0;
+      while ((PerfTestRunner.now() - start <= MAX_MEASURE_TIME_PER_FRAME) &&
+        (count * MEASURE_DRAW_TIMES < MAX_MEASURE_DRAW_TIMES)) {
+        for (var i = 0; i < MEASURE_DRAW_TIMES; i++) {
+          currentTest.doRun();
+        }
+        count++;
+      }
+      if (currentTest.ensureComplete)
+        currentTest.ensureComplete();
+      var elapsedTime = PerfTestRunner.now() - start;
+      if (currentTest.postRun)
+        currentTest.postRun();
+
+      let runsPerSecond = MEASURE_DRAW_TIMES * count * 1000 / elapsedTime;
+      PerfTestRunner.measureValueAsync(runsPerSecond);
+    } catch(err) {
+      CanvasRunner.logFatalError("test fails due to GPU issue. " + err);
+      throw err;
     }
 
-    function testDone() {
-        isTestDone = true;
+    if (!isTestDone)
+      requestAnimationFrame(runTest);
+  }
+
+  // Times CPU + raster + GPU for draw calls, invoked with the ?RAF query string
+  // All times in milliseconds
+  function runTestRAF() {
+    // How long in ms we want each trial to take
+    // Must be much greater than 16 (16ms is the v-sync rate)
+    const GOAL_TIME = 200;
+
+    function runTrial(numRuns) {
+      if (currentTest.preRun) currentTest.preRun();
+      let startTime = PerfTestRunner.now();
+      for (var i = 0; i < numRuns; i++) {
+        currentTest.doRun();
+      }
+      requestAnimationFrame(() => {
+        let elapsedTime = PerfTestRunner.now() - startTime;
+        let runsPerSecond = numRuns * 1000 / elapsedTime;
+        PerfTestRunner.measureValueAsync(runsPerSecond);
+        if (!isTestDone) runTrial(numRuns, startTime);
+      });
+      if (currentTest.ensureComplete) currentTest.ensureComplete();
+      if (currentTest.postRun) currentTest.postRun();
     }
 
-    CanvasRunner.logFatalError = function (text) {
-        PerfTestRunner.logFatalError(text);
+    // Figure out how many times currentTest.doRun() + RAF will be required
+    // to last GOAL_TIME
+    function calculateNumberOfRuns(resolve, numRuns) {
+      numRuns = numRuns || 1;
+      if (currentTest.preRun) currentTest.preRun();
+      const startTime = PerfTestRunner.now();
+
+      for (var i = 0; i < numRuns; i++) {
+        currentTest.doRun();
+      }
+
+      requestAnimationFrame(() => {
+        let elapsedTime = PerfTestRunner.now() - startTime;
+        if (elapsedTime >= GOAL_TIME) {
+          const timePerRun = elapsedTime / numRuns;
+          const numRunsFinal = Math.round(GOAL_TIME / timePerRun);
+          if (currentTest.ensureComplete) currentTest.ensureComplete();
+          if (currentTest.postRun) currentTest.postRun();
+          resolve(numRunsFinal);
+        } else {
+          calculateNumberOfRuns(resolve, numRuns * 2);
+        }
+      });
     }
 
-    CanvasRunner.startPlayingAndWaitForVideo = function (video, callback) {
-        var gotPlaying = false;
-        var gotTimeUpdate = false;
-
-        var maybeCallCallback = function() {
-            if (gotPlaying && gotTimeUpdate && callback) {
-                callback(video);
-                callback = undefined;
-                video.removeEventListener('playing', playingListener, true);
-                video.removeEventListener('timeupdate', timeupdateListener, true);
-            }
-        };
-
-        var playingListener = function() {
-            gotPlaying = true;
-            maybeCallCallback();
-        };
-
-        var timeupdateListener = function() {
-            // Checking to make sure the current time has advanced beyond
-            // the start time seems to be a reliable heuristic that the
-            // video element has data that can be consumed.
-            if (video.currentTime > 0.0) {
-                gotTimeUpdate = true;
-                maybeCallCallback();
-            }
-        };
-
-        video.addEventListener('playing', playingListener, true);
-        video.addEventListener('timeupdate', timeupdateListener, true);
-        video.loop = true;
-        video.play();
+    try {
+      new Promise(function(resolve, reject) {
+        calculateNumberOfRuns(resolve);
+      }).then(function(numberOfRuns) {
+        runTrial(numberOfRuns);
+      });
+    } catch(err) {
+      CanvasRunner.logFatalError("test fails due to GPU issue. " + err);
+      throw err;
     }
+  }
 
-    window.CanvasRunner = CanvasRunner;
+  function testDone() {
+    isTestDone = true;
+  }
+
+  CanvasRunner.logFatalError = function (text) {
+    PerfTestRunner.logFatalError(text);
+  }
+
+  CanvasRunner.startPlayingAndWaitForVideo = function (video, callback) {
+    var gotPlaying = false;
+    var gotTimeUpdate = false;
+
+    var maybeCallCallback = function() {
+      if (gotPlaying && gotTimeUpdate && callback) {
+        callback(video);
+        callback = undefined;
+        video.removeEventListener('playing', playingListener, true);
+        video.removeEventListener('timeupdate', timeupdateListener, true);
+      }
+    };
+
+    var playingListener = function() {
+      gotPlaying = true;
+      maybeCallCallback();
+    };
+
+    var timeupdateListener = function() {
+      // Checking to make sure the current time has advanced beyond
+      // the start time seems to be a reliable heuristic that the
+      // video element has data that can be consumed.
+      if (video.currentTime > 0.0) {
+        gotTimeUpdate = true;
+        maybeCallCallback();
+      }
+    };
+
+    video.addEventListener('playing', playingListener, true);
+    video.addEventListener('timeupdate', timeupdateListener, true);
+    video.loop = true;
+    video.play();
+  }
+
+  window.CanvasRunner = CanvasRunner;
 })();
diff --git a/third_party/blink/perf_tests/canvas/upload-webgl-to-texture.html b/third_party/blink/perf_tests/canvas/upload-webgl-to-texture.html
index 07a2cf97..d36e4291 100644
--- a/third_party/blink/perf_tests/canvas/upload-webgl-to-texture.html
+++ b/third_party/blink/perf_tests/canvas/upload-webgl-to-texture.html
@@ -12,7 +12,9 @@
 if (!sourceCtx || !destCtx)
     CanvasRunner.logFatalError("WebGL is not supported or enabled on this platform!");
 var tex = null;
- 
+const width = 1024;
+const height = 1024;
+
 function setSize(width, height) {
     sourceCanvas3D.width = width;
     sourceCanvas3D.height = height;
@@ -28,7 +30,7 @@
     gl.disable(gl.SCISSOR_TEST);
     gl.clear(gl.COLOR_BUFER_BIT);
     gl.enable(gl.SCISSOR_TEST);
-    gl.scissor(rand(1024), rand(1024), rand(1024), rand(1024));
+    gl.scissor(rand(width), rand(height), rand(width), rand(height));
     gl.clearColor(Math.random(), Math.random(), Math.random(), 1);
     gl.clear(gl.COLOR_BUFFER_BIT);
 }
@@ -51,7 +53,7 @@
 }
 
 window.onload = function () {
-    setSize(1024, 1024);
+    setSize(width, height);
     renderWebGL(sourceCtx);
     CanvasRunner.start({
         description: "This benchmark checks the speed on uploading WebGL(1024x1024) to WebGL Texture(1024x1024).",
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 91496a97..52416c8 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -455,6 +455,7 @@
     "web/web_memory_statistics.h",
     "web/web_menu_item_info.h",
     "web/web_meta_element.h",
+    "web/web_navigation_control.h",
     "web/web_navigation_params.h",
     "web/web_navigation_policy.h",
     "web/web_navigation_type.h",
diff --git a/third_party/blink/public/mojom/quota/quota_dispatcher_host.mojom b/third_party/blink/public/mojom/quota/quota_dispatcher_host.mojom
index 26b04e4c..e778a80b 100644
--- a/third_party/blink/public/mojom/quota/quota_dispatcher_host.mojom
+++ b/third_party/blink/public/mojom/quota/quota_dispatcher_host.mojom
@@ -23,7 +23,8 @@
                             blink.mojom.StorageType storage_type) =>
       (blink.mojom.QuotaStatusCode error,
        int64 current_usage,
-       int64 current_quota);
+       int64 current_quota,
+       UsageBreakdown usage_breakdown);
 
   // Renderer process requests a new quota size for the origin's storage from
   // the browser process. |requested_size| indicates how much storage space (in
diff --git a/third_party/blink/public/mojom/quota/quota_types.mojom b/third_party/blink/public/mojom/quota/quota_types.mojom
index c000867a..77f41e0 100644
--- a/third_party/blink/public/mojom/quota/quota_types.mojom
+++ b/third_party/blink/public/mojom/quota/quota_types.mojom
@@ -21,3 +21,13 @@
   kErrorAbort = 20,                // ABORT_ERR
   kUnknown = -1,
 };
+
+struct UsageBreakdown {
+  int64 fileSystem = 0;
+  int64 webSql = 0;
+  int64 appcache = 0;
+  int64 indexedDatabase = 0;
+  int64 serviceWorkerCache = 0;
+  int64 serviceWorker = 0;
+  int64 backgroundFetch = 0;
+};
diff --git a/third_party/blink/public/mojom/shared_worker/OWNERS b/third_party/blink/public/mojom/shared_worker/OWNERS
index a9e7183..e1abbf3 100644
--- a/third_party/blink/public/mojom/shared_worker/OWNERS
+++ b/third_party/blink/public/mojom/shared_worker/OWNERS
@@ -1,4 +1,4 @@
-file://content/browser/shared_worker/OWNERS
+file://content/browser/worker_host/OWNERS
 
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 449a339..4b680e1b 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -9,7 +9,6 @@
 #include <set>
 
 #include "base/callback.h"
-#include "base/unguessable_token.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
@@ -24,7 +23,6 @@
 #include "third_party/blink/public/web/web_document_loader.h"
 #include "third_party/blink/public/web/web_frame.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
-#include "third_party/blink/public/web/web_history_item.h"
 #include "third_party/blink/public/web/web_ime_text_span.h"
 #include "third_party/blink/public/web/web_navigation_params.h"
 #include "third_party/blink/public/web/web_text_direction.h"
@@ -37,7 +35,6 @@
 class WebAssociatedURLLoader;
 class WebAutofillClient;
 class WebContentSettingsClient;
-class WebData;
 class WebDocument;
 class WebDoubleSize;
 class WebDOMEvent;
@@ -58,8 +55,8 @@
 struct WebAssociatedURLLoaderOptions;
 struct WebConsoleMessage;
 struct WebContentSecurityPolicyViolation;
-struct WebNavigationParams;
 struct WebMediaPlayerAction;
+struct WebPoint;
 struct WebPrintParams;
 struct WebPrintPresetOptions;
 struct WebScriptSource;
@@ -193,11 +190,6 @@
 
   // Navigation ----------------------------------------------------------
 
-  // Runs beforeunload handlers for this frame and returns the value returned
-  // by handlers.
-  // Note: this may lead to the destruction of the frame.
-  virtual bool DispatchBeforeUnloadEvent(bool is_reload) = 0;
-
   // Start reloading the current document.
   // Note: StartReload() will be deprecated, use StartNavigation() instead.
   virtual void StartReload(WebFrameLoadType) = 0;
@@ -205,62 +197,8 @@
   // Start navigation to the given URL.
   virtual void StartNavigation(const WebURLRequest&) = 0;
 
-  // Commits a cross-document navigation in the frame. For history navigations,
-  // a valid WebHistoryItem should be provided.
-  // TODO(dgozman): return mojom::CommitResult.
-  virtual void CommitNavigation(
-      const WebURLRequest&,
-      WebFrameLoadType,
-      const WebHistoryItem&,
-      bool is_client_redirect,
-      const base::UnguessableToken& devtools_navigation_token,
-      std::unique_ptr<WebNavigationParams> navigation_params,
-      std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) = 0;
-
-  // Commits a same-document navigation in the frame. For history navigations, a
-  // valid WebHistoryItem should be provided. Returns CommitResult::Ok if the
-  // navigation has actually committed.
-  virtual mojom::CommitResult CommitSameDocumentNavigation(
-      const WebURL&,
-      WebFrameLoadType,
-      const WebHistoryItem&,
-      bool is_client_redirect,
-      std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) = 0;
-
-  // Loads a JavaScript URL in the frame.
-  virtual void LoadJavaScriptURL(const WebURL&) = 0;
-
-  // This method is short-hand for calling CommitDataNavigation, where mime_type
-  // is "text/html" and text_encoding is "UTF-8".
-  // TODO(dgozman): rename to CommitHTMLStringNavigation.
-  virtual void LoadHTMLString(const WebData& html,
-                              const WebURL& base_url,
-                              const WebURL& unreachable_url = WebURL()) = 0;
-
-  // Navigates to the given |data| with specified |mime_type| and optional
-  // |text_encoding|.
-  //
-  // If specified, |unreachable_url| is reported via
-  // WebDocumentLoader::UnreachableURL.
-  //
-  // If |replace| is false, then this data will be loaded as a normal
-  // navigation.  Otherwise, the current history item will be replaced.
-  //
-  // Request's url indicates the security origin and is used as a base
-  // url to resolve links in the committed document.
-  virtual void CommitDataNavigation(
-      const WebURLRequest&,
-      const WebData&,
-      const WebString& mime_type,
-      const WebString& text_encoding,
-      const WebURL& unreachable_url,
-      WebFrameLoadType,
-      const WebHistoryItem&,
-      bool is_client_redirect,
-      std::unique_ptr<WebNavigationParams> navigation_params,
-      std::unique_ptr<WebDocumentLoader::ExtraData> navigation_data) = 0;
-
   // Returns the document loader that is currently loading.  May be null.
+  // TODO(dgozman): move this to WebNavigationControl.
   virtual WebDocumentLoader* GetProvisionalDocumentLoader() const = 0;
 
   // View-source rendering mode.  Set this before loading an URL to cause
@@ -271,23 +209,6 @@
   // Returns the document loader that is currently loaded.
   virtual WebDocumentLoader* GetDocumentLoader() const = 0;
 
-  enum FallbackContentResult {
-    // An error page should be shown instead of fallback.
-    NoFallbackContent,
-    // Something else committed, no fallback content or error page needed.
-    NoLoadInProgress,
-    // Fallback content rendered, no error page needed.
-    FallbackRendered
-  };
-  // On load failure, attempts to make frame's parent render fallback content.
-  virtual FallbackContentResult MaybeRenderFallbackContent(
-      const WebURLError&) const = 0;
-
-  // When load failure is in a cross-process frame this notifies the frame here
-  // that its owner should render fallback content if any. Only called on owners
-  // that render their own content (i.e., <object>).
-  virtual void RenderFallbackContent() const = 0;
-
   // Called when a navigation is blocked because a Content Security Policy (CSP)
   // is infringed.
   virtual void ReportContentSecurityPolicyViolation(
@@ -317,11 +238,6 @@
   virtual bool IsNavigationScheduledWithin(
       double interval_in_seconds) const = 0;
 
-  // Override the normal rules for whether a load has successfully committed
-  // in this frame. Used to propagate state when this frame has navigated
-  // cross process.
-  virtual void SetCommittedFirstRealLoad() = 0;
-
   // Reports a list of unique blink::WebFeature values representing
   // Blink features used, performed or encountered by the browser during the
   // current page load happening on the frame.
@@ -338,28 +254,6 @@
                                  bool had_redirect,
                                  const WebSourceLocation&) = 0;
 
-  // Informs the frame that the navigation it asked the client to do was
-  // dropped.
-  virtual void ClientDroppedNavigation() = 0;
-
-  // Marks the frame as loading, without performing any loading. Used for
-  // initial history navigations in child frames, which may actually happen
-  // in the other process.
-  virtual void MarkAsLoading() = 0;
-
-  // Marks the frame as loading and creates a placeholder document loader.
-  // This placeholder informs Blink that the navigation is ongoing, while it
-  // is actually being handled by the client.
-  // TODO(dgozman): remove this together with placeholder document loader.
-  virtual bool CreatePlaceholderDocumentLoader(
-      const WebURLRequest&,
-      WebFrameLoadType,
-      WebNavigationType,
-      bool is_client_redirect,
-      const base::UnguessableToken& devtools_navigation_token,
-      std::unique_ptr<WebNavigationParams>,
-      std::unique_ptr<WebDocumentLoader::ExtraData>) = 0;
-
   // Orientation Changes ----------------------------------------------------
 
   // Notify the frame that the screen orientation has changed.
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index e8f1ba9..2a46b3fab 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -104,6 +104,7 @@
 class WebMediaPlayerClient;
 class WebMediaPlayerEncryptedMediaClient;
 class WebMediaPlayerSource;
+class WebNavigationControl;
 class WebServiceWorkerProvider;
 class WebPlugin;
 class WebPushClient;
@@ -131,8 +132,9 @@
   // Initialization ------------------------------------------------------
   // Called exactly once during construction to notify the client about the
   // created WebLocalFrame. Guaranteed to be invoked before any other
-  // WebLocalFrameClient callbacks.
-  virtual void BindToFrame(WebLocalFrame*) {}
+  // WebLocalFrameClient callbacks. Note this takes WebNavigationControl
+  // to give the client full control over frame's navigation.
+  virtual void BindToFrame(WebNavigationControl*) {}
 
   // Factory methods -----------------------------------------------------
 
@@ -332,14 +334,15 @@
 
   // Requests the client to begin a navigation for this frame.
   //
-  // The client can just call CommitNavigation() on this frame in response.
-  // This will effectively commit a navigation the frame has asked about.
-  // This usually happens for navigations which do not require a network
-  // request, e.g. about:blank or mhtml archive.
+  // The client can just call CommitNavigation() on this frame's
+  // WebNavigationControl in response. This will effectively commit a navigation
+  // the frame has asked about. This usually happens for navigations which
+  // do not require a network request, e.g. about:blank or mhtml archive.
   //
   // In the case of a navigation which requires network request and goes
   // to the browser process, client calls CreatePlaceholderDocumentLoader
-  // (see it for more details) and commits/cancels the navigation later.
+  // (see WebNavigationControl for more details) and commits/cancels
+  // the navigation later.
   //
   // It is also totally valid to ignore the request and abandon the
   // navigation entirely.
diff --git a/third_party/blink/public/web/web_navigation_control.h b/third_party/blink/public/web/web_navigation_control.h
new file mode 100644
index 0000000..5c51fae
--- /dev/null
+++ b/third_party/blink/public/web/web_navigation_control.h
@@ -0,0 +1,147 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_NAVIGATION_CONTROL_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_NAVIGATION_CONTROL_H_
+
+#include <memory>
+
+#include "base/unguessable_token.h"
+#include "third_party/blink/public/web/web_document_loader.h"
+#include "third_party/blink/public/web/web_frame_load_type.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace blink {
+
+class WebData;
+class WebString;
+class WebURL;
+struct WebURLError;
+class WebURLRequest;
+class WebHistoryItem;
+struct WebNavigationParams;
+
+// This interface gives control to navigation-related functionality of
+// WebLocalFrame. It is separated from WebLocalFrame to give precise control
+// over callers of navigation methods.
+// WebLocalFrameClient gets a reference to this interface in BindToFrame.
+class WebNavigationControl : public WebLocalFrame {
+ public:
+  ~WebNavigationControl() override {}
+
+  // Runs beforeunload handlers for this frame and its local descendants.
+  // Returns |true| if all the frames agreed to proceed with unloading
+  // from their respective event handlers.
+  // Note: this may lead to the destruction of the frame.
+  virtual bool DispatchBeforeUnloadEvent(bool is_reload) = 0;
+
+  // Commits a cross-document navigation in the frame. For history navigations,
+  // a valid WebHistoryItem should be provided.
+  // TODO(dgozman): return mojom::CommitResult.
+  virtual void CommitNavigation(
+      const WebURLRequest&,
+      WebFrameLoadType,
+      const WebHistoryItem&,
+      bool is_client_redirect,
+      const base::UnguessableToken& devtools_navigation_token,
+      std::unique_ptr<WebNavigationParams> navigation_params,
+      std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) = 0;
+
+  // Commits a same-document navigation in the frame. For history navigations, a
+  // valid WebHistoryItem should be provided. Returns CommitResult::Ok if the
+  // navigation has actually committed.
+  virtual mojom::CommitResult CommitSameDocumentNavigation(
+      const WebURL&,
+      WebFrameLoadType,
+      const WebHistoryItem&,
+      bool is_client_redirect,
+      std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) = 0;
+
+  // Loads a JavaScript URL in the frame.
+  // TODO(dgozman): this may replace the document, so perhaps we should
+  // return something meaningful?
+  virtual void LoadJavaScriptURL(const WebURL&) = 0;
+
+  // This method is short-hand for calling CommitDataNavigation, where mime_type
+  // is "text/html" and text_encoding is "UTF-8".
+  // TODO(dgozman): rename to CommitHTMLStringNavigation.
+  virtual void LoadHTMLString(const WebData& html,
+                              const WebURL& base_url,
+                              const WebURL& unreachable_url = WebURL()) = 0;
+
+  // Navigates to the given |data| with specified |mime_type| and optional
+  // |text_encoding|.
+  //
+  // If specified, |unreachable_url| is reported via
+  // WebDocumentLoader::UnreachableURL.
+  //
+  // If |replace| is false, then this data will be loaded as a normal
+  // navigation.  Otherwise, the current history item will be replaced.
+  //
+  // Request's url indicates the security origin and is used as a base
+  // url to resolve links in the committed document.
+  virtual void CommitDataNavigation(
+      const WebURLRequest&,
+      const WebData&,
+      const WebString& mime_type,
+      const WebString& text_encoding,
+      const WebURL& unreachable_url,
+      WebFrameLoadType,
+      const WebHistoryItem&,
+      bool is_client_redirect,
+      std::unique_ptr<WebNavigationParams> navigation_params,
+      std::unique_ptr<WebDocumentLoader::ExtraData> navigation_data) = 0;
+
+  enum FallbackContentResult {
+    // An error page should be shown instead of fallback.
+    NoFallbackContent,
+    // Something else committed, no fallback content or error page needed.
+    NoLoadInProgress,
+    // Fallback content rendered, no error page needed.
+    FallbackRendered
+  };
+  // On load failure, attempts to make frame's parent render fallback content.
+  virtual FallbackContentResult MaybeRenderFallbackContent(
+      const WebURLError&) const = 0;
+
+  // When load failure is in a cross-process frame this notifies the frame here
+  // that its owner should render fallback content if any. Only called on owners
+  // that render their own content (i.e., <object>).
+  virtual void RenderFallbackContent() const = 0;
+
+  // Override the normal rules for whether a load has successfully committed
+  // in this frame. Used to propagate state when this frame has navigated
+  // cross process.
+  virtual void SetCommittedFirstRealLoad() = 0;
+
+  // Informs the frame that the navigation it asked the client to do was
+  // dropped.
+  virtual void ClientDroppedNavigation() = 0;
+
+  // Marks the frame as loading, without performing any loading. Used for
+  // initial history navigations in child frames, which may actually happen
+  // in another process.
+  virtual void MarkAsLoading() = 0;
+
+  // Marks the frame as loading and creates a placeholder document loader.
+  // This placeholder informs Blink that the navigation is ongoing, while it
+  // is actually being handled by the client.
+  // TODO(dgozman): remove this together with placeholder document loader.
+  virtual bool CreatePlaceholderDocumentLoader(
+      const WebURLRequest&,
+      WebFrameLoadType,
+      WebNavigationType,
+      bool is_client_redirect,
+      const base::UnguessableToken& devtools_navigation_token,
+      std::unique_ptr<WebNavigationParams>,
+      std::unique_ptr<WebDocumentLoader::ExtraData>) = 0;
+
+ protected:
+  explicit WebNavigationControl(WebTreeScopeType scope)
+      : WebLocalFrame(scope) {}
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_NAVIGATION_CONTROL_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
index b084da30..cc746be 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
@@ -58,7 +58,6 @@
 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/build/scripts/templates/origin_trials.h.tmpl b/third_party/blink/renderer/build/scripts/templates/origin_trials.h.tmpl
index b1ffe33..7f4ff3b 100644
--- a/third_party/blink/renderer/build/scripts/templates/origin_trials.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/origin_trials.h.tmpl
@@ -11,6 +11,11 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
+#define ASSERT_ORIGIN_TRIAL(feature) \
+static_assert(std::is_same<decltype(::blink::origin_trials::feature##Enabled(\
+                                        nullptr)), bool>(), \
+              #feature " must be part of an origin trial");
+
 namespace blink {
 
 class ExecutionContext;
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_fuzzer.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_fuzzer.cc
index b6174c1..7d1c0e3 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_fuzzer.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths_fuzzer.cc
@@ -18,7 +18,7 @@
   blink::FuzzedDataProvider provider(data, size);
 
   const auto property_id =
-      blink::convertToCSSPropertyID(provider.ConsumeInt32InRange(
+      blink::convertToCSSPropertyID(provider.ConsumeIntegralInRange<int>(
           blink::firstCSSProperty, blink::lastCSSProperty));
   const auto data_string = provider.ConsumeRemainingBytes();
 
diff --git a/third_party/blink/renderer/core/editing/frame_caret_test.cc b/third_party/blink/renderer/core/editing/frame_caret_test.cc
index 789bde3..44f12350 100644
--- a/third_party/blink/renderer/core/editing/frame_caret_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_caret_test.cc
@@ -11,21 +11,21 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/page/focus_controller.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
 class FrameCaretTest : public EditingTestBase {
  public:
   FrameCaretTest()
-      : was_running_layout_test_(LayoutTestSupport::IsRunningLayoutTest()) {
-    // The caret blink timer doesn't work if isRunningLayoutTest() because
-    // LayoutTheme::caretBlinkInterval() returns 0.
-    LayoutTestSupport::SetIsRunningLayoutTest(false);
+      : was_running_layout_test_(WebTestSupport::IsRunningWebTest()) {
+    // The caret blink timer doesn't work if IsRunningWebTest() because
+    // LayoutTheme::CaretBlinkInterval() returns 0.
+    WebTestSupport::SetIsRunningWebTest(false);
   }
   ~FrameCaretTest() override {
-    LayoutTestSupport::SetIsRunningLayoutTest(was_running_layout_test_);
+    WebTestSupport::SetIsRunningWebTest(was_running_layout_test_);
   }
 
   static bool ShouldBlinkCaret(const FrameCaret& caret) {
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
index 1a579cf..af0b1c6d 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -86,7 +86,6 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/web_task_runner.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 31c018f..6a958b1 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -2228,8 +2228,7 @@
   EXPECT_EQ(0, child_document->FirstBodyElement()->GetIntegralAttribute(
                    html_names::kMarginheightAttr));
 
-  LocalFrameView* frame_view =
-      static_cast<WebLocalFrameImpl*>(local_frame)->GetFrameView();
+  LocalFrameView* frame_view = local_frame->GetFrameView();
   EXPECT_EQ(nullptr, frame_view->LayoutViewport()->HorizontalScrollbar());
   EXPECT_EQ(nullptr, frame_view->LayoutViewport()->VerticalScrollbar());
 }
@@ -5144,7 +5143,7 @@
   main_frame->EnsureTextFinder().ResetMatchCount();
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
                                                         search_text, *options);
   }
@@ -5216,7 +5215,7 @@
   main_frame->EnsureTextFinder().ResetMatchCount();
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
                                                         search_text, *options);
   }
@@ -5228,7 +5227,7 @@
       mojom::StopFindAction::kStopFindActionClearSelection);
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
                                                         search_text, *options);
   }
@@ -5245,7 +5244,7 @@
   main_frame->EnsureTextFinder().ResetMatchCount();
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(
         kFindIdentifier, search_text_new, *options);
   }
@@ -5293,7 +5292,7 @@
   main_frame->EnsureTextFinder().ResetMatchCount();
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
                                                         search_text, *options);
   }
@@ -5324,7 +5323,7 @@
   find_in_page_client.SetFrame(main_frame);
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
         kFindIdentifier, search_text, *options, false));
   }
@@ -5337,7 +5336,7 @@
   main_frame->EnsureTextFinder().ResetMatchCount();
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
                                                         search_text, *options);
   }
@@ -5368,7 +5367,7 @@
   find_in_page_client.SetFrame(main_frame);
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
         kFindIdentifier, search_text, *options, false));
   }
@@ -5378,7 +5377,7 @@
   main_frame->EnsureTextFinder().ResetMatchCount();
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
                                                         search_text, *options);
   }
@@ -5388,7 +5387,7 @@
   RemoveElementById(main_frame, "frame");
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
                                                         search_text, *options);
   }
@@ -5420,7 +5419,7 @@
   EXPECT_TRUE(!!main_frame->TraverseNext());
 
   for (WebLocalFrameImpl* frame = main_frame; frame;
-       frame = static_cast<WebLocalFrameImpl*>(frame->TraverseNext())) {
+       frame = ToWebLocalFrameImpl(frame->TraverseNext())) {
     EXPECT_FALSE(frame->GetFindInPage()->FindInternal(
         kFindIdentifier, search_text, *options, false));
   }
@@ -9635,7 +9634,7 @@
   ASSERT_EQ(MainFrame()->FirstChild(), remote_frame);
 
   RemoteToLocalSwapWebFrameClient client(remote_frame);
-  WebLocalFrame* local_frame =
+  WebLocalFrameImpl* local_frame =
       frame_test_helpers::CreateProvisional(*remote_frame, &client);
   local_frame->SetCommittedFirstRealLoad();
   frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
@@ -12551,9 +12550,9 @@
   // content shouldn't crash. It should return NoLoadInProgress. This is so the
   // caller won't attempt to replace the correctly empty frame with an error
   // page.
-  EXPECT_EQ(
-      WebLocalFrame::NoLoadInProgress,
-      child->MaybeRenderFallbackContent(ResourceError::Failure(request.Url())));
+  EXPECT_EQ(WebNavigationControl::NoLoadInProgress,
+            ToWebLocalFrameImpl(child)->MaybeRenderFallbackContent(
+                ResourceError::Failure(request.Url())));
 }
 
 TEST_F(WebFrameTest, AltTextOnAboutBlankPage) {
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index c7bd80c..7580b4f 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -65,7 +65,7 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -127,7 +127,7 @@
   void ScheduleAnimation(const LocalFrameView*) override {
     // Calling scheduleAnimation on m_webView so WebViewTestProxy will call
     // beginFrame.
-    if (LayoutTestSupport::IsRunningLayoutTest()) {
+    if (WebTestSupport::IsRunningWebTest()) {
       popup_->web_view_->MainFrameImpl()
           ->FrameWidgetImpl()
           ->ScheduleAnimation();
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index ce892e10..0776bb4 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -4565,7 +4565,7 @@
   {
     frame->ExecuteScript(
         WebScriptSource("addEventListener('beforeunload', function() {});"));
-    web_view->MainFrame()->ToWebLocalFrame()->DispatchBeforeUnloadEvent(false);
+    web_view->MainFrameImpl()->DispatchBeforeUnloadEvent(false);
     EXPECT_FALSE(UseCounter::IsCounted(*document,
                                        WebFeature::kSubFrameBeforeUnloadFired));
   }
@@ -4576,9 +4576,7 @@
     frame->ExecuteScript(WebScriptSource(
         "document.getElementsByTagName('iframe')[0].contentWindow."
         "addEventListener('beforeunload', function() {});"));
-    web_view->MainFrame()
-        ->FirstChild()
-        ->ToWebLocalFrame()
+    ToWebLocalFrameImpl(web_view->MainFrame()->FirstChild()->ToWebLocalFrame())
         ->DispatchBeforeUnloadEvent(false);
 
     Document* child_document = ToLocalFrame(web_view_helper_.GetWebView()
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.cc b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
index 190a1c41..c3ec251 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
@@ -7,6 +7,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/platform/json/json_values.h"
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -272,6 +273,8 @@
                                  mojom::FeaturePolicyFeature::kSyncXHR);
     // Under origin trial: Should be made conditional on WebVR and WebXR
     // runtime flags once it is out of trial.
+    ASSERT_ORIGIN_TRIAL(WebVR);
+    ASSERT_ORIGIN_TRIAL(WebXR);
     default_feature_name_map.Set("vr", mojom::FeaturePolicyFeature::kWebVr);
     if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled()) {
       default_feature_name_map.Set(
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index d0cc186..27c7728 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -113,11 +113,12 @@
 }  // namespace
 
 void LoadFrame(WebLocalFrame* frame, const std::string& url) {
+  WebLocalFrameImpl* impl = ToWebLocalFrameImpl(frame);
   WebURL web_url(url_test_helpers::ToKURL(url));
   if (web_url.ProtocolIs("javascript")) {
-    frame->LoadJavaScriptURL(web_url);
+    impl->LoadJavaScriptURL(web_url);
   } else {
-    frame->CommitNavigation(
+    impl->CommitNavigation(
         WebURLRequest(web_url), blink::WebFrameLoadType::kStandard,
         blink::WebHistoryItem(), false, base::UnguessableToken::Create(),
         BuildDummyNavigationParams(), nullptr /* extra_data */);
@@ -128,15 +129,17 @@
 void LoadHTMLString(WebLocalFrame* frame,
                     const std::string& html,
                     const WebURL& base_url) {
-  frame->LoadHTMLString(WebData(html.data(), html.size()), base_url);
+  WebLocalFrameImpl* impl = ToWebLocalFrameImpl(frame);
+  impl->LoadHTMLString(WebData(html.data(), html.size()), base_url, WebURL());
   PumpPendingRequestsForFrameToLoad(frame);
 }
 
 void LoadHistoryItem(WebLocalFrame* frame,
                      const WebHistoryItem& item,
                      mojom::FetchCacheMode cache_mode) {
+  WebLocalFrameImpl* impl = ToWebLocalFrameImpl(frame);
   HistoryItem* history_item = item;
-  frame->CommitNavigation(
+  impl->CommitNavigation(
       WrappedResourceRequest(history_item->GenerateResourceRequest(cache_mode)),
       WebFrameLoadType::kBackForward, item, false /* is_client_redirect */,
       base::UnguessableToken::Create(), BuildDummyNavigationParams(),
@@ -429,7 +432,7 @@
                               std::unique_ptr<TestWebFrameClient> self_owned) {
   DCHECK(!frame_);
   DCHECK(!self_owned || self_owned.get() == this);
-  frame_ = frame;
+  frame_ = ToWebLocalFrameImpl(frame);
   self_owned_ = std::move(self_owned);
 }
 
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index 0d3ddec..ccfbc32 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -300,7 +300,7 @@
 
   static bool IsLoading() { return loads_in_progress_ > 0; }
 
-  WebLocalFrame* Frame() const { return frame_; }
+  WebNavigationControl* Frame() const { return frame_; }
   // Pass ownership of the TestWebFrameClient to |self_owned| here if the
   // TestWebFrameClient should delete itself on frame detach.
   void Bind(WebLocalFrame*,
@@ -344,7 +344,7 @@
 
   // This is null from when the client is created until it is initialized with
   // Bind().
-  WebLocalFrame* frame_ = nullptr;
+  WebNavigationControl* frame_ = nullptr;
 
   std::unique_ptr<WebWidgetClient> owned_widget_client_;
 };
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index f564f832..2c046e111 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1744,7 +1744,7 @@
     WebTreeScopeType scope,
     WebLocalFrameClient* client,
     blink::InterfaceRegistry* interface_registry)
-    : WebLocalFrame(scope),
+    : WebNavigationControl(scope),
       client_(client),
       local_frame_client_(LocalFrameClientImpl::Create(this)),
       autofill_client_(nullptr),
@@ -2125,7 +2125,7 @@
       std::move(navigation_params), std::move(navigation_data));
 }
 
-WebLocalFrame::FallbackContentResult
+WebNavigationControl::FallbackContentResult
 WebLocalFrameImpl::MaybeRenderFallbackContent(const WebURLError& error) const {
   DCHECK(GetFrame());
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 281cf46..0f6a29e 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -41,6 +41,7 @@
 #include "third_party/blink/public/web/devtools_agent.mojom-blink.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
 #include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_navigation_control.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/core/exported/web_input_method_controller_impl.h"
@@ -82,7 +83,7 @@
 // Implementation of WebFrame, note that this is a reference counted object.
 class CORE_EXPORT WebLocalFrameImpl final
     : public GarbageCollectedFinalized<WebLocalFrameImpl>,
-      public WebLocalFrame {
+      public WebNavigationControl {
  public:
   // WebFrame methods:
   // TODO(dcheng): Fix sorting here; a number of method have been moved to
@@ -262,6 +263,48 @@
   WebLocalFrameImpl* LocalRoot() override;
   WebFrame* FindFrameByName(const WebString& name) override;
   void SendPings(const WebURL& destination_url) override;
+  void ReportContentSecurityPolicyViolation(
+      const blink::WebContentSecurityPolicyViolation&) override;
+  bool IsLoading() const override;
+  bool IsNavigationScheduledWithin(double interval) const override;
+  void NotifyUserActivation() override;
+  void BlinkFeatureUsageReport(const std::set<int>& features) override;
+  void MixedContentFound(const WebURL& main_resource_url,
+                         const WebURL& mixed_content_url,
+                         mojom::RequestContextType,
+                         bool was_allowed,
+                         bool had_redirect,
+                         const WebSourceLocation&) override;
+  void SendOrientationChangeEvent() override;
+  WebSandboxFlags EffectiveSandboxFlags() const override;
+  void DidCallAddSearchProvider() override;
+  void DidCallIsSearchProviderInstalled() override;
+  void ReplaceSelection(const WebString&) override;
+  bool FindForTesting(int identifier,
+                      const WebString& search_text,
+                      bool match_case,
+                      bool forward,
+                      bool force,
+                      bool find_next,
+                      bool wrap_within_frame) override;
+  void SetTickmarks(const WebVector<WebRect>&) override;
+  WebNode ContextMenuNode() const override;
+  WebFrameWidget* FrameWidget() const override;
+  void CopyImageAt(const WebPoint&) override;
+  void SaveImageAt(const WebPoint&) override;
+  void UsageCountChromeLoadTimes(const WebString& metric) override;
+  FrameScheduler* Scheduler() const override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
+  WebInputMethodController* GetInputMethodController() override;
+  void ExtractSmartClipData(WebRect rect_in_viewport,
+                            WebString& clip_text,
+                            WebString& clip_html,
+                            WebRect& clip_rect) override;
+  void AdvanceFocusInForm(WebFocusType) override;
+  void PerformMediaPlayerAction(const WebPoint&,
+                                const WebMediaPlayerAction&) override;
+
+  // WebNavigationControl methods:
   bool DispatchBeforeUnloadEvent(bool) override;
   void CommitNavigation(
       const WebURLRequest&,
@@ -292,19 +335,7 @@
   FallbackContentResult MaybeRenderFallbackContent(
       const WebURLError&) const override;
   void RenderFallbackContent() const override;
-  void ReportContentSecurityPolicyViolation(
-      const blink::WebContentSecurityPolicyViolation&) override;
-  bool IsLoading() const override;
-  bool IsNavigationScheduledWithin(double interval) const override;
   void SetCommittedFirstRealLoad() override;
-  void NotifyUserActivation() override;
-  void BlinkFeatureUsageReport(const std::set<int>& features) override;
-  void MixedContentFound(const WebURL& main_resource_url,
-                         const WebURL& mixed_content_url,
-                         mojom::RequestContextType,
-                         bool was_allowed,
-                         bool had_redirect,
-                         const WebSourceLocation&) override;
   void ClientDroppedNavigation() override;
   void MarkAsLoading() override;
   bool CreatePlaceholderDocumentLoader(
@@ -315,34 +346,6 @@
       const base::UnguessableToken& devtools_navigation_token,
       std::unique_ptr<WebNavigationParams>,
       std::unique_ptr<WebDocumentLoader::ExtraData>) override;
-  void SendOrientationChangeEvent() override;
-  WebSandboxFlags EffectiveSandboxFlags() const override;
-  void DidCallAddSearchProvider() override;
-  void DidCallIsSearchProviderInstalled() override;
-  void ReplaceSelection(const WebString&) override;
-  bool FindForTesting(int identifier,
-                      const WebString& search_text,
-                      bool match_case,
-                      bool forward,
-                      bool force,
-                      bool find_next,
-                      bool wrap_within_frame) override;
-  void SetTickmarks(const WebVector<WebRect>&) override;
-  WebNode ContextMenuNode() const override;
-  WebFrameWidget* FrameWidget() const override;
-  void CopyImageAt(const WebPoint&) override;
-  void SaveImageAt(const WebPoint&) override;
-  void UsageCountChromeLoadTimes(const WebString& metric) override;
-  FrameScheduler* Scheduler() const override;
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
-  WebInputMethodController* GetInputMethodController() override;
-  void ExtractSmartClipData(WebRect rect_in_viewport,
-                            WebString& clip_text,
-                            WebString& clip_html,
-                            WebRect& clip_rect) override;
-  void AdvanceFocusInForm(WebFocusType) override;
-  void PerformMediaPlayerAction(const WebPoint&,
-                                const WebMediaPlayerAction&) override;
 
   void InitializeCoreFrame(Page&, FrameOwner*, const AtomicString& name);
   LocalFrame* GetFrame() const { return frame_.Get(); }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index 3d16585..c711d30 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -88,13 +88,19 @@
           GetOrCreateResourceDispatcher()
               ? GetOrCreateResourceDispatcher()->GetWeakPtr()
               : nullptr;
-      if (Is3d()) {
-        const CanvasResourceProvider::ResourceUsage usage =
-            SharedGpuContext::IsGpuCompositingEnabled()
-                ? CanvasResourceProvider::kAcceleratedCompositedResourceUsage
-                : CanvasResourceProvider::kSoftwareCompositedResourceUsage;
 
-        CanvasResourceProvider::PresentationMode presentation_mode =
+      if (Is3d()) {
+        CanvasResourceProvider::ResourceUsage usage;
+        if (SharedGpuContext::IsGpuCompositingEnabled()) {
+          if (LowLatencyEnabled())
+            usage = CanvasResourceProvider::kAcceleratedDirectResourceUsage;
+          else
+            usage = CanvasResourceProvider::kAcceleratedCompositedResourceUsage;
+        } else {
+          usage = CanvasResourceProvider::kSoftwareCompositedResourceUsage;
+        }
+
+        const CanvasResourceProvider::PresentationMode presentation_mode =
             RuntimeEnabledFeatures::WebGLImageChromiumEnabled()
                 ? CanvasResourceProvider::kAllowImageChromiumPresentationMode
                 : CanvasResourceProvider::kDefaultPresentationMode;
@@ -111,10 +117,15 @@
         const bool want_acceleration =
             hint == kPreferAcceleration && ShouldAccelerate2dContext();
 
-        const CanvasResourceProvider::ResourceUsage usage =
-            want_acceleration
-                ? CanvasResourceProvider::kAcceleratedCompositedResourceUsage
-                : CanvasResourceProvider::kSoftwareCompositedResourceUsage;
+        CanvasResourceProvider::ResourceUsage usage;
+        if (want_acceleration) {
+          if (LowLatencyEnabled())
+            usage = CanvasResourceProvider::kAcceleratedDirectResourceUsage;
+          else
+            usage = CanvasResourceProvider::kAcceleratedCompositedResourceUsage;
+        } else {
+          usage = CanvasResourceProvider::kSoftwareCompositedResourceUsage;
+        }
 
         const CanvasResourceProvider::PresentationMode presentation_mode =
             (RuntimeEnabledFeatures::Canvas2dImageChromiumEnabled() ||
diff --git a/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc b/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc
index e21e5169..583d803 100644
--- a/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc
+++ b/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc
@@ -37,8 +37,8 @@
 #include "third_party/blink/renderer/core/layout/layout_details_marker.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -161,10 +161,10 @@
 void PickerIndicatorElement::DidNotifySubtreeInsertionsToDocument() {
   if (!GetDocument().ExistingAXObjectCache())
     return;
-  // Don't make this focusable if we are in layout tests in order to avoid
+  // Don't make this focusable if we are in web tests in order to avoid
   // breaking existing tests.
-  // FIXME: We should have a way to disable accessibility in layout tests.
-  if (LayoutTestSupport::IsRunningLayoutTest())
+  // FIXME: We should have a way to disable accessibility in web tests.
+  if (WebTestSupport::IsRunningWebTest())
     return;
   setAttribute(kTabindexAttr, "0");
   setAttribute(kAriaHaspopupAttr, "menu");
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index a250915..ae5e8ad 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -56,8 +56,8 @@
 #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/histogram.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -489,8 +489,8 @@
     exitPictureInPicture(base::DoNothing());
 
   if (GetWebMediaPlayer()) {
-    // FIXME: There is no embedder-side handling in layout test mode.
-    if (!LayoutTestSupport::IsRunningLayoutTest())
+    // FIXME: There is no embedder-side handling in web test mode.
+    if (!WebTestSupport::IsRunningWebTest())
       GetWebMediaPlayer()->EnteredFullscreen();
     GetWebMediaPlayer()->OnDisplayTypeChanged(DisplayType());
   }
diff --git a/third_party/blink/renderer/core/html/parser/text_resource_decoder_for_fuzzing.h b/third_party/blink/renderer/core/html/parser/text_resource_decoder_for_fuzzing.h
index f407437c..d71506c 100644
--- a/third_party/blink/renderer/core/html/parser/text_resource_decoder_for_fuzzing.h
+++ b/third_party/blink/renderer/core/html/parser/text_resource_decoder_for_fuzzing.h
@@ -22,7 +22,7 @@
   static TextResourceDecoderOptions FuzzedOption(
       FuzzedDataProvider& fuzzed_data) {
     switch (static_cast<TextResourceDecoderOptions::EncodingDetectionOption>(
-        fuzzed_data.ConsumeInt32InRange(
+        fuzzed_data.ConsumeIntegralInRange<int32_t>(
             TextResourceDecoderOptions::kUseAllAutoDetection,
             TextResourceDecoderOptions::kAlwaysUseUTF8ForText))) {
       case TextResourceDecoderOptions::kUseAllAutoDetection:
@@ -42,7 +42,7 @@
   static TextResourceDecoderOptions::ContentType FuzzedContentType(
       FuzzedDataProvider& fuzzed_data) {
     return static_cast<TextResourceDecoderOptions::ContentType>(
-        fuzzed_data.ConsumeInt32InRange(
+        fuzzed_data.ConsumeIntegralInRange<int32_t>(
             TextResourceDecoderOptions::kPlainTextContent,
             TextResourceDecoderOptions::kMaxContentType));
   }
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc
index 35f13b7..a1a06e4 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -17,8 +17,8 @@
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -232,7 +232,7 @@
     v8_session_state_json_.Set(ToCoreString(v8_session_->stateJSON()));
   // Make tests more predictable by flushing all sessions before sending
   // protocol response in any of them.
-  if (LayoutTestSupport::IsRunningLayoutTest())
+  if (WebTestSupport::IsRunningWebTest())
     agent_->FlushProtocolNotifications();
   host_ptr_->DispatchProtocolResponse(message, call_id,
                                       session_state_.TakeUpdates());
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_agent.cc b/third_party/blink/renderer/core/inspector/inspector_audits_agent.cc
index b6ac5a8..a0885223 100644
--- a/third_party/blink/renderer/core/inspector/inspector_audits_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_audits_agent.cc
@@ -16,7 +16,7 @@
 using protocol::Maybe;
 using protocol::Response;
 
-namespace EncodingEnum = protocol::Audits::GetEncodedResponse::EncodingEnum;
+namespace encoding_enum = protocol::Audits::GetEncodedResponse::EncodingEnum;
 
 namespace {
 
@@ -82,8 +82,8 @@
     Maybe<protocol::Binary>* out_body,
     int* out_original_size,
     int* out_encoded_size) {
-  DCHECK(encoding == EncodingEnum::Jpeg || encoding == EncodingEnum::Png ||
-         encoding == EncodingEnum::Webp);
+  DCHECK(encoding == encoding_enum::Jpeg || encoding == encoding_enum::Png ||
+         encoding == encoding_enum::Webp);
 
   String body;
   bool is_base64_encoded;
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 1c8b069d..a303dac 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -19,7 +19,7 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
 #include "third_party/blink/renderer/platform/graphics/path.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -437,10 +437,10 @@
 
   // Just for testing, invert the content color for nodes rendered by LayoutNG.
   // TODO(layout-dev): Stop munging the color before NG ships. crbug.com/869866
-  Color content_color = layout_object->IsLayoutNGObject() &&
-                                !LayoutTestSupport::IsRunningLayoutTest()
-                            ? Color(highlight_config.content.Rgb() ^ 0x00ffffff)
-                            : highlight_config.content;
+  Color content_color =
+      layout_object->IsLayoutNGObject() && !WebTestSupport::IsRunningWebTest()
+          ? Color(highlight_config.content.Rgb() ^ 0x00ffffff)
+          : highlight_config.content;
 
   Vector<FloatQuad> svg_quads;
   if (BuildSVGQuads(node, svg_quads)) {
diff --git a/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc b/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
index dfaef897..bb59c3a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
@@ -88,6 +88,7 @@
       resource_request.SetCacheMode(mojom::FetchCacheMode::kOnlyIfCached);
     }
     resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
+    resource_request.SetSkipServiceWorker(true);
 
     if (!resource_request.Url().GetString().IsEmpty()) {
       urls_to_fetch.insert(resource_request.Url().GetString());
diff --git a/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc b/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc
index d163b7c8..9b47ddb 100644
--- a/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc
+++ b/third_party/blink/renderer/core/inspector/worker_inspector_controller.cc
@@ -45,7 +45,6 @@
 #include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/web_thread_supporting_gc.h"
 
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index 9a66a2462..b5aee03 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -58,10 +58,10 @@
 #include "third_party/blink/renderer/platform/fonts/string_truncator.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/touch_action.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
 #include "third_party/blink/renderer/platform/theme.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "ui/native_theme/native_theme.h"
 
@@ -101,7 +101,7 @@
 
 // Wrapper function defined in WebKit.h
 void SetMockThemeEnabledForTest(bool value) {
-  LayoutTestSupport::SetMockThemeEnabledForTest(value);
+  WebTestSupport::SetMockThemeEnabledForTest(value);
   LayoutTheme::GetTheme().DidChangeThemeEngine();
 }
 
@@ -682,8 +682,8 @@
 TimeDelta LayoutTheme::CaretBlinkInterval() const {
   // Disable the blinking caret in layout test mode, as it introduces
   // a race condition for the pixel tests. http://b/1198440
-  return LayoutTestSupport::IsRunningLayoutTest() ? TimeDelta()
-                                                  : caret_blink_interval_;
+  return WebTestSupport::IsRunningWebTest() ? TimeDelta()
+                                            : caret_blink_interval_;
 }
 
 static FontDescription& GetCachedFontDescription(CSSValueID system_font_id) {
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.cc b/third_party/blink/renderer/core/layout/layout_theme_default.cc
index d981319c..4010a8d 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.cc
@@ -32,7 +32,7 @@
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/data_resource_helper.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -44,7 +44,7 @@
 static const float kMaxCancelButtonSize = 21;
 
 static bool UseMockTheme() {
-  return LayoutTestSupport::IsMockThemeEnabledForTest();
+  return WebTestSupport::IsMockThemeEnabledForTest();
 }
 
 unsigned LayoutThemeDefault::active_selection_background_color_ = 0xff1e90ff;
diff --git a/third_party/blink/renderer/core/layout/layout_theme_mac.mm b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
index d35622c..0e82ef5 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_mac.mm
+++ b/third_party/blink/renderer/core/layout/layout_theme_mac.mm
@@ -36,7 +36,6 @@
 #import "third_party/blink/renderer/platform/data_resource_helper.h"
 #import "third_party/blink/renderer/platform/fonts/string_truncator.h"
 #import "third_party/blink/renderer/platform/graphics/bitmap_image.h"
-#import "third_party/blink/renderer/platform/layout_test_support.h"
 #import "third_party/blink/renderer/platform/mac/color_mac.h"
 #import "third_party/blink/renderer/platform/mac/theme_mac.h"
 #import "third_party/blink/renderer/platform/mac/version_util_mac.h"
@@ -44,6 +43,7 @@
 #import "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #import "third_party/blink/renderer/platform/text/platform_locale.h"
 #import "third_party/blink/renderer/platform/theme.h"
+#import "third_party/blink/renderer/platform/web_test_support.h"
 
 // The methods in this file are specific to the Mac OS X platform.
 
@@ -970,7 +970,7 @@
 }
 
 bool LayoutThemeMac::UsesTestModeFocusRingColor() const {
-  return LayoutTestSupport::IsRunningLayoutTest();
+  return WebTestSupport::IsRunningWebTest();
 }
 
 NSView* LayoutThemeMac::DocumentView() const {
diff --git a/third_party/blink/renderer/core/layout/layout_theme_mobile.cc b/third_party/blink/renderer/core/layout/layout_theme_mobile.cc
index f0b845c..0d602a5 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_mobile.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_mobile.cc
@@ -30,7 +30,7 @@
 #include "third_party/blink/public/platform/web_theme_engine.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/data_resource_helper.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -51,7 +51,7 @@
 }
 
 void LayoutThemeMobile::AdjustInnerSpinButtonStyle(ComputedStyle& style) const {
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     // Match Linux spin button style in layout tests.
     // FIXME: Consider removing the conditional if a future Android theme
     // matches this.
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 72f40ed9..f93003f 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -97,8 +97,8 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/graphics/touch_action.h"
 #include "third_party/blink/renderer/platform/histogram.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
@@ -211,7 +211,7 @@
 bool ChromeClientImpl::CanTakeFocus(WebFocusType) {
   // For now the browser can always take focus if we're not running layout
   // tests.
-  return !LayoutTestSupport::IsRunningLayoutTest();
+  return !WebTestSupport::IsRunningWebTest();
 }
 
 void ChromeClientImpl::TakeFocus(WebFocusType type) {
diff --git a/third_party/blink/renderer/core/page/validation_message_client_impl.cc b/third_party/blink/renderer/core/page/validation_message_client_impl.cc
index c6623a5..2612a44 100644
--- a/third_party/blink/renderer/core/page/validation_message_client_impl.cc
+++ b/third_party/blink/renderer/core/page/validation_message_client_impl.cc
@@ -36,7 +36,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/validation_message_overlay_delegate.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -94,7 +94,7 @@
 }
 
 void ValidationMessageClientImpl::HideValidationMessage(const Element& anchor) {
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     HideValidationMessageImmediately(anchor);
     return;
   }
@@ -140,7 +140,7 @@
 
 void ValidationMessageClientImpl::CheckAnchorStatus(TimerBase*) {
   DCHECK(current_anchor_);
-  if ((!LayoutTestSupport::IsRunningLayoutTest() &&
+  if ((!WebTestSupport::IsRunningWebTest() &&
        CurrentTimeTicks() >= finish_time_) ||
       !CurrentView()) {
     HideValidationMessage(*current_anchor_);
diff --git a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
index 0fd8a0f8..9fd5f4a 100644
--- a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
+++ b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
@@ -16,8 +16,8 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/page_popup_client.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -160,7 +160,7 @@
   frame->ForceSynchronousDocumentInstall("text/html", data);
 
   Element& container = GetElementById("container");
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     container.SetInlineStyleProperty(CSSPropertyTransition, "none");
     GetElementById("icon").SetInlineStyleProperty(CSSPropertyTransition,
                                                   "none");
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
index 658b27b..748d83ec 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -59,7 +59,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/skia/include/core/SkMatrix44.h"
 #include "ui/gfx/geometry/rect.h"
@@ -310,11 +310,10 @@
     curve->AddKeyframe(CompositorFloatKeyframe(
         extra_duration_required.InSecondsF(), kStartOpacity, timing_function));
   }
-  // For layout tests we don't fade out.
+  // For web tests we don't fade out.
   curve->AddKeyframe(CompositorFloatKeyframe(
       (kFadeDuration + extra_duration_required).InSecondsF(),
-      LayoutTestSupport::IsRunningLayoutTest() ? kStartOpacity : 0,
-      timing_function));
+      WebTestSupport::IsRunningWebTest() ? kStartOpacity : 0, timing_function));
 
   std::unique_ptr<CompositorKeyframeModel> keyframe_model =
       CompositorKeyframeModel::Create(
diff --git a/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc b/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc
index df01f76..341763c 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc
@@ -10,8 +10,8 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -20,12 +20,12 @@
   PaintLayerClipperTest() : RenderingTest(EmptyLocalFrameClient::Create()) {}
 
   void SetUp() override {
-    LayoutTestSupport::SetMockThemeEnabledForTest(true);
+    WebTestSupport::SetMockThemeEnabledForTest(true);
     RenderingTest::SetUp();
   }
 
   void TearDown() override {
-    LayoutTestSupport::SetMockThemeEnabledForTest(false);
+    WebTestSupport::SetMockThemeEnabledForTest(false);
     RenderingTest::TearDown();
   }
 };
diff --git a/third_party/blink/renderer/core/paint/theme_painter_default.cc b/third_party/blink/renderer/core/paint/theme_painter_default.cc
index ed22010..d4f4e51b 100644
--- a/third_party/blink/renderer/core/paint/theme_painter_default.cc
+++ b/third_party/blink/renderer/core/paint/theme_painter_default.cc
@@ -35,7 +35,7 @@
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -44,7 +44,7 @@
 const unsigned kDefaultButtonBackgroundColor = 0xffdddddd;
 
 bool UseMockTheme() {
-  return LayoutTestSupport::IsMockThemeEnabledForTest();
+  return WebTestSupport::IsMockThemeEnabledForTest();
 }
 
 WebThemeEngine::State GetWebThemeState(const Node* node) {
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc b/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc
index 0d3b2ee..d7f792f5 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc
@@ -41,15 +41,15 @@
 #include "third_party/blink/renderer/platform/geometry/int_rect_outsets.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
 namespace {
 
 static bool UseMockTheme() {
-  return LayoutTestSupport::IsRunningLayoutTest();
+  return WebTestSupport::IsRunningWebTest();
 }
 
 // Contains a flag indicating whether WebThemeEngine should paint a UI widget
diff --git a/third_party/blink/renderer/core/testing/sim/sim_test.cc b/third_party/blink/renderer/core/testing/sim/sim_test.cc
index 8a3e27e6..ab8aff7c 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_test.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_test.cc
@@ -12,8 +12,8 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -30,7 +30,7 @@
   // updateScrollerStyleForNewRecommendedScrollerStyle which then does
   // FrameView::scrollbarStyleChanged and will adjust the scrollbar existence
   // in the middle of a test.
-  LayoutTestSupport::SetMockThemeEnabledForTest(true);
+  WebTestSupport::SetMockThemeEnabledForTest(true);
   ScrollbarTheme::SetMockScrollbarsEnabled(true);
   // Threaded animations are usually enabled for blink. However these tests use
   // synchronous compositing, which can not run threaded animations.
@@ -46,7 +46,7 @@
   test::RunPendingTasks();
 
   Document::SetThreadedParsingEnabledForTesting(true);
-  LayoutTestSupport::SetMockThemeEnabledForTest(false);
+  WebTestSupport::SetMockThemeEnabledForTest(false);
   ScrollbarTheme::SetMockScrollbarsEnabled(false);
   content::TestBlinkWebUnitTestSupport::SetThreadedAnimationEnabled(true);
   WebCache::Clear();
diff --git a/third_party/blink/renderer/devtools/front_end/common/ResourceType.js b/third_party/blink/renderer/devtools/front_end/common/ResourceType.js
index ebc106c1..5366548 100644
--- a/third_party/blink/renderer/devtools/front_end/common/ResourceType.js
+++ b/third_party/blink/renderer/devtools/front_end/common/ResourceType.js
@@ -79,6 +79,19 @@
   }
 
   /**
+   * @param {string} name
+   * @return {?Common.ResourceType}
+   */
+  static fromName(name) {
+    for (const resourceTypeId in Common.resourceTypes) {
+      const resourceType = Common.resourceTypes[resourceTypeId];
+      if (resourceType.name() === name)
+        return resourceType;
+    }
+    return null;
+  }
+
+  /**
    * @param {string} url
    * @return {string|undefined}
    */
diff --git a/third_party/blink/renderer/devtools/front_end/har_importer/HARFormat.js b/third_party/blink/renderer/devtools/front_end/har_importer/HARFormat.js
index 14cf5cd..07a39899 100644
--- a/third_party/blink/renderer/devtools/front_end/har_importer/HARFormat.js
+++ b/third_party/blink/renderer/devtools/front_end/har_importer/HARFormat.js
@@ -163,6 +163,7 @@
     if (data['_initiator'])
       this._initiator = new HARImporter.HARInitiator(data['_initiator']);
     this._priority = HARImporter.HARBase._optionalString(data['_priority']);
+    this._resourceType = HARImporter.HARBase._optionalString(data['_resourceType']);
   }
 };
 
diff --git a/third_party/blink/renderer/devtools/front_end/har_importer/HARImporter.js b/third_party/blink/renderer/devtools/front_end/har_importer/HARImporter.js
index 081ac1a..aa982f44 100644
--- a/third_party/blink/renderer/devtools/front_end/har_importer/HARImporter.js
+++ b/third_party/blink/renderer/devtools/front_end/har_importer/HARImporter.js
@@ -116,12 +116,7 @@
 
     // Meta data.
     request.setRemoteAddress(entry.serverIPAddress || '', 80);  // Har does not support port numbers.
-    let resourceType = (pageLoad && pageLoad.mainRequest === request) ?
-        Common.resourceTypes.Document :
-        Common.ResourceType.fromMimeType(entry.response.content.mimeType);
-    if (!resourceType)
-      resourceType = Common.ResourceType.fromURL(entry.request.url) || Common.resourceTypes.Other;
-    request.setResourceType(resourceType);
+    request.setResourceType(HARImporter.Importer._getResourceType(request, entry, pageLoad));
 
     const priority = entry.customAsString('priority');
     if (Protocol.Network.ResourcePriority.hasOwnProperty(priority))
@@ -132,6 +127,34 @@
 
   /**
    * @param {!SDK.NetworkRequest} request
+   * @param {!HARImporter.HAREntry} entry
+   * @param {?SDK.NetworkLog.PageLoad} pageLoad
+   * @return {!Common.ResourceType}
+   */
+  static _getResourceType(request, entry, pageLoad) {
+    const customResourceTypeName = entry.customAsString('resourceType');
+    if (customResourceTypeName) {
+      const customResourceType = Common.ResourceType.fromName(customResourceTypeName);
+      if (customResourceType)
+        return customResourceType;
+    }
+
+    if (pageLoad && pageLoad.mainRequest === request)
+      return Common.resourceTypes.Document;
+
+    const resourceTypeFromMime = Common.ResourceType.fromMimeType(entry.response.content.mimeType);
+    if (resourceTypeFromMime !== Common.resourceTypes.Other)
+      return resourceTypeFromMime;
+
+    const resourceTypeFromUrl = Common.ResourceType.fromURL(entry.request.url);
+    if (resourceTypeFromUrl)
+      return resourceTypeFromUrl;
+
+    return Common.resourceTypes.Other;
+  }
+
+  /**
+   * @param {!SDK.NetworkRequest} request
    * @param {number} issueTime
    * @param {number} entryTotalDuration
    * @param {!HARImporter.HARTimings} timings
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/HARLog.js b/third_party/blink/renderer/devtools/front_end/sdk/HARLog.js
index e39b3fe..6bc75d2 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/HARLog.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/HARLog.js
@@ -169,7 +169,8 @@
       // IPv6 address should not have square brackets per (https://tools.ietf.org/html/rfc2373#section-2.2).
       serverIPAddress: ipAddress.replace(/\[\]/g, ''),
       _initiator: exportedInitiator,
-      _priority: harEntry._request.priority()
+      _priority: harEntry._request.priority(),
+      _resourceType: harEntry._request.resourceType().name()
     };
 
     // Chrome specific.
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 051ab3a..2202534 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -346,6 +346,7 @@
     "webaudio/audio_basic_processor_handler_test.cc",
     "webaudio/audio_context_autoplay_test.cc",
     "webaudio/audio_context_test.cc",
+    "webaudio/audio_node_input_test.cc",
     "webaudio/audio_worklet_global_scope_test.cc",
     "webaudio/audio_worklet_thread_test.cc",
     "webaudio/convolver_node_test.cc",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection.cc b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
index 22d347f..71e240e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
@@ -248,7 +248,7 @@
   DCHECK(selection.AssertValid());
   Document* document = selection.Base().GetDocument();
   if (!document) {
-    NOTREACHED();
+    NOTREACHED() << "Valid DOM selections should have an attached document.";
     return false;
   }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc b/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
index 068053c..3477e1d 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
@@ -8,14 +8,18 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/dom/range.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/editing/position.h"
 #include "third_party/blink/renderer/core/editing/selection_template.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_position.h"
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 namespace test {
@@ -24,16 +28,180 @@
 // Basic tests.
 //
 
-TEST_F(AccessibilitySelectionTest, SetSelectionInText) {
-  SetBodyInnerHTML(R"HTML(<p id='paragraph'>Hello</p>)HTML");
+TEST_F(AccessibilitySelectionTest, FromCurrentSelection) {
+  GetPage().GetSettings().SetScriptEnabled(true);
+  SetBodyInnerHTML(R"HTML(
+      <p id="paragraph1">Hello.</p>
+      <p id="paragraph2">How are you?</p>
+      )HTML");
 
-  const Node* text = GetElementById("paragraph")->firstChild();
+  ASSERT_FALSE(AXSelection::FromCurrentSelection(GetDocument()).IsValid());
+
+  Element* const script_element =
+      GetDocument().CreateRawElement(html_names::kScriptTag);
+  ASSERT_NE(nullptr, script_element);
+  script_element->setTextContent(R"SCRIPT(
+      let text1 = document.querySelectorAll('p')[0].firstChild;
+      let paragraph2 = document.querySelectorAll('p')[1];
+      let range = document.createRange();
+      range.setStart(text1, 3);
+      range.setEnd(paragraph2, 1);
+      let selection = getSelection();
+      selection.removeAllRanges();
+      selection.addRange(range);
+      )SCRIPT");
+  GetDocument().body()->AppendChild(script_element);
+  UpdateAllLifecyclePhasesForTest();
+
+  const AXObject* ax_static_text_1 =
+      GetAXObjectByElementId("paragraph1")->FirstChild();
+  ASSERT_NE(nullptr, ax_static_text_1);
+  ASSERT_EQ(ax::mojom::Role::kStaticText, ax_static_text_1->RoleValue());
+  const AXObject* ax_paragraph_2 = GetAXObjectByElementId("paragraph2");
+  ASSERT_NE(nullptr, ax_paragraph_2);
+  ASSERT_EQ(ax::mojom::Role::kParagraph, ax_paragraph_2->RoleValue());
+
+  const auto ax_selection = AXSelection::FromCurrentSelection(GetDocument());
+  ASSERT_TRUE(ax_selection.IsValid());
+
+  EXPECT_TRUE(ax_selection.Base().IsTextPosition());
+  EXPECT_EQ(ax_static_text_1, ax_selection.Base().ContainerObject());
+  EXPECT_EQ(3, ax_selection.Base().TextOffset());
+
+  EXPECT_FALSE(ax_selection.Extent().IsTextPosition());
+  EXPECT_EQ(ax_paragraph_2, ax_selection.Extent().ContainerObject());
+  EXPECT_EQ(1, ax_selection.Extent().ChildIndex());
+
+  EXPECT_EQ(
+      "++<Paragraph>\n"
+      "++++<StaticText: Hel^lo.>\n"
+      "++<Paragraph>\n"
+      "++++<StaticText: How are you?>\n|",
+      GetSelectionText(ax_selection));
+}
+
+TEST_F(AccessibilitySelectionTest, ClearCurrentSelection) {
+  GetPage().GetSettings().SetScriptEnabled(true);
+  SetBodyInnerHTML(R"HTML(
+      <p>Hello.</p>
+      <p>How are you?</p>
+      )HTML");
+
+  Element* const script_element =
+      GetDocument().CreateRawElement(html_names::kScriptTag);
+  ASSERT_NE(nullptr, script_element);
+  script_element->setTextContent(R"SCRIPT(
+      let text1 = document.querySelectorAll('p')[0].firstChild;
+      let paragraph2 = document.querySelectorAll('p')[1];
+      let range = document.createRange();
+      range.setStart(text1, 3);
+      range.setEnd(paragraph2, 1);
+      let selection = getSelection();
+      selection.removeAllRanges();
+      selection.addRange(range);
+      )SCRIPT");
+  GetDocument().body()->AppendChild(script_element);
+  UpdateAllLifecyclePhasesForTest();
+
+  SelectionInDOMTree selection = Selection().GetSelectionInDOMTree();
+  ASSERT_FALSE(selection.IsNone());
+
+  AXSelection::ClearCurrentSelection(GetDocument());
+  selection = Selection().GetSelectionInDOMTree();
+  EXPECT_TRUE(selection.IsNone());
+
+  const auto ax_selection = AXSelection::FromCurrentSelection(GetDocument());
+  EXPECT_FALSE(ax_selection.IsValid());
+  EXPECT_EQ("", GetSelectionText(ax_selection));
+}
+
+TEST_F(AccessibilitySelectionTest, CancelSelect) {
+  GetPage().GetSettings().SetScriptEnabled(true);
+  SetBodyInnerHTML(R"HTML(
+      <p id="paragraph1">Hello.</p>
+      <p id="paragraph2">How are you?</p>
+      )HTML");
+
+  Element* const script_element =
+      GetDocument().CreateRawElement(html_names::kScriptTag);
+  ASSERT_NE(nullptr, script_element);
+  script_element->setTextContent(R"SCRIPT(
+      document.addEventListener("selectstart", (e) => {
+        e.preventDefault();
+      }, false);
+      )SCRIPT");
+  GetDocument().body()->AppendChild(script_element);
+  UpdateAllLifecyclePhasesForTest();
+
+  const AXObject* ax_static_text_1 =
+      GetAXObjectByElementId("paragraph1")->FirstChild();
+  ASSERT_NE(nullptr, ax_static_text_1);
+  ASSERT_EQ(ax::mojom::Role::kStaticText, ax_static_text_1->RoleValue());
+  const AXObject* ax_paragraph_2 = GetAXObjectByElementId("paragraph2");
+  ASSERT_NE(nullptr, ax_paragraph_2);
+  ASSERT_EQ(ax::mojom::Role::kParagraph, ax_paragraph_2->RoleValue());
+
+  AXSelection::Builder builder;
+  AXSelection ax_selection =
+      builder
+          .SetBase(AXPosition::CreatePositionInTextObject(*ax_static_text_1, 3))
+          .SetExtent(AXPosition::CreateLastPositionInObject(*ax_paragraph_2))
+          .Build();
+
+  EXPECT_FALSE(ax_selection.Select()) << "The operation has been cancelled.";
+  EXPECT_TRUE(Selection().GetSelectionInDOMTree().IsNone());
+  EXPECT_FALSE(AXSelection::FromCurrentSelection(GetDocument()).IsValid());
+
+  GetDocument().RemoveAllEventListeners();
+
+  EXPECT_TRUE(ax_selection.Select()) << "The operation should now go through.";
+  EXPECT_FALSE(Selection().GetSelectionInDOMTree().IsNone());
+  EXPECT_EQ(
+      "++<Paragraph>\n"
+      "++++<StaticText: Hel^lo.>\n"
+      "++<Paragraph>\n"
+      "++++<StaticText: How are you?>\n|",
+      GetSelectionText(AXSelection::FromCurrentSelection(GetDocument())));
+}
+
+TEST_F(AccessibilitySelectionTest, DocumentRangeMatchesSelection) {
+  SetBodyInnerHTML(R"HTML(
+      <p id="paragraph1">Hello.</p>
+      <p id="paragraph2">How are you?</p>
+      )HTML");
+
+  const AXObject* ax_static_text_1 =
+      GetAXObjectByElementId("paragraph1")->FirstChild();
+  ASSERT_NE(nullptr, ax_static_text_1);
+  ASSERT_EQ(ax::mojom::Role::kStaticText, ax_static_text_1->RoleValue());
+  const AXObject* ax_paragraph_2 = GetAXObjectByElementId("paragraph2");
+  ASSERT_NE(nullptr, ax_paragraph_2);
+  ASSERT_EQ(ax::mojom::Role::kParagraph, ax_paragraph_2->RoleValue());
+
+  AXSelection::Builder builder;
+  AXSelection ax_selection =
+      builder
+          .SetBase(AXPosition::CreatePositionInTextObject(*ax_static_text_1, 3))
+          .SetExtent(AXPosition::CreateLastPositionInObject(*ax_paragraph_2))
+          .Build();
+  EXPECT_TRUE(ax_selection.Select());
+  ASSERT_FALSE(Selection().GetSelectionInDOMTree().IsNone());
+  ASSERT_NE(nullptr, Selection().DocumentCachedRange());
+  EXPECT_EQ(String("lo.\n      How are you?"),
+            Selection().DocumentCachedRange()->toString());
+}
+
+TEST_F(AccessibilitySelectionTest, SetSelectionInText) {
+  SetBodyInnerHTML(R"HTML(<p id="paragraph">Hello</p>)HTML");
+
+  const Node* text = GetDocument().QuerySelector("p")->firstChild();
   ASSERT_NE(nullptr, text);
   ASSERT_TRUE(text->IsTextNode());
 
   const AXObject* ax_static_text =
       GetAXObjectByElementId("paragraph")->FirstChild();
   ASSERT_NE(nullptr, ax_static_text);
+  ASSERT_EQ(ax::mojom::Role::kStaticText, ax_static_text->RoleValue());
 
   const auto ax_base =
       AXPosition::CreatePositionInTextObject(*ax_static_text, 3);
@@ -54,15 +222,16 @@
 }
 
 TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) {
-  SetBodyInnerHTML(R"HTML(<p id='paragraph'>     Hello</p>)HTML");
+  SetBodyInnerHTML(R"HTML(<p id="paragraph">     Hello</p>)HTML");
 
-  const Node* text = GetElementById("paragraph")->firstChild();
+  const Node* text = GetDocument().QuerySelector("p")->firstChild();
   ASSERT_NE(nullptr, text);
   ASSERT_TRUE(text->IsTextNode());
 
   const AXObject* ax_static_text =
       GetAXObjectByElementId("paragraph")->FirstChild();
   ASSERT_NE(nullptr, ax_static_text);
+  ASSERT_EQ(ax::mojom::Role::kStaticText, ax_static_text->RoleValue());
 
   const auto ax_base =
       AXPosition::CreatePositionInTextObject(*ax_static_text, 3);
@@ -226,7 +395,7 @@
   // first <li>.
   ax_selection.Select(AXSelectionBehavior::kShrinkToValidDOMRange);
   const SelectionInDOMTree shrunk_selection =
-      GetFrame().Selection().GetSelectionInDOMTree();
+      Selection().GetSelectionInDOMTree();
 
   EXPECT_EQ(item_1, shrunk_selection.Base().AnchorNode());
   EXPECT_TRUE(shrunk_selection.Base().IsBeforeChildren());
@@ -238,7 +407,7 @@
   // |AXSelection| should move the anchor to before the first <li>.
   ax_selection.Select(AXSelectionBehavior::kExtendToValidDOMRange);
   const SelectionInDOMTree extended_selection =
-      GetFrame().Selection().GetSelectionInDOMTree();
+      Selection().GetSelectionInDOMTree();
 
   ASSERT_TRUE(extended_selection.Base().IsOffsetInAnchor());
   EXPECT_EQ(item_1->parentNode(), extended_selection.Base().AnchorNode());
diff --git a/third_party/blink/renderer/modules/locks/lock.idl b/third_party/blink/renderer/modules/locks/lock.idl
index 24d444a..182914a 100644
--- a/third_party/blink/renderer/modules/locks/lock.idl
+++ b/third_party/blink/renderer/modules/locks/lock.idl
@@ -5,8 +5,7 @@
 // https://wicg.github.io/web-locks/#lock
 [
     SecureContext,
-    Exposed=(Window,Worker),
-    RuntimeEnabled=WebLocksAPI
+    Exposed=(Window,Worker)
 ] interface Lock {
     readonly attribute DOMString name;
     readonly attribute LockMode mode;
diff --git a/third_party/blink/renderer/modules/locks/lock_manager.idl b/third_party/blink/renderer/modules/locks/lock_manager.idl
index 8337d2b..dfa15d19 100644
--- a/third_party/blink/renderer/modules/locks/lock_manager.idl
+++ b/third_party/blink/renderer/modules/locks/lock_manager.idl
@@ -8,8 +8,7 @@
 // https://wicg.github.io/web-locks/#lockmanager
 [
     SecureContext,
-    Exposed=(Window,Worker),
-    RuntimeEnabled=WebLocksAPI
+    Exposed=(Window,Worker)
 ] interface LockManager {
     [CallWith=ScriptState, RaisesException, Measure] Promise<Lock> request(
         DOMString name,
diff --git a/third_party/blink/renderer/modules/locks/navigator_locks.idl b/third_party/blink/renderer/modules/locks/navigator_locks.idl
index 7b9ef29b..79d2709 100644
--- a/third_party/blink/renderer/modules/locks/navigator_locks.idl
+++ b/third_party/blink/renderer/modules/locks/navigator_locks.idl
@@ -6,8 +6,7 @@
 [
     SecureContext,
     Exposed=Window,
-    ImplementedAs=NavigatorLocks,
-    RuntimeEnabled=WebLocksAPI
+    ImplementedAs=NavigatorLocks
 ] partial interface Navigator {
     [CallWith=ScriptState] readonly attribute LockManager locks;
 };
diff --git a/third_party/blink/renderer/modules/locks/worker_navigator_locks.idl b/third_party/blink/renderer/modules/locks/worker_navigator_locks.idl
index c8d07dc..cbbccdf 100644
--- a/third_party/blink/renderer/modules/locks/worker_navigator_locks.idl
+++ b/third_party/blink/renderer/modules/locks/worker_navigator_locks.idl
@@ -6,8 +6,7 @@
 [
     SecureContext,
     Exposed=Worker,
-    ImplementedAs=NavigatorLocks,
-    RuntimeEnabled=WebLocksAPI
+    ImplementedAs=NavigatorLocks
 ] partial interface WorkerNavigator {
     [CallWith=ScriptState] readonly attribute LockManager locks;
 };
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element.cc
index 388b039..3bfca89 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element.cc
@@ -64,6 +64,9 @@
 
  private:
   void Invoke(ExecutionContext* context, Event* event) override {
+    if (event->target() != element_)
+      return;
+
     if (event->type() == event_type_names::kTransitionend) {
       callback_.Run();
       return;
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element_test.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element_test.cc
index 334b4a3..a6faf08 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_panel_element_test.cc
@@ -33,8 +33,8 @@
   }
 
  protected:
-  void SimulateTransitionEnd() {
-    TriggerEvent(event_type_names::kTransitionend);
+  void SimulateTransitionEnd(Element& element) {
+    TriggerEvent(element, event_type_names::kTransitionend);
   }
 
   void ExpectPanelIsDisplayed() { EXPECT_TRUE(GetPanel().IsWanted()); }
@@ -55,9 +55,10 @@
   HTMLMediaElement& GetMediaElement() { return *media_element_.Get(); }
 
  private:
-  void TriggerEvent(const AtomicString& name) {
+  void TriggerEvent(Element& element, const AtomicString& name) {
     Event* event = Event::Create(name);
-    GetPanel().DispatchEvent(*event);
+    event->SetTarget(&element);
+    GetPanel().FireEventListeners(*event);
   }
 
   Persistent<HTMLMediaElement> media_element_;
@@ -66,6 +67,9 @@
 };
 
 TEST_F(MediaControlPanelElementTest, StateTransitions) {
+  Element* child_div = HTMLDivElement::Create(GetPanel().GetDocument());
+  GetPanel().ParserAppendChild(child_div);
+
   // Make sure we are displayed (we are already opaque).
   GetPanel().SetIsDisplayed(true);
   ExpectPanelIsDisplayed();
@@ -75,10 +79,15 @@
   EventListenerNotCreated();
   GetPanel().MakeTransparent();
 
-  // The event listener should now be attached so we should simulate the
-  // transition end and the panel will be hidden.
+  // The event listener should now be attached
   EventListenerAttached();
-  SimulateTransitionEnd();
+
+  // Simulate child div transition end and the panel should not be hidden
+  SimulateTransitionEnd(*child_div);
+  ExpectPanelIsDisplayed();
+
+  // Simulate panel transition end and the panel will be hidden
+  SimulateTransitionEnd(GetPanel());
   ExpectPanelIsNotDisplayed();
 
   // The event listener should be detached. We should now make the panel
@@ -89,7 +98,7 @@
   // The event listener should now be attached so we should simulate the
   // transition end event and the panel will be hidden.
   EventListenerAttached();
-  SimulateTransitionEnd();
+  SimulateTransitionEnd(GetPanel());
   ExpectPanelIsDisplayed();
 }
 
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
index 01104b6..94c9a25 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
@@ -85,9 +85,9 @@
 #include "third_party/blink/renderer/modules/remoteplayback/html_media_element_remote_playback.h"
 #include "third_party/blink/renderer/modules/remoteplayback/remote_playback.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -2213,10 +2213,10 @@
 
 void MediaControlsImpl::OpenVolumeSliderIfNecessary() {
   if (ShouldOpenVolumeSlider()) {
-    volume_slider_wanted_timer_.StartOneShot(
-        LayoutTestSupport::IsRunningLayoutTest() ? kTimeToShowVolumeSliderTest
+    volume_slider_wanted_timer_.StartOneShot(WebTestSupport::IsRunningWebTest()
+                                                 ? kTimeToShowVolumeSliderTest
                                                  : kTimeToShowVolumeSlider,
-        FROM_HERE);
+                                             FROM_HERE);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
index 71648251..79dbba89 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl_test.cc
@@ -42,11 +42,11 @@
 #include "third_party/blink/renderer/modules/remoteplayback/html_media_element_remote_playback.h"
 #include "third_party/blink/renderer/modules/remoteplayback/remote_playback.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
 #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 // The MediaTimelineWidths histogram suffix expected to be encountered in these
 // tests. Depends on the OS, since Android sizes its timeline differently.
@@ -1290,7 +1290,7 @@
   SetHasAudio(true);
   SimulateLoadedMetadata();
 
-  LayoutTestSupport::SetIsRunningLayoutTest(false);
+  WebTestSupport::SetIsRunningWebTest(false);
 
   Element* volume_slider = GetElementByShadowPseudoId(
       MediaControls(), "-webkit-media-controls-volume-slider");
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
index d4b84b5..12def00 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate_test.cc
@@ -30,10 +30,10 @@
 #include "third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.h"
 #include "third_party/blink/renderer/modules/screen_orientation/web_lock_orientation_callback.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 using testing::_;
 using testing::AtLeast;
@@ -298,8 +298,8 @@
   void SetUp() override {
     // Unset this to fix ScreenOrientationControllerImpl::ComputeOrientation.
     // TODO(mlamouri): Refactor to avoid this (crbug.com/726817).
-    was_running_layout_test_ = LayoutTestSupport::IsRunningLayoutTest();
-    LayoutTestSupport::SetIsRunningLayoutTest(false);
+    was_running_layout_test_ = WebTestSupport::IsRunningWebTest();
+    WebTestSupport::SetIsRunningWebTest(false);
 
     MediaControlsOrientationLockDelegateTest::SetUp();
 
@@ -315,7 +315,7 @@
 
   void TearDown() override {
     MediaControlsOrientationLockDelegateTest::TearDown();
-    LayoutTestSupport::SetIsRunningLayoutTest(was_running_layout_test_);
+    WebTestSupport::SetIsRunningWebTest(was_running_layout_test_);
   }
 
   void SetIsAutoRotateEnabledByUser(bool enabled) {
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 35f32d6b..9877418 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -652,6 +652,7 @@
           "push_messaging/push_event_init.idl",
           "push_messaging/push_subscription_options_init.idl",
           "quota/storage_estimate.idl",
+          "quota/storage_usage_details.idl",
           "sensor/sensor_error_event_init.idl",
           "sensor/sensor_options.idl",
           "sensor/spatial_sensor_options.idl",
diff --git a/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc b/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc
index a2dd03eb..9be70c87 100644
--- a/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc
+++ b/third_party/blink/renderer/modules/quota/deprecated_storage_quota.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/quota/dom_error.h"
 #include "third_party/blink/renderer/modules/quota/quota_utils.h"
+#include "third_party/blink/renderer/modules/quota/storage_estimate.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -49,6 +50,7 @@
 namespace blink {
 
 using mojom::StorageType;
+using mojom::blink::UsageBreakdownPtr;
 
 namespace {
 
@@ -68,7 +70,8 @@
     V8PersistentCallbackFunction<V8StorageErrorCallback>* error_callback,
     mojom::QuotaStatusCode status_code,
     int64_t usage_in_bytes,
-    int64_t quota_in_bytes) {
+    int64_t quota_in_bytes,
+    UsageBreakdownPtr usage_breakdown) {
   if (status_code != mojom::QuotaStatusCode::kOk) {
     if (error_callback) {
       error_callback->InvokeAndReportException(nullptr,
@@ -156,7 +159,8 @@
       .QueryStorageUsageAndQuota(
           WrapRefCounted(security_origin), storage_type,
           mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-              std::move(callback), mojom::QuotaStatusCode::kErrorAbort, 0, 0));
+              std::move(callback), mojom::QuotaStatusCode::kErrorAbort, 0, 0,
+              nullptr));
 }
 
 void DeprecatedStorageQuota::requestQuota(
diff --git a/third_party/blink/renderer/modules/quota/storage_estimate.idl b/third_party/blink/renderer/modules/quota/storage_estimate.idl
index 3b38d019..46b9a73 100644
--- a/third_party/blink/renderer/modules/quota/storage_estimate.idl
+++ b/third_party/blink/renderer/modules/quota/storage_estimate.idl
@@ -7,4 +7,5 @@
 dictionary StorageEstimate {
     unsigned long long usage;
     unsigned long long quota;
+    [RuntimeEnabled=StorageQuotaDetails] StorageUsageDetails usageDetails;
 };
diff --git a/third_party/blink/renderer/modules/quota/storage_manager.cc b/third_party/blink/renderer/modules/quota/storage_manager.cc
index fff001f..764d93a 100644
--- a/third_party/blink/renderer/modules/quota/storage_manager.cc
+++ b/third_party/blink/renderer/modules/quota/storage_manager.cc
@@ -6,6 +6,7 @@
 
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/quota/quota_types.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_storage_estimate.h"
@@ -25,6 +26,7 @@
 using mojom::blink::PermissionName;
 using mojom::blink::PermissionService;
 using mojom::blink::PermissionStatus;
+using mojom::blink::UsageBreakdownPtr;
 
 namespace {
 
@@ -34,7 +36,8 @@
 void QueryStorageUsageAndQuotaCallback(ScriptPromiseResolver* resolver,
                                        mojom::QuotaStatusCode status_code,
                                        int64_t usage_in_bytes,
-                                       int64_t quota_in_bytes) {
+                                       int64_t quota_in_bytes,
+                                       UsageBreakdownPtr usage_breakdown) {
   if (status_code != mojom::QuotaStatusCode::kOk) {
     // TODO(sashab): Replace this with a switch statement, and remove the enum
     // values from QuotaStatusCode.
@@ -46,6 +49,27 @@
   StorageEstimate* estimate = StorageEstimate::Create();
   estimate->setUsage(usage_in_bytes);
   estimate->setQuota(quota_in_bytes);
+
+  // We only want to show usage details for systems that are used by the app,
+  // this way we do not create any web compatibility issues by unecessarily
+  // exposing obsoleted/proprietary storage systems, but also report when
+  // those systems are in use.
+  StorageUsageDetails* details = StorageUsageDetails::Create();
+  if (usage_breakdown->appcache) {
+    details->setApplicationCache(usage_breakdown->appcache);
+  }
+  if (usage_breakdown->indexedDatabase) {
+    details->setIndexedDB(usage_breakdown->indexedDatabase);
+  }
+  if (usage_breakdown->serviceWorkerCache) {
+    details->setCaches(usage_breakdown->serviceWorkerCache);
+  }
+  if (usage_breakdown->serviceWorker) {
+    details->setServiceWorkerRegistrations(usage_breakdown->serviceWorker);
+  }
+
+  estimate->setUsageDetails(details);
+
   resolver->Resolve(estimate);
 }
 
@@ -115,7 +139,8 @@
       .QueryStorageUsageAndQuota(
           WrapRefCounted(security_origin), mojom::StorageType::kTemporary,
           mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-              std::move(callback), mojom::QuotaStatusCode::kErrorAbort, 0, 0));
+              std::move(callback), mojom::QuotaStatusCode::kErrorAbort, 0, 0,
+              nullptr));
   return promise;
 }
 
diff --git a/third_party/blink/renderer/modules/quota/storage_usage_details.idl b/third_party/blink/renderer/modules/quota/storage_usage_details.idl
new file mode 100644
index 0000000..4163a9a
--- /dev/null
+++ b/third_party/blink/renderer/modules/quota/storage_usage_details.idl
@@ -0,0 +1,13 @@
+// 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.
+
+// https://storage.spec.whatwg.org/#api
+
+dictionary StorageUsageDetails {
+    unsigned long long applicationCache;
+    unsigned long long indexedDB;
+    unsigned long long caches;
+    unsigned long long serviceWorkerRegistrations;
+    unsigned long long websql;
+};
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc
index 92b9887..e533cab2 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.cc
@@ -18,7 +18,7 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/modules/screen_orientation/screen_orientation.h"
 #include "third_party/blink/renderer/modules/screen_orientation/screen_orientation_dispatcher.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -55,10 +55,10 @@
 WebScreenOrientationType ScreenOrientationControllerImpl::ComputeOrientation(
     const IntRect& rect,
     uint16_t rotation) {
-  // Bypass orientation detection in layout tests to get consistent results.
-  // FIXME: The screen dimension should be fixed when running the layout tests
+  // Bypass orientation detection in web tests to get consistent results.
+  // FIXME: The screen dimension should be fixed when running the web tests
   // to avoid such issues.
-  if (LayoutTestSupport::IsRunningLayoutTest())
+  if (WebTestSupport::IsRunningWebTest())
     return kWebScreenOrientationPortraitPrimary;
 
   bool is_tall_display = rotation % 180 ? rect.Height() < rect.Width()
diff --git a/third_party/blink/renderer/modules/sensor/sensor.cc b/third_party/blink/renderer/modules/sensor/sensor.cc
index 76953d0d..b4efc3b 100644
--- a/third_party/blink/renderer/modules/sensor/sensor.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor.cc
@@ -15,7 +15,7 @@
 #include "third_party/blink/renderer/core/timing/window_performance.h"
 #include "third_party/blink/renderer/modules/sensor/sensor_error_event.h"
 #include "third_party/blink/renderer/modules/sensor/sensor_provider_proxy.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -130,7 +130,7 @@
   DCHECK(sensor_proxy_);
   is_null = false;
 
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     // In layout tests performance.now() * 0.001 is passed to the shared buffer.
     return sensor_proxy_->GetReading().timestamp() * 1000;
   }
diff --git a/third_party/blink/renderer/modules/sensor/sensor_proxy.cc b/third_party/blink/renderer/modules/sensor/sensor_proxy.cc
index ec5b6d7..9b3fed19 100644
--- a/third_party/blink/renderer/modules/sensor/sensor_proxy.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor_proxy.cc
@@ -13,7 +13,7 @@
 #include "third_party/blink/renderer/core/page/focus_controller.h"
 #include "third_party/blink/renderer/modules/sensor/sensor_provider_proxy.h"
 #include "third_party/blink/renderer/modules/sensor/sensor_reading_remapper.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
@@ -67,7 +67,7 @@
 namespace {
 
 uint16_t GetScreenOrientationAngleForPage(Page* page) {
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     // Simulate that the device is turned 90 degrees on the right.
     // 'orientation_angle' must be 270 as per
     // https://w3c.github.io/screen-orientation/#dfn-update-the-orientation-information.
diff --git a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
index 8d37624..1b06663 100644
--- a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
@@ -13,8 +13,8 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "v8/include/v8.h"
 
@@ -28,7 +28,7 @@
 const unsigned kWindowInteractionTimeoutForTest = 1;
 
 TimeDelta WindowInteractionTimeout() {
-  return TimeDelta::FromSeconds(LayoutTestSupport::IsRunningLayoutTest()
+  return TimeDelta::FromSeconds(WebTestSupport::IsRunningWebTest()
                                     ? kWindowInteractionTimeoutForTest
                                     : kWindowInteractionTimeout);
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_input.cc b/third_party/blink/renderer/modules/webaudio/audio_node_input.cc
index a85602e4..ab84b0e6 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_input.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_input.cc
@@ -33,7 +33,7 @@
 
 namespace blink {
 
-inline AudioNodeInput::AudioNodeInput(AudioHandler& handler)
+AudioNodeInput::AudioNodeInput(AudioHandler& handler)
     : AudioSummingJunction(handler.Context()->GetDeferredTaskHandler()),
       handler_(handler) {
   // Set to mono by default.
@@ -41,6 +41,18 @@
       AudioBus::Create(1, audio_utilities::kRenderQuantumFrames);
 }
 
+AudioNodeInput::~AudioNodeInput() {
+  GetDeferredTaskHandler().AssertGraphOwner();
+
+  for (AudioNodeOutput* output : outputs_)
+    output->RemoveInput(*this);
+  for (AudioNodeOutput* output : disabled_outputs_)
+    output->RemoveInput(*this);
+  outputs_.clear();
+  disabled_outputs_.clear();
+  ChangedOutputs();
+}
+
 std::unique_ptr<AudioNodeInput> AudioNodeInput::Create(AudioHandler& handler) {
   return base::WrapUnique(new AudioNodeInput(handler));
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_input.h b/third_party/blink/renderer/modules/webaudio/audio_node_input.h
index 85b5048..82d1092 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_input.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_input.h
@@ -27,6 +27,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_NODE_INPUT_H_
 
 #include <memory>
+#include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_node.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_summing_junction.h"
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
@@ -43,11 +44,12 @@
 // number of channels of the input's bus is the maximum of the number of
 // channels of all its connections.
 
-class AudioNodeInput final : public AudioSummingJunction {
+class MODULES_EXPORT AudioNodeInput final : public AudioSummingJunction {
   USING_FAST_MALLOC(AudioNodeInput);
 
  public:
   static std::unique_ptr<AudioNodeInput> Create(AudioHandler&);
+  ~AudioNodeInput() override;
 
   // AudioSummingJunction
   void DidUpdate() override;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_input_test.cc b/third_party/blink/renderer/modules/webaudio/audio_node_input_test.cc
new file mode 100644
index 0000000..072bf6f5
--- /dev/null
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_input_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webaudio/audio_node.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
+#include "third_party/blink/renderer/modules/webaudio/delay_node.h"
+#include "third_party/blink/renderer/modules/webaudio/offline_audio_context.h"
+
+namespace blink {
+
+TEST(AudioNodeInputTest, InputDestroyedBeforeOutput) {
+  auto page = DummyPageHolder::Create();
+  OfflineAudioContext* context = OfflineAudioContext::Create(
+      &page->GetDocument(), 2, 1, 48000, ASSERT_NO_EXCEPTION);
+  DelayNode* node1 = context->createDelay(ASSERT_NO_EXCEPTION);
+  auto& handler1 = node1->Handler();
+  DelayNode* node2 = context->createDelay(ASSERT_NO_EXCEPTION);
+  auto& handler2 = node2->Handler();
+
+  auto input = AudioNodeInput::Create(handler1);
+  auto output = AudioNodeOutput::Create(&handler2, 0);
+
+  {
+    BaseAudioContext::GraphAutoLocker graph_lock(context);
+    input->Connect(*output);
+    ASSERT_TRUE(output->IsConnected());
+
+    // This should not crash.
+    input.reset();
+    output->Dispose();
+    output.reset();
+  }
+}
+
+TEST(AudioNodeInputTest, OutputDestroyedBeforeInput) {
+  auto page = DummyPageHolder::Create();
+  OfflineAudioContext* context = OfflineAudioContext::Create(
+      &page->GetDocument(), 2, 1, 48000, ASSERT_NO_EXCEPTION);
+  DelayNode* node1 = context->createDelay(ASSERT_NO_EXCEPTION);
+  auto& handler1 = node1->Handler();
+  DelayNode* node2 = context->createDelay(ASSERT_NO_EXCEPTION);
+  auto& handler2 = node2->Handler();
+
+  auto input = AudioNodeInput::Create(handler1);
+  auto output = AudioNodeOutput::Create(&handler2, 0);
+
+  {
+    BaseAudioContext::GraphAutoLocker graph_lock(context);
+    input->Connect(*output);
+    ASSERT_TRUE(output->IsConnected());
+
+    // This should not crash.
+    output->Dispose();
+    output.reset();
+    input.reset();
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_output.h b/third_party/blink/renderer/modules/webaudio/audio_node_output.h
index dd76a98..2dbe62c 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_output.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_output.h
@@ -28,6 +28,7 @@
 
 #include <memory>
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_node.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_param.h"
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
@@ -39,7 +40,7 @@
 
 // AudioNodeOutput represents a single output for an AudioNode.
 // It may be connected to one or more AudioNodeInputs.
-class AudioNodeOutput final {
+class MODULES_EXPORT AudioNodeOutput final {
   USING_FAST_MALLOC(AudioNodeOutput);
 
  public:
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index fb433a44..d584c90 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1167,8 +1167,6 @@
     "keyboard_codes.h",
     "language.cc",
     "language.h",
-    "layout_test_support.cc",
-    "layout_test_support.h",
     "lifecycle_notifier.h",
     "lifecycle_observer.h",
     "link_hash.cc",
@@ -1366,6 +1364,8 @@
     "web_mouse_wheel_event.cc",
     "web_pointer_event.cc",
     "web_task_runner.h",
+    "web_test_support.cc",
+    "web_test_support.h",
     "web_text_input_info.cc",
     "web_thread_supporting_gc.cc",
     "web_thread_supporting_gc.h",
@@ -2113,6 +2113,7 @@
     "graphics/test/fake_canvas_resource_host.h",
     "graphics/test/fake_gles2_interface.h",
     "graphics/test/fake_web_graphics_context_3d_provider.h",
+    "graphics/test/gpu_memory_buffer_test_platform.h",
     "graphics/test/mock_compositor_frame_sink.h",
     "graphics/test/mock_embedded_frame_sink_provider.h",
     "graphics/test/mock_image_decoder.h",
diff --git a/third_party/blink/renderer/platform/fonts/DEPS b/third_party/blink/renderer/platform/fonts/DEPS
index 941f8cd..d859ec6 100644
--- a/third_party/blink/renderer/platform/fonts/DEPS
+++ b/third_party/blink/renderer/platform/fonts/DEPS
@@ -13,7 +13,6 @@
     "+third_party/blink/renderer/platform/histogram.h",
     "+third_party/blink/renderer/platform/instrumentation",
     "+third_party/blink/renderer/platform/language.h",
-    "+third_party/blink/renderer/platform/layout_test_support.h",
     "+third_party/blink/renderer/platform/mac/version_util_mac.h",
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/resolution_units.h",
@@ -22,5 +21,6 @@
     "+third_party/blink/renderer/platform/shared_buffer.h",
     "+third_party/blink/renderer/platform/testing",
     "+third_party/blink/renderer/platform/text",
+    "+third_party/blink/renderer/platform/web_test_support.h",
     "+third_party/blink/renderer/platform/wtf",
 ]
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data.cc b/third_party/blink/renderer/platform/fonts/font_platform_data.cc
index e8529448..64dd821 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.cc
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.cc
@@ -29,8 +29,8 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
@@ -148,11 +148,11 @@
   auto system_style =
       QuerySystemRenderStyle(family_, text_size_, typeface_->fontStyle());
 
-  // In layout tests, ignore system preference for subpixel positioning,
+  // In web tests, ignore system preference for subpixel positioning,
   // or explicitly disable if requested.
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     system_style.use_subpixel_positioning =
-        LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest()
+        WebTestSupport::IsTextSubpixelPositioningAllowedForTest()
             ? WebFontRenderStyle::kNoPreference
             : 0;
   }
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
index 751e6c26..829170d 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm
@@ -39,9 +39,9 @@
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
 #include "third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 
@@ -87,9 +87,9 @@
 }
 
 static bool UseHinting() {
-  // Enable hinting only when antialiasing is disabled in layout tests.
-  return (LayoutTestSupport::IsRunningLayoutTest() &&
-          !LayoutTestSupport::IsFontAntialiasingEnabledForTest());
+  // Enable hinting only when antialiasing is disabled in web tests.
+  return (WebTestSupport::IsRunningWebTest() &&
+          !WebTestSupport::IsFontAntialiasingEnabledForTest());
 }
 
 void FontCache::PlatformInit() {
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
index cda5492..97bebbd 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
@@ -30,7 +30,7 @@
 #import "third_party/blink/renderer/platform/fonts/font.h"
 #import "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
 #import "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
-#import "third_party/blink/renderer/platform/layout_test_support.h"
+#import "third_party/blink/renderer/platform/web_test_support.h"
 #import "third_party/blink/renderer/platform/wtf/retain_ptr.h"
 #import "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #import "third_party/skia/include/core/SkFont.h"
@@ -137,12 +137,12 @@
     }
   }
 
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     should_smooth_fonts = false;
-    should_antialias = should_antialias &&
-                       LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+    should_antialias =
+        should_antialias && WebTestSupport::IsFontAntialiasingEnabledForTest();
     should_subpixel_position =
-        LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest();
+        WebTestSupport::IsTextSubpixelPositioningAllowedForTest();
   }
 
   paint->setAntiAlias(should_antialias);
@@ -189,12 +189,12 @@
     }
   }
 
-  if (LayoutTestSupport::IsRunningLayoutTest()) {
+  if (WebTestSupport::IsRunningWebTest()) {
     should_smooth_fonts = false;
-    should_antialias = should_antialias &&
-                       LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+    should_antialias =
+        should_antialias && WebTestSupport::IsFontAntialiasingEnabledForTest();
     should_subpixel_position =
-        LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest();
+        WebTestSupport::IsTextSubpixelPositioningAllowedForTest();
   }
 
   if (should_antialias && should_smooth_fonts) {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
index a58d78c..35e0d66e 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
@@ -16,11 +16,11 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/text/text_break_iterator.h"
 #include "third_party/blink/renderer/platform/text/text_run.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 using testing::ElementsAre;
@@ -114,40 +114,40 @@
 class ScopedSubpixelOverride {
  public:
   ScopedSubpixelOverride(bool b) {
-    prev_layout_test_ = LayoutTestSupport::IsRunningLayoutTest();
+    prev_layout_test_ = WebTestSupport::IsRunningWebTest();
     prev_subpixel_allowed_ =
-        LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest();
-    prev_antialias_ = LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+        WebTestSupport::IsTextSubpixelPositioningAllowedForTest();
+    prev_antialias_ = WebTestSupport::IsFontAntialiasingEnabledForTest();
     prev_fd_subpixel_ = FontDescription::SubpixelPositioning();
 
-    // This is required for all LayoutTestSupport settings to have effects.
-    LayoutTestSupport::SetIsRunningLayoutTest(true);
+    // This is required for all WebTestSupport settings to have effects.
+    WebTestSupport::SetIsRunningWebTest(true);
 
     if (b) {
       // Allow subpixel positioning.
-      LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(true);
+      WebTestSupport::SetTextSubpixelPositioningAllowedForTest(true);
 
       // Now, enable subpixel positioning in platform-specific ways.
 
       // Mac always enables subpixel positioning.
 
       // On Windows, subpixel positioning also requires antialiasing.
-      LayoutTestSupport::SetFontAntialiasingEnabledForTest(true);
+      WebTestSupport::SetFontAntialiasingEnabledForTest(true);
 
       // On platforms other than Windows and Mac this needs to be set as
       // well.
       FontDescription::SetSubpixelPositioning(true);
     } else {
       // Explicitly disallow all subpixel positioning.
-      LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(false);
+      WebTestSupport::SetTextSubpixelPositioningAllowedForTest(false);
     }
   }
   ~ScopedSubpixelOverride() {
     FontDescription::SetSubpixelPositioning(prev_fd_subpixel_);
-    LayoutTestSupport::SetFontAntialiasingEnabledForTest(prev_antialias_);
-    LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(
+    WebTestSupport::SetFontAntialiasingEnabledForTest(prev_antialias_);
+    WebTestSupport::SetTextSubpixelPositioningAllowedForTest(
         prev_subpixel_allowed_);
-    LayoutTestSupport::SetIsRunningLayoutTest(prev_layout_test_);
+    WebTestSupport::SetIsRunningWebTest(prev_layout_test_);
 
     // Fonts cached with a different subpixel positioning state are not
     // automatically invalidated and need to be cleared between test
diff --git a/third_party/blink/renderer/platform/fonts/web_font_render_style.cc b/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
index a1f9b21b..b63f8a2 100644
--- a/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
+++ b/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
@@ -7,7 +7,7 @@
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 #include <SkFont.h>
 
@@ -108,9 +108,9 @@
     font.setLCDRenderText(use_subpixel_rendering);
 
   // Force-enable subpixel positioning, except when full hinting is requested on
-  // low-dpi screen or when running layout tests.
+  // low-dpi screen or when running web tests.
   bool force_subpixel_positioning =
-      !LayoutTestSupport::IsRunningLayoutTest() &&
+      !WebTestSupport::IsRunningWebTest() &&
       (sk_hint_style != SkFontHinting::kFull || device_scale_factor > 1.0f);
 
   font.setSubpixelText(force_subpixel_positioning || use_subpixel_positioning);
@@ -131,9 +131,9 @@
   }
 
   // Force-enable subpixel positioning, except when full hinting is requested on
-  // low-dpi screen or when running layout tests.
+  // low-dpi screen or when running web tests.
   bool force_subpixel_positioning =
-      !LayoutTestSupport::IsRunningLayoutTest() &&
+      !WebTestSupport::IsRunningWebTest() &&
       (sk_hint_style != SkFontHinting::kFull || device_scale_factor > 1.0f);
 
   font->setSubpixel(force_subpixel_positioning || use_subpixel_positioning);
diff --git a/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc b/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
index ed89ed8..4fc4b20d 100644
--- a/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
+++ b/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
@@ -35,7 +35,7 @@
 #include "SkFont.h"
 #include "SkTypeface.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -61,8 +61,8 @@
   if (text_flags & SkPaint::kAntiAlias_Flag)
     flags |= SkPaint::kSubpixelText_Flag;
 
-  if (LayoutTestSupport::IsRunningLayoutTest() &&
-      !LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest())
+  if (WebTestSupport::IsRunningWebTest() &&
+      !WebTestSupport::IsTextSubpixelPositioningAllowedForTest())
     flags &= ~SkPaint::kSubpixelText_Flag;
 
   SkASSERT(!(text_flags & ~kTextFlagsMask));
@@ -98,8 +98,8 @@
   if (text_flags & SkPaint::kAntiAlias_Flag)
     font->setSubpixel(true);
 
-  if (LayoutTestSupport::IsRunningLayoutTest() &&
-      !LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest())
+  if (WebTestSupport::IsRunningWebTest() &&
+      !WebTestSupport::IsTextSubpixelPositioningAllowedForTest())
     font->setSubpixel(false);
 
   font->setEmbeddedBitmaps(!avoid_embedded_bitmaps_);
@@ -114,8 +114,8 @@
 }
 
 static int ComputePaintTextFlags(String font_family_name) {
-  if (LayoutTestSupport::IsRunningLayoutTest())
-    return LayoutTestSupport::IsFontAntialiasingEnabledForTest()
+  if (WebTestSupport::IsRunningWebTest())
+    return WebTestSupport::IsFontAntialiasingEnabledForTest()
                ? SkPaint::kAntiAlias_Flag
                : 0;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
index f42c6f8..97e51b4 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -53,11 +53,11 @@
 #include "third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
 #include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/waitable_event.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/gl/GrGLTypes.h"
@@ -91,20 +91,6 @@
   }
 };
 
-class FakePlatformSupport : public TestingPlatformSupport {
- public:
-  void RunUntilStop() const { base::RunLoop().Run(); }
-
-  void StopRunning() const { base::RunLoop().Quit(); }
-
- private:
-  gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
-    return &test_gpu_memory_buffer_manager_;
-  }
-
-  viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
-};
-
 class ImageTrackingDecodeCache : public cc::StubDecodeCache {
  public:
   ImageTrackingDecodeCache() = default;
@@ -421,7 +407,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationLifeCycle)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -467,7 +453,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationReEntry)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -517,7 +503,7 @@
        DISABLED_HibernationLifeCycleWithDeferredRenderingDisabled)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
 
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
@@ -566,7 +552,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_BackgroundRenderingWhileHibernating)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -619,7 +605,7 @@
     DISABLED_BackgroundRenderingWhileHibernatingWithDeferredRenderingDisabled)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -677,7 +663,7 @@
        DISABLED_DisableDeferredRenderingWhileHibernating)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -738,7 +724,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernating)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -776,7 +762,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_SnapshotWhileHibernating)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -825,7 +811,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernationIsPending)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -859,7 +845,7 @@
        DISABLED_HibernationAbortedDueToVisibilityChange)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -895,7 +881,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToLostContext)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -930,7 +916,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileHibernating)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -973,7 +959,7 @@
 TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileBackgroundRendering)
 #endif
 {
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
                  CanvasColorParams());
@@ -1017,7 +1003,7 @@
 TEST_F(Canvas2DLayerBridgeTest, GpuMemoryBufferRecycling) {
   InSequence s;
   ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(true);
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   const_cast<gpu::Capabilities&>(SharedGpuContext::ContextProviderWrapper()
                                      ->ContextProvider()
                                      ->GetCapabilities())
@@ -1095,7 +1081,7 @@
 TEST_F(Canvas2DLayerBridgeTest, NoGpuMemoryBufferRecyclingWhenPageHidden) {
   InSequence s;
   ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(true);
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   const_cast<gpu::Capabilities&>(SharedGpuContext::ContextProviderWrapper()
                                      ->ContextProvider()
                                      ->GetCapabilities())
@@ -1173,7 +1159,7 @@
 TEST_F(Canvas2DLayerBridgeTest, ReleaseGpuMemoryBufferAfterBridgeDestroyed) {
   InSequence s;
   ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(true);
-  ScopedTestingPlatformSupport<FakePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   const_cast<gpu::Capabilities&>(SharedGpuContext::ContextProviderWrapper()
                                      ->ContextProvider()
                                      ->GetCapabilities())
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index d9d91f3..69d5a9a 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -426,6 +426,10 @@
   return !gpu_mailbox_.IsZero();
 }
 
+GLuint CanvasResourceGpuMemoryBuffer::GetBackingTextureHandleForOverwrite() {
+  return texture_2d_id_for_copy_ ? texture_2d_id_for_copy_ : texture_id_;
+}
+
 const gpu::SyncToken CanvasResourceGpuMemoryBuffer::GetSyncToken() {
   if (mailbox_needs_new_sync_token_) {
     auto* gl = ContextGL();
@@ -467,8 +471,6 @@
 }
 
 void CanvasResourceGpuMemoryBuffer::TakeSkImage(sk_sp<SkImage> image) {
-  TRACE_EVENT0("blink", "CanvasResourceGpuMemoryBuffer::CopyFromTexture");
-
   WillPaint();
   if (!surface_)
     return;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index 32b1bfe..113d199 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -86,6 +86,11 @@
   // Only CanvasResourceProvider and derivatives should call this.
   virtual void TakeSkImage(sk_sp<SkImage> image) = 0;
 
+  virtual GLuint GetBackingTextureHandleForOverwrite() {
+    NOTREACHED();
+    return 0;
+  }
+
  protected:
   CanvasResource(base::WeakPtr<CanvasResourceProvider>,
                  SkFilterQuality,
@@ -208,6 +213,7 @@
                        GLenum format,
                        GLenum type) override;
   scoped_refptr<StaticBitmapImage> Bitmap() override;
+  GLuint GetBackingTextureHandleForOverwrite() final;
 
  private:
   void TearDown() override;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index e1c788ae..1f949ee 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -346,7 +346,97 @@
   }
 };
 
+// * Renders to a GpuMemoryBuffer-backed texture used to create a SkSurface.
+// * Layers are overlay candidates
+class CanvasResourceProviderDirectGpuMemoryBuffer final
+    : public CanvasResourceProvider {
+ public:
+  CanvasResourceProviderDirectGpuMemoryBuffer(
+      const IntSize& size,
+      unsigned msaa_sample_count,
+      const CanvasColorParams color_params,
+      base::WeakPtr<WebGraphicsContext3DProviderWrapper>
+          context_provider_wrapper,
+      base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
+      bool is_origin_top_left)
+      : CanvasResourceProvider(size,
+                               color_params,
+                               std::move(context_provider_wrapper),
+                               std::move(resource_dispatcher)),
+        msaa_sample_count_(msaa_sample_count),
+        is_origin_top_left_(is_origin_top_left) {
+    constexpr bool is_accelerated = true;
+    resource_ = CanvasResourceGpuMemoryBuffer::Create(
+        Size(), ColorParams(), ContextProviderWrapper(), CreateWeakPtr(),
+        FilterQuality(), is_accelerated);
+  }
+
+  ~CanvasResourceProviderDirectGpuMemoryBuffer() override = default;
+  bool IsValid() const final { return GetSkSurface() && !IsGpuContextLost(); }
+  bool IsAccelerated() const final { return true; }
+  bool SupportsDirectCompositing() const override { return true; }
+  bool SupportsSingleBuffering() const override { return true; }
+
+ private:
+  GLuint GetBackingTextureHandleForOverwrite() override {
+    return resource_->GetBackingTextureHandleForOverwrite();
+  }
+
+  scoped_refptr<CanvasResource> CreateResource() final {
+    TRACE_EVENT0("blink",
+                 "CanvasResourceProviderDirectGpuMemoryBuffer::CreateResource");
+    DCHECK(resource_);
+    return resource_;
+  }
+
+  scoped_refptr<CanvasResource> ProduceFrame() final {
+    TRACE_EVENT0("blink",
+                 "CanvasResourceProviderDirectGpuMemoryBuffer::ProduceFrame");
+    if (IsGpuContextLost())
+      return nullptr;
+    FlushSkia();
+
+    auto* gl = ContextGL();
+    DCHECK(gl);
+    gl->Flush();
+
+    return NewOrRecycledResource();
+  }
+
+  sk_sp<SkSurface> CreateSkSurface() const override {
+    if (IsGpuContextLost())
+      return nullptr;
+    auto* gr = GetGrContext();
+    DCHECK(gr);
+
+    GrGLTextureInfo texture_info = {};
+    texture_info.fID = resource_->GetBackingTextureHandleForOverwrite();
+    texture_info.fTarget = GL_TEXTURE_2D;
+    // Skia requires a sized internal format.
+    texture_info.fFormat = ColorParams().GLSizedInternalFormat();
+
+    const GrBackendTexture backend_texture(Size().Width(), Size().Height(),
+                                           GrMipMapped::kNo, texture_info);
+
+    const enum GrSurfaceOrigin surface_origin =
+        is_origin_top_left_ ? kTopLeft_GrSurfaceOrigin
+                            : kBottomLeft_GrSurfaceOrigin;
+
+    auto surface = SkSurface::MakeFromBackendTextureAsRenderTarget(
+        gr, backend_texture, surface_origin, msaa_sample_count_,
+        ColorParams().GetSkColorType(),
+        ColorParams().GetSkColorSpaceForSkSurfaces(),
+        ColorParams().GetSkSurfaceProps());
+    return surface;
+  }
+
+  const unsigned msaa_sample_count_;
+  const bool is_origin_top_left_;
+  scoped_refptr<CanvasResource> resource_;
+};
+
 enum CanvasResourceType {
+  kDirectGpuMemoryBufferResourceType,
   kTextureGpuMemoryBufferResourceType,
   kRamGpuMemoryBufferResourceType,
   kSharedBitmapResourceType,
@@ -378,6 +468,18 @@
     kBitmapResourceType,
 };
 
+constexpr CanvasResourceType kAcceleratedDirectFallbackList[] = {
+    kDirectGpuMemoryBufferResourceType,
+    // The rest is equal to |kAcceleratedCompositedFallbackList|.
+    kTextureGpuMemoryBufferResourceType,
+    kTextureResourceType,
+    // Fallback to software composited
+    kRamGpuMemoryBufferResourceType,
+    kSharedBitmapResourceType,
+    // Fallback to no direct compositing support
+    kBitmapResourceType,
+};
+
 std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create(
     const IntSize& size,
     ResourceUsage usage,
@@ -407,15 +509,21 @@
       resource_type_fallback_list = kAcceleratedCompositedFallbackList;
       list_length = arraysize(kAcceleratedCompositedFallbackList);
       break;
+    case kAcceleratedDirectResourceUsage:
+      resource_type_fallback_list = kAcceleratedDirectFallbackList;
+      list_length = arraysize(kAcceleratedDirectFallbackList);
+      break;
   }
 
   std::unique_ptr<CanvasResourceProvider> provider;
   for (size_t i = 0; i < list_length; ++i) {
     // Note: We are deliberately not using std::move() on
-    // context_provider_wrapper and resource_dispatcher to ensure that the
+    // |context_provider_wrapper| and |resource_dispatcher| to ensure that the
     // pointers remain valid for the next iteration of this loop if necessary.
     switch (resource_type_fallback_list[i]) {
       case kTextureGpuMemoryBufferResourceType:
+        FALLTHROUGH;
+      case kDirectGpuMemoryBufferResourceType:
         if (!SharedGpuContext::IsGpuCompositingEnabled())
           continue;
         if (presentation_mode != kAllowImageChromiumPresentationMode)
@@ -435,10 +543,20 @@
         DCHECK_EQ(color_params.GLUnsizedInternalFormat(),
                   gpu::InternalFormatForGpuMemoryBufferFormat(
                       color_params.GetBufferFormat()));
-        provider =
-            std::make_unique<CanvasResourceProviderTextureGpuMemoryBuffer>(
-                size, msaa_sample_count, color_params, context_provider_wrapper,
-                resource_dispatcher, is_origin_top_left);
+        if (resource_type_fallback_list[i] ==
+            kDirectGpuMemoryBufferResourceType) {
+          provider =
+              std::make_unique<CanvasResourceProviderDirectGpuMemoryBuffer>(
+                  size, msaa_sample_count, color_params,
+                  context_provider_wrapper, resource_dispatcher,
+                  is_origin_top_left);
+        } else {
+          provider =
+              std::make_unique<CanvasResourceProviderTextureGpuMemoryBuffer>(
+                  size, msaa_sample_count, color_params,
+                  context_provider_wrapper, resource_dispatcher,
+                  is_origin_top_left);
+        }
         break;
       case kRamGpuMemoryBufferResourceType:
         if (!SharedGpuContext::IsGpuCompositingEnabled())
@@ -476,11 +594,11 @@
             size, color_params, context_provider_wrapper, resource_dispatcher);
         break;
     }
-    if (provider && provider->IsValid()) {
-      base::UmaHistogramBoolean("Blink.Canvas.ResourceProviderIsAccelerated",
-                                provider->IsAccelerated());
-      return provider;
-    }
+    if (!provider->IsValid())
+      continue;
+    base::UmaHistogramBoolean("Blink.Canvas.ResourceProviderIsAccelerated",
+                              provider->IsAccelerated());
+    return provider;
   }
 
   return nullptr;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index d3894cb5..3a06967df 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -55,6 +55,7 @@
     kSoftwareCompositedResourceUsage,
     kAcceleratedResourceUsage,
     kAcceleratedCompositedResourceUsage,
+    kAcceleratedDirectResourceUsage,
   };
 
   enum PresentationMode {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index 085187a..90bf30b 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 
+#include "components/viz/test/test_gpu_memory_buffer_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
@@ -11,13 +12,31 @@
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
+#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
+using testing::_;
 using testing::InSequence;
+using testing::Return;
 using testing::Test;
 
 namespace blink {
 
+namespace {
+
+class FakeGLES2InterfaceWithImageSupport : public FakeGLES2Interface {
+ public:
+  GLuint CreateImageCHROMIUM(ClientBuffer, GLsizei, GLsizei, GLenum) final {
+    return image_id_++;
+  }
+  void DestroyImageCHROMIUM(GLuint image_id) final {
+    EXPECT_LE(image_id, image_id_);
+  }
+
+ private:
+  GLuint image_id_ = 3;
+};
+
 class MockCanvasResourceDispatcherClient
     : public CanvasResourceDispatcherClient {
  public:
@@ -26,6 +45,8 @@
   MOCK_METHOD0(BeginFrame, void());
 };
 
+}  // anonymous namespace
+
 class CanvasResourceProviderTest : public Test {
  public:
   void SetUp() override {
@@ -43,9 +64,25 @@
 
   void TearDown() override { SharedGpuContext::ResetForTesting(); }
 
+  // Adds |buffer_format| to the context capabilities if it's not supported.
+  void EnsureBufferFormatIsSupported(gfx::BufferFormat buffer_format) {
+    auto* context_provider = context_provider_wrapper_->ContextProvider();
+    if (context_provider->GetCapabilities().gpu_memory_buffer_formats.Has(
+            buffer_format)) {
+      return;
+    }
+
+    auto capabilities = context_provider->GetCapabilities();
+    capabilities.gpu_memory_buffer_formats.Add(buffer_format);
+
+    static_cast<FakeWebGraphicsContext3DProvider*>(context_provider)
+        ->SetCapabilities(capabilities);
+  }
+
  protected:
-  FakeGLES2Interface gl_;
+  FakeGLES2InterfaceWithImageSupport gl_;
   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform_;
 };
 
 TEST_F(CanvasResourceProviderTest,
@@ -53,18 +90,7 @@
   const IntSize kSize(10, 10);
   const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
                                        kRGBA8CanvasPixelFormat, kNonOpaque);
-
-  // Add the deduced gfx::BufferFormat if it's not supported.
-  auto* context_provider = context_provider_wrapper_->ContextProvider();
-  const gfx::BufferFormat buffer_format = kColorParams.GetBufferFormat();
-  if (!context_provider->GetCapabilities().gpu_memory_buffer_formats.Has(
-          buffer_format)) {
-    auto capabilities = context_provider->GetCapabilities();
-    capabilities.gpu_memory_buffer_formats.Add(buffer_format);
-
-    static_cast<FakeWebGraphicsContext3DProvider*>(context_provider)
-        ->SetCapabilities(capabilities);
-  }
+  EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
 
   auto provider = CanvasResourceProvider::Create(
       kSize, CanvasResourceProvider::kAcceleratedCompositedResourceUsage,
@@ -126,8 +152,8 @@
   EXPECT_EQ(provider->Size(), kSize);
   EXPECT_TRUE(provider->IsValid());
   EXPECT_FALSE(provider->IsAccelerated());
-  EXPECT_FALSE(provider->SupportsDirectCompositing());
-  EXPECT_FALSE(provider->SupportsSingleBuffering());
+  EXPECT_TRUE(provider->SupportsDirectCompositing());
+  EXPECT_TRUE(provider->SupportsSingleBuffering());
   EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
   EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
   EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
@@ -191,4 +217,32 @@
   EXPECT_TRUE(provider->IsSingleBuffered());
 }
 
+TEST_F(CanvasResourceProviderTest,
+       CanvasResourceProviderDirectGpuMemoryBuffer) {
+  const IntSize kSize(10, 10);
+  const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
+                                       kRGBA8CanvasPixelFormat, kNonOpaque);
+  EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
+
+  auto provider = CanvasResourceProvider::Create(
+      kSize, CanvasResourceProvider::kAcceleratedDirectResourceUsage,
+      context_provider_wrapper_, 0 /* msaa_sample_count */, kColorParams,
+      CanvasResourceProvider::kAllowImageChromiumPresentationMode,
+      nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+
+  EXPECT_EQ(provider->Size(), kSize);
+  EXPECT_TRUE(provider->IsValid());
+  EXPECT_TRUE(provider->IsAccelerated());
+  EXPECT_TRUE(provider->SupportsDirectCompositing());
+  EXPECT_TRUE(provider->SupportsSingleBuffering());
+  EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
+  EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
+  EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
+            kColorParams.GetOpacityMode());
+
+  EXPECT_FALSE(provider->IsSingleBuffered());
+  provider->TryEnableSingleBuffering();
+  EXPECT_TRUE(provider->IsSingleBuffered());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
index f27b5bf..3dc0a13 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
@@ -16,7 +16,7 @@
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
@@ -39,20 +39,6 @@
   MOCK_METHOD2(BindTexture, void(GLenum, GLuint));
 };
 
-class FakeCanvasResourcePlatformSupport : public TestingPlatformSupport {
- public:
-  void RunUntilStop() const { base::RunLoop().Run(); }
-
-  void StopRunning() const { base::RunLoop().Quit(); }
-
- private:
-  gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
-    return &test_gpu_memory_buffer_manager_;
-  }
-
-  viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
-};
-
 class CanvasResourceTest : public Test {
  public:
   void SetUp() override {
@@ -134,7 +120,7 @@
 
 TEST_F(CanvasResourceTest, GpuMemoryBufferSyncTokenRefresh) {
   testing::InSequence s;
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
 
   constexpr GLuint image_id = 1;
   const GLuint texture_target = gpu::GetPlatformSpecificTextureTarget();
@@ -204,7 +190,7 @@
 }
 
 TEST_F(CanvasResourceTest, MakeAcceleratedFromAcceleratedResourceIsNoOp) {
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
 
   SkImageInfo image_info =
       SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
@@ -232,7 +218,7 @@
 }
 
 TEST_F(CanvasResourceTest, MakeAcceleratedFromRasterResource) {
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
 
   SkImageInfo image_info =
       SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
@@ -258,7 +244,7 @@
 }
 
 TEST_F(CanvasResourceTest, MakeUnacceleratedFromUnacceleratedResourceIsNoOp) {
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
 
   SkImageInfo image_info =
       SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
@@ -277,7 +263,7 @@
 }
 
 TEST_F(CanvasResourceTest, MakeUnacceleratedFromAcceleratedResource) {
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
 
   SkImageInfo image_info =
       SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, nullptr);
@@ -315,7 +301,7 @@
 
 TEST_F(CanvasResourceTest, RamGpuMemoryBuffer_ResourcePreparation) {
   testing::InSequence s;
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
 
   EXPECT_TRUE(!!context_provider_wrapper_);
   constexpr GLuint image_id = 1;
@@ -355,7 +341,7 @@
 
 TEST_F(CanvasResourceTest, GpuMemoryBuffer_accelerated_8bit) {
   testing::InSequence s;
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   EXPECT_TRUE(!!context_provider_wrapper_);
 
   constexpr GLuint image_id = 1;
@@ -381,7 +367,7 @@
 
 TEST_F(CanvasResourceTest, GpuMemoryBuffer_accelerated_float16) {
   testing::InSequence s;
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   EXPECT_TRUE(!!context_provider_wrapper_);
 
   constexpr GLuint image_id = 1;
@@ -409,7 +395,7 @@
 
 TEST_F(CanvasResourceTest, PrepareTransferableResource_SharedBitmap) {
   testing::InSequence s;
-  ScopedTestingPlatformSupport<FakeCanvasResourcePlatformSupport> platform;
+  ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
   scoped_refptr<CanvasResource> canvas_resource =
       CanvasResourceSharedBitmap::Create(IntSize(10, 10), CanvasColorParams(),
                                          nullptr,  // CanvasResourceProvider
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
index c5f07c6..2fdde1208 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc
@@ -42,8 +42,8 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
+#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
-#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "v8/include/v8.h"
 
 using testing::Test;
@@ -51,19 +51,6 @@
 
 namespace blink {
 
-namespace {
-
-class FakePlatformSupport : public TestingPlatformSupport {
-  gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
-    return &test_gpu_memory_buffer_manager_;
-  }
-
- private:
-  viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
-};
-
-}  // anonymous namespace
-
 class DrawingBufferTest : public Test {
  protected:
   void SetUp() override { Init(kDisableMultisampling); }
@@ -394,7 +381,8 @@
 
  protected:
   void SetUp() override {
-    platform_.reset(new ScopedTestingPlatformSupport<FakePlatformSupport>);
+    platform_.reset(
+        new ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform>);
 
     IntSize initial_size(kInitialWidth, kInitialHeight);
     auto gl = std::make_unique<GLES2InterfaceForTests>();
@@ -418,7 +406,8 @@
   }
 
   GLuint image_id0_;
-  std::unique_ptr<ScopedTestingPlatformSupport<FakePlatformSupport>> platform_;
+  std::unique_ptr<ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform>>
+      platform_;
 };
 
 TEST_F(DrawingBufferImageChromiumTest, VerifyResizingReallocatesImages) {
diff --git a/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc b/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc
index e225f5e..951146c 100644
--- a/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc
+++ b/third_party/blink/renderer/platform/graphics/high_contrast_image_classifier_test.cc
@@ -43,7 +43,7 @@
 
  protected:
   scoped_refptr<BitmapImage> LoadImage(const std::string& file_name) {
-    String file_path = test::BlinkLayoutTestsDir();
+    String file_path = test::BlinkWebTestsDir();
     file_path.append(file_name.c_str());
     scoped_refptr<SharedBuffer> image_data = test::ReadFromFile(file_path);
     EXPECT_TRUE(image_data.get() && image_data.get()->size());
diff --git a/third_party/blink/renderer/platform/graphics/test/DEPS b/third_party/blink/renderer/platform/graphics/test/DEPS
index 52a8536..3a676d7 100644
--- a/third_party/blink/renderer/platform/graphics/test/DEPS
+++ b/third_party/blink/renderer/platform/graphics/test/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+    "+components/viz/test/test_gpu_memory_buffer_manager.h",
     "+gpu/command_buffer/client/gles2_interface_stub.h",
     "+gpu/config/gpu_feature_info.h"
 ]
diff --git a/third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h b/third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h
new file mode 100644
index 0000000..226eb3e
--- /dev/null
+++ b/third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_GPU_MEMORY_BUFFER_TEST_PLATFORM_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_GPU_MEMORY_BUFFER_TEST_PLATFORM_H_
+
+#include "components/viz/test/test_gpu_memory_buffer_manager.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+
+class GpuMemoryBufferTestPlatform : public blink::TestingPlatformSupport {
+ private:
+  gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
+    return &test_gpu_memory_buffer_manager_;
+  }
+
+  viz::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
+};
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_GPU_MEMORY_BUFFER_TEST_PLATFORM_H_
diff --git a/third_party/blink/renderer/platform/heap/persistent.h b/third_party/blink/renderer/platform/heap/persistent.h
index e8bf8c4a..52dc689 100644
--- a/third_party/blink/renderer/platform/heap/persistent.h
+++ b/third_party/blink/renderer/platform/heap/persistent.h
@@ -197,13 +197,6 @@
     region.FreePersistentNode(persistent_node_);
   }
 
- protected:
-  NO_SANITIZE_ADDRESS
-  T* AtomicGet() {
-    return reinterpret_cast<T*>(AcquireLoad(reinterpret_cast<void* volatile*>(
-        const_cast<typename std::remove_const<T>::type**>(&raw_))));
-  }
-
  private:
   NO_SANITIZE_ADDRESS
   void Assign(T* ptr) {
@@ -511,8 +504,6 @@
   CrossThreadPersistent(const Member<U>& other) : Parent(other) {}
   CrossThreadPersistent(WTF::HashTableDeletedValueType x) : Parent(x) {}
 
-  T* AtomicGet() { return Parent::AtomicGet(); }
-
   // Instead of using release(), assign then clear() instead.
   // Using release() with per thread heap enabled can cause the object to be
   // destroyed before assigning it to a new handle.
diff --git a/third_party/blink/renderer/platform/heap/persistent_node.cc b/third_party/blink/renderer/platform/heap/persistent_node.cc
index 2ebb072..ce00cc36e 100644
--- a/third_party/blink/renderer/platform/heap/persistent_node.cc
+++ b/third_party/blink/renderer/platform/heap/persistent_node.cc
@@ -182,7 +182,7 @@
           reinterpret_cast<CrossThreadPersistent<DummyGCBase>*>(
               slots->slot_[i].Self());
       DCHECK(persistent);
-      void* raw_object = persistent->AtomicGet();
+      void* raw_object = persistent->Get();
       if (!raw_object)
         continue;
       BasePage* page = PageFromObject(raw_object);
diff --git a/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc
index bc794ff3..5e80c87 100644
--- a/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/gif/gif_image_decoder_test.cc
@@ -42,7 +42,7 @@
 
 namespace {
 
-const char kLayoutTestResourcesDir[] = "web_tests/images/resources";
+const char kWebTestsResourcesDir[] = "web_tests/images/resources";
 
 std::unique_ptr<ImageDecoder> CreateDecoder() {
   return std::make_unique<GIFImageDecoder>(
@@ -66,7 +66,7 @@
   std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
 
   scoped_refptr<SharedBuffer> data =
-      ReadFile(kLayoutTestResourcesDir, "animated.gif");
+      ReadFile(kWebTestsResourcesDir, "animated.gif");
   ASSERT_TRUE(data.get());
   decoder->SetData(data.get(), true);
 
@@ -90,7 +90,7 @@
 TEST(GIFImageDecoderTest, crbug779261) {
   std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
   scoped_refptr<SharedBuffer> data =
-      ReadFile(kLayoutTestResourcesDir, "crbug779261.gif");
+      ReadFile(kWebTestsResourcesDir, "crbug779261.gif");
   ASSERT_TRUE(data.get());
   decoder->SetData(data.get(), true);
 
@@ -112,7 +112,7 @@
   std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
 
   scoped_refptr<SharedBuffer> data =
-      ReadFile(kLayoutTestResourcesDir, "animated.gif");
+      ReadFile(kWebTestsResourcesDir, "animated.gif");
   ASSERT_TRUE(data.get());
   decoder->SetData(data.get(), true);
 
@@ -135,7 +135,7 @@
   std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
 
   const Vector<char> data =
-      ReadFile(kLayoutTestResourcesDir, "animated.gif")->CopyAs<Vector<char>>();
+      ReadFile(kWebTestsResourcesDir, "animated.gif")->CopyAs<Vector<char>>();
 
   size_t frame_count = 0;
 
@@ -157,7 +157,7 @@
 }
 
 TEST(GIFImageDecoderTest, parseAndDecodeByteByByte) {
-  TestByteByByteDecode(&CreateDecoder, kLayoutTestResourcesDir,
+  TestByteByByteDecode(&CreateDecoder, kWebTestsResourcesDir,
                        "animated-gif-with-offsets.gif", 5u,
                        kAnimationLoopInfinite);
 }
@@ -184,7 +184,7 @@
   std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
 
   const Vector<char> data =
-      ReadFile(kLayoutTestResourcesDir, "animated.gif")->CopyAs<Vector<char>>();
+      ReadFile(kWebTestsResourcesDir, "animated.gif")->CopyAs<Vector<char>>();
 
   ASSERT_GE(data.size(), 10u);
   scoped_refptr<SharedBuffer> temp_data =
@@ -204,7 +204,7 @@
   std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
 
   scoped_refptr<SharedBuffer> data =
-      ReadFile(kLayoutTestResourcesDir, "animated.gif");
+      ReadFile(kWebTestsResourcesDir, "animated.gif");
   ASSERT_TRUE(data.get());
   decoder->SetData(data.get(), true);
 
@@ -219,7 +219,7 @@
   std::unique_ptr<ImageDecoder> decoder = CreateDecoder();
 
   scoped_refptr<SharedBuffer> data_buffer =
-      ReadFile(kLayoutTestResourcesDir, "animated.gif");
+      ReadFile(kWebTestsResourcesDir, "animated.gif");
   ASSERT_TRUE(data_buffer.get());
   const Vector<char> data = data_buffer->CopyAs<Vector<char>>();
 
@@ -265,16 +265,16 @@
 
 TEST(GIFImageDecoderTest, updateRequiredPreviousFrameAfterFirstDecode) {
   TestUpdateRequiredPreviousFrameAfterFirstDecode(
-      &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif");
+      &CreateDecoder, kWebTestsResourcesDir, "animated-10color.gif");
 }
 
 TEST(GIFImageDecoderTest, randomFrameDecode) {
   // Single frame image.
   TestRandomFrameDecode(&CreateDecoder, kDecodersTestingDir, "radient.gif");
   // Multiple frame images.
-  TestRandomFrameDecode(&CreateDecoder, kLayoutTestResourcesDir,
+  TestRandomFrameDecode(&CreateDecoder, kWebTestsResourcesDir,
                         "animated-gif-with-offsets.gif");
-  TestRandomFrameDecode(&CreateDecoder, kLayoutTestResourcesDir,
+  TestRandomFrameDecode(&CreateDecoder, kWebTestsResourcesDir,
                         "animated-10color.gif");
 }
 
@@ -284,14 +284,14 @@
       &CreateDecoder, kDecodersTestingDir, "radient.gif");
   // Multiple frame images.
   TestRandomDecodeAfterClearFrameBufferCache(
-      &CreateDecoder, kLayoutTestResourcesDir, "animated-gif-with-offsets.gif");
+      &CreateDecoder, kWebTestsResourcesDir, "animated-gif-with-offsets.gif");
   TestRandomDecodeAfterClearFrameBufferCache(
-      &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif");
+      &CreateDecoder, kWebTestsResourcesDir, "animated-10color.gif");
 }
 
 TEST(GIFImageDecoderTest, resumePartialDecodeAfterClearFrameBufferCache) {
   TestResumePartialDecodeAfterClearFrameBufferCache(
-      &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif");
+      &CreateDecoder, kWebTestsResourcesDir, "animated-10color.gif");
 }
 
 // The first LZW codes in the image are invalid values that try to create a loop
@@ -372,13 +372,13 @@
 }
 
 TEST(GIFImageDecoderTest, verifyRepetitionCount) {
-  TestRepetitionCount(kLayoutTestResourcesDir, "full2loop.gif", 2);
+  TestRepetitionCount(kWebTestsResourcesDir, "full2loop.gif", 2);
   TestRepetitionCount(kDecodersTestingDir, "radient.gif", kAnimationNone);
 }
 
 TEST(GIFImageDecoderTest, repetitionCountChangesWhenSeen) {
   scoped_refptr<SharedBuffer> full_data_buffer =
-      ReadFile(kLayoutTestResourcesDir, "animated-10color.gif");
+      ReadFile(kWebTestsResourcesDir, "animated-10color.gif");
   ASSERT_TRUE(full_data_buffer.get());
   const Vector<char> full_data = full_data_buffer->CopyAs<Vector<char>>();
 
@@ -462,7 +462,7 @@
 // Ensure that calling SetMemoryAllocator does not short-circuit
 // InitializeNewFrame.
 TEST(GIFImageDecoderTest, externalAllocator) {
-  auto data = ReadFile(kLayoutTestResourcesDir, "boston.gif");
+  auto data = ReadFile(kWebTestsResourcesDir, "boston.gif");
   ASSERT_TRUE(data.get());
 
   auto decoder = CreateDecoder();
@@ -480,7 +480,7 @@
 }
 
 TEST(GIFImageDecoderTest, recursiveDecodeFailure) {
-  auto data = ReadFile(kLayoutTestResourcesDir, "count-down-color-test.gif");
+  auto data = ReadFile(kWebTestsResourcesDir, "count-down-color-test.gif");
   ASSERT_TRUE(data.get());
 
   {
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
index d1d6578..e55f300 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.cc
@@ -15,7 +15,7 @@
 namespace blink {
 
 scoped_refptr<SharedBuffer> ReadFile(const char* file_name) {
-  String file_path = test::BlinkLayoutTestsDir();
+  String file_path = test::BlinkWebTestsDir();
   file_path.append(file_name);
   return test::ReadFromFile(file_path);
 }
@@ -23,7 +23,7 @@
 scoped_refptr<SharedBuffer> ReadFile(const char* dir, const char* file_name) {
   StringBuilder file_path;
   if (strncmp(dir, "web_tests/", 10) == 0) {
-    file_path.Append(test::BlinkLayoutTestsDir());
+    file_path.Append(test::BlinkWebTestsDir());
     file_path.Append('/');
     file_path.Append(dir + 10);
   } else {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index bb2a284c..f774159 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1212,6 +1212,10 @@
       status: "experimental",
     },
     {
+      name: "StorageQuotaDetails",
+      status: "experimental",
+    },
+    {
       name: "TextUnderlinePositionLeftRight",
       status: "stable",
     },
@@ -1351,10 +1355,6 @@
       status: "test",
     },
     {
-      name: "WebLocksAPI",
-      status: "stable",
-    },
-    {
       name: "WebNFC",
       status: "experimental",
     },
diff --git a/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc b/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc
index 4420d63..dae0154 100644
--- a/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc
+++ b/third_party/blink/renderer/platform/testing/fuzzed_data_provider.cc
@@ -11,8 +11,7 @@
 
 CString FuzzedDataProvider::ConsumeBytesInRange(uint32_t min_bytes,
                                                 uint32_t max_bytes) {
-  size_t num_bytes =
-      static_cast<size_t>(provider_.ConsumeUint32InRange(min_bytes, max_bytes));
+  size_t num_bytes = provider_.ConsumeIntegralInRange(min_bytes, max_bytes);
   std::vector<char> bytes = provider_.ConsumeBytes<char>(num_bytes);
   return CString(bytes.data(), bytes.size());
 }
@@ -22,12 +21,4 @@
   return CString(bytes.data(), bytes.size());
 }
 
-bool FuzzedDataProvider::ConsumeBool() {
-  return provider_.ConsumeBool();
-}
-
-int FuzzedDataProvider::ConsumeInt32InRange(int min, int max) {
-  return provider_.ConsumeInt32InRange(min, max);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h b/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h
index ee8897f..f3dac90 100644
--- a/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h
+++ b/third_party/blink/renderer/platform/testing/fuzzed_data_provider.h
@@ -28,19 +28,22 @@
   CString ConsumeRemainingBytes();
 
   // Returns a bool, or false when no data remains.
-  bool ConsumeBool();
+  bool ConsumeBool() { return provider_.ConsumeBool(); }
 
   // Returns a number in the range [min, max] by consuming bytes from the input
   // data. The value might not be uniformly distributed in the given range. If
   // there's no input data left, always returns |min|. |min| must be less than
   // or equal to |max|.
-  int ConsumeInt32InRange(int min, int max);
+  template <typename T>
+  T ConsumeIntegralInRange(T min, T max) {
+    return provider_.ConsumeIntegralInRange<T>(min, max);
+  }
 
   // Returns a value from |array|, consuming as many bytes as needed to do so.
   // |array| must be a fixed-size array.
-  template <typename Type, size_t size>
-  Type PickValueInArray(Type (&array)[size]) {
-    return array[provider_.ConsumeUint32InRange(0, size - 1)];
+  template <typename T, size_t size>
+  T PickValueInArray(T (&array)[size]) {
+    return array[provider_.ConsumeIntegralInRange<size_t>(0, size - 1)];
   }
 
   // Reports the remaining bytes available for fuzzed input.
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.cc b/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
index 3fbeb71..5d18e76 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
@@ -51,8 +51,7 @@
       path.Append(FILE_PATH_LITERAL("third_party/blink")));
 }
 
-// TODO(tkent): Rename this function.  crbug.com/843412.
-base::FilePath LayoutTestsFilePath() {
+base::FilePath WebTestsFilePath() {
   base::FilePath path;
   base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
   return base::MakeAbsoluteFilePath(
@@ -94,8 +93,8 @@
   return FilePathToWebString(BlinkRootFilePath());
 }
 
-String BlinkLayoutTestsDir() {
-  return FilePathToWebString(LayoutTestsFilePath());
+String BlinkWebTestsDir() {
+  return FilePathToWebString(WebTestsFilePath());
 }
 
 String ExecutableDir() {
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.h b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
index a04c53d..832b5d8 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
@@ -52,9 +52,9 @@
 // /src/third_party/blink.
 String BlinkRootDir();
 
-// Returns Blink LayoutTests directory as an absolute path, e.g.
-// /src/third_party/WebKit/LayoutTests.
-String BlinkLayoutTestsDir();
+// Returns Blink web_tests directory as an absolute path, e.g.
+// /src/third_party/blink/web_tests.
+String BlinkWebTestsDir();
 
 // Returns directory containing the current executable as absolute path.
 String ExecutableDir();
diff --git a/third_party/blink/renderer/platform/text/DEPS b/third_party/blink/renderer/platform/text/DEPS
index 9ab8273..05eb49a63 100644
--- a/third_party/blink/renderer/platform/text/DEPS
+++ b/third_party/blink/renderer/platform/text/DEPS
@@ -10,10 +10,10 @@
     "+third_party/blink/renderer/platform/heap",
     "+third_party/blink/renderer/platform/language.h",
     "+third_party/blink/renderer/platform/mac/version_util_mac.h",
-    "+third_party/blink/renderer/platform/layout_test_support.h",
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/runtime_enabled_features.h",
     "+third_party/blink/renderer/platform/testing",
+    "+third_party/blink/renderer/platform/web_test_support.h",
     "+third_party/blink/renderer/platform/weborigin",
     "+third_party/blink/renderer/platform/wtf",
 ]
diff --git a/third_party/blink/renderer/platform/text/locale_mac.mm b/third_party/blink/renderer/platform/text/locale_mac.mm
index 6271af2..490dbff0 100644
--- a/third_party/blink/renderer/platform/text/locale_mac.mm
+++ b/third_party/blink/renderer/platform/text/locale_mac.mm
@@ -35,7 +35,7 @@
 #include <memory>
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/language.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/retain_ptr.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -52,7 +52,7 @@
 }
 
 static RetainPtr<NSLocale> DetermineLocale(const String& locale) {
-  if (!LayoutTestSupport::IsRunningLayoutTest()) {
+  if (!WebTestSupport::IsRunningWebTest()) {
     RetainPtr<NSLocale> current_locale = [NSLocale currentLocale];
     String current_locale_language =
         LanguageFromLocale(String([current_locale.Get() localeIdentifier]));
diff --git a/third_party/blink/renderer/platform/text/locale_win.cc b/third_party/blink/renderer/platform/text/locale_win.cc
index 2030fbf..8b5049f 100644
--- a/third_party/blink/renderer/platform/text/locale_win.cc
+++ b/third_party/blink/renderer/platform/text/locale_win.cc
@@ -36,8 +36,8 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/date_components.h"
 #include "third_party/blink/renderer/platform/language.h"
-#include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/text/date_time_format.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/date_math.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
@@ -93,7 +93,7 @@
 std::unique_ptr<Locale> Locale::Create(const String& locale) {
   // Whether the default settings for the locale should be used, ignoring user
   // overrides.
-  bool defaults_for_locale = LayoutTestSupport::IsRunningLayoutTest();
+  bool defaults_for_locale = WebTestSupport::IsRunningWebTest();
   return LocaleWin::Create(LCIDFromLocale(locale, defaults_for_locale),
                            defaults_for_locale);
 }
diff --git a/third_party/blink/renderer/platform/layout_test_support.cc b/third_party/blink/renderer/platform/web_test_support.cc
similarity index 74%
rename from third_party/blink/renderer/platform/layout_test_support.cc
rename to third_party/blink/renderer/platform/web_test_support.cc
index a5c3688..66a26d6c 100644
--- a/third_party/blink/renderer/platform/layout_test_support.cc
+++ b/third_party/blink/renderer/platform/web_test_support.cc
@@ -28,28 +28,28 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/web_test_support.h"
 
 #include "third_party/blink/public/web/blink.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 
 namespace blink {
 
-// Wrapper functions defined in WebKit.h
+// Wrapper functions defined in blink.h
 void SetLayoutTestMode(bool value) {
-  LayoutTestSupport::SetIsRunningLayoutTest(value);
+  WebTestSupport::SetIsRunningWebTest(value);
 }
 
 bool LayoutTestMode() {
-  return LayoutTestSupport::IsRunningLayoutTest();
+  return WebTestSupport::IsRunningWebTest();
 }
 
 void SetFontAntialiasingEnabledForTest(bool value) {
-  LayoutTestSupport::SetFontAntialiasingEnabledForTest(value);
+  WebTestSupport::SetFontAntialiasingEnabledForTest(value);
 }
 
 bool FontAntialiasingEnabledForTest() {
-  return LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+  return WebTestSupport::IsFontAntialiasingEnabledForTest();
 }
 
 static bool g_is_running_layout_test = false;
@@ -57,36 +57,36 @@
 static bool g_is_font_antialiasing_enabled = false;
 static bool g_is_subpixel_positioning_allowed = true;
 
-bool LayoutTestSupport::IsRunningLayoutTest() {
+bool WebTestSupport::IsRunningWebTest() {
   return g_is_running_layout_test;
 }
 
-void LayoutTestSupport::SetIsRunningLayoutTest(bool value) {
+void WebTestSupport::SetIsRunningWebTest(bool value) {
   g_is_running_layout_test = value;
 }
 
-bool LayoutTestSupport::IsMockThemeEnabledForTest() {
+bool WebTestSupport::IsMockThemeEnabledForTest() {
   return g_is_mock_theme_enabled;
 }
 
-void LayoutTestSupport::SetMockThemeEnabledForTest(bool value) {
+void WebTestSupport::SetMockThemeEnabledForTest(bool value) {
   DCHECK(g_is_running_layout_test);
   g_is_mock_theme_enabled = value;
 }
 
-bool LayoutTestSupport::IsFontAntialiasingEnabledForTest() {
+bool WebTestSupport::IsFontAntialiasingEnabledForTest() {
   return g_is_font_antialiasing_enabled;
 }
 
-void LayoutTestSupport::SetFontAntialiasingEnabledForTest(bool value) {
+void WebTestSupport::SetFontAntialiasingEnabledForTest(bool value) {
   g_is_font_antialiasing_enabled = value;
 }
 
-bool LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest() {
+bool WebTestSupport::IsTextSubpixelPositioningAllowedForTest() {
   return g_is_subpixel_positioning_allowed;
 }
 
-void LayoutTestSupport::SetTextSubpixelPositioningAllowedForTest(bool value) {
+void WebTestSupport::SetTextSubpixelPositioningAllowedForTest(bool value) {
   g_is_subpixel_positioning_allowed = value;
 }
 
diff --git a/third_party/blink/renderer/platform/layout_test_support.h b/third_party/blink/renderer/platform/web_test_support.h
similarity index 85%
rename from third_party/blink/renderer/platform/layout_test_support.h
rename to third_party/blink/renderer/platform/web_test_support.h
index 161819f..a3e5e5f 100644
--- a/third_party/blink/renderer/platform/layout_test_support.h
+++ b/third_party/blink/renderer/platform/web_test_support.h
@@ -28,20 +28,20 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_TEST_SUPPORT_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_TEST_SUPPORT_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_TEST_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_TEST_SUPPORT_H_
 
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
-class LayoutTestSupport {
-  STATIC_ONLY(LayoutTestSupport);
+class WebTestSupport {
+  STATIC_ONLY(WebTestSupport);
 
  public:
-  PLATFORM_EXPORT static bool IsRunningLayoutTest();
-  PLATFORM_EXPORT static void SetIsRunningLayoutTest(bool);
+  PLATFORM_EXPORT static bool IsRunningWebTest();
+  PLATFORM_EXPORT static void SetIsRunningWebTest(bool);
   PLATFORM_EXPORT static bool IsMockThemeEnabledForTest();
   PLATFORM_EXPORT static void SetMockThemeEnabledForTest(bool);
   PLATFORM_EXPORT static bool IsFontAntialiasingEnabledForTest();
@@ -52,4 +52,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LAYOUT_TEST_SUPPORT_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEB_TEST_SUPPORT_H_
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index 98500fb..81b8fa1 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/renderer/platform/wtf/wtf.h"
 #include "url/url_canon.h"
 #include "url/url_canon_ip.h"
+#include "url/url_util.h"
 
 namespace blink {
 
@@ -121,13 +122,19 @@
     return true;
 
   // Nonstandard schemes and unregistered schemes aren't known to contain hosts
-  // and/or ports, so they'll usually be placed in opaque origins. An exception
-  // is made for non-standard local schemes.
-  // TODO: Migrate "content:" and "externalfile:" to be standard schemes, and
-  // remove the local scheme exception.
-  if (!relevant_url.CanSetHostOrPort() &&
-      !SchemeRegistry::ShouldTreatURLSchemeAsLocal(relevant_url.Protocol())) {
-    return true;
+  // and/or ports, so they'll usually be placed in opaque origins.
+  if (!relevant_url.CanSetHostOrPort()) {
+    // A temporary exception is made for non-standard local schemes.
+    // TODO: Migrate "content:" and "externalfile:" to be standard schemes, and
+    // remove the local scheme exception.
+    if (SchemeRegistry::ShouldTreatURLSchemeAsLocal(relevant_url.Protocol()))
+      return false;
+
+    // Otherwise, treat non-standard origins as opaque, unless the Android
+    // WebView workaround is enabled. If the workaround is enabled, return false
+    // so that the scheme is retained, to avoid breaking XHRs on custom schemes,
+    // et cetera.
+    return !url::AllowNonStandardSchemesForAndroidWebView();
   }
 
   // This is the common case.
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index 8a95d1e..e77bd92 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -790,4 +790,21 @@
   }
 }
 
+TEST_F(SecurityOriginTest, NonStandardScheme) {
+  scoped_refptr<const SecurityOrigin> origin =
+      SecurityOrigin::CreateFromString("cow://");
+  EXPECT_TRUE(origin->IsOpaque());
+}
+
+TEST_F(SecurityOriginTest, NonStandardSchemeWithAndroidWebViewHack) {
+  url::EnableNonStandardSchemesForAndroidWebView();
+  scoped_refptr<const SecurityOrigin> origin =
+      SecurityOrigin::CreateFromString("cow://");
+  EXPECT_FALSE(origin->IsOpaque());
+  EXPECT_EQ("cow", origin->Protocol());
+  EXPECT_EQ("", origin->Host());
+  EXPECT_EQ(0, origin->Port());
+  url::Shutdown();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index b035c8e..68a70e67 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -231,6 +231,7 @@
             'css_parsing_utils::.+',
             'cssvalue::.+',
             'encoding::.+',
+            'encoding_enum::.+',
             'event_handling_util::.+',
             'event_util::.+',
             'file_error::.+',
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/android.py b/third_party/blink/tools/blinkpy/web_tests/port/android.py
index 52fad99..cb2b390 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/android.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/android.py
@@ -37,6 +37,7 @@
 
 from blinkpy.common import exit_codes
 from blinkpy.common.path_finder import RELATIVE_WEB_TESTS
+from blinkpy.common.path_finder import WEB_TESTS_LAST_COMPONENT
 from blinkpy.common.path_finder import get_chromium_src_dir
 from blinkpy.common.system.executive import ScriptError
 from blinkpy.common.system.profiler import SingleFileOutputProfiler
@@ -112,8 +113,11 @@
 # but we use a file-to-http feature to bridge the file request to host's http
 # server to get the real test files and corresponding resources.
 # See webkit/support/platform_support_android.cc for the other side of this bridge.
+# WEB_TEST_PATH_PREFIX should be matched to the local directory name of
+# web_tests because some tests and test_runner find test root directory
+# with it.
 PERF_TEST_PATH_PREFIX = '/PerformanceTests'
-LAYOUT_TEST_PATH_PREFIX = '/LayoutTests'
+WEB_TESTS_PATH_PREFIX = '/' + WEB_TESTS_LAST_COMPONENT
 
 # We start netcat processes for each of the three stdio streams. In doing so,
 # we attempt to use ports starting from 10201. This starting value is
@@ -469,7 +473,7 @@
 
     def start_http_server(self, additional_dirs, number_of_drivers):
         additional_dirs[PERF_TEST_PATH_PREFIX] = self._perf_tests_dir()
-        additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir()
+        additional_dirs[WEB_TESTS_PATH_PREFIX] = self.layout_tests_dir()
         super(AndroidPort, self).start_http_server(additional_dirs, number_of_drivers)
 
     def create_driver(self, worker_number, no_timeout=False):
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py b/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
index 617b881..88a1fde 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/fuchsia.py
@@ -35,6 +35,7 @@
 import threading
 
 from blinkpy.common import exit_codes
+from blinkpy.common.path_finder import WEB_TESTS_LAST_COMPONENT
 from blinkpy.common.path_finder import get_chromium_src_dir
 from blinkpy.web_tests.port import base
 from blinkpy.web_tests.port import driver
@@ -70,9 +71,12 @@
 # Path to the content shell package relative to the build directory.
 CONTENT_SHELL_PACKAGE_PATH = 'gen/content/shell/content_shell/content_shell.far'
 
-# HTTP path prefix for the HTTP server.
+# HTTP path prefixes for the HTTP server.
+# WEB_TEST_PATH_PREFIX should be matched to the local directory name of
+# web_tests because some tests and test_runner find test root directory
+# with it.
 PERF_TEST_PATH_PREFIX = '/PerformanceTests'
-LAYOUT_TEST_PATH_PREFIX = '/LayoutTests'
+WEB_TESTS_PATH_PREFIX = '/' + WEB_TESTS_LAST_COMPONENT
 
 # Paths to the directory where the fonts are copied to. Must match the path in
 # content/shell/app/blink_test_platform_support_fuchsia.cc .
@@ -245,7 +249,7 @@
 
     def start_http_server(self, additional_dirs, number_of_drivers):
         additional_dirs[PERF_TEST_PATH_PREFIX] = self._perf_tests_dir()
-        additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir()
+        additional_dirs[WEB_TESTS_PATH_PREFIX] = self.layout_tests_dir()
         super(FuchsiaPort, self).start_http_server(
             additional_dirs, number_of_drivers)
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService b/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService
index 926cdd6..42616245 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService
@@ -17,9 +17,6 @@
 crbug.com/896924 http/tests/inspector-protocol/network/interception-multiclient.js [ Pass ]
 crbug.com/899303 http/tests/inspector-protocol/fetch/fetch-basic.js [ Pass ]
 
-# Flaky on non-NetworkService (disabled), consistent failing on NetworkService. Probably due to DCHECK.
-crbug.com/849670 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Pass Failure ]
-
 # This passes in content_shell but not in chrome with network service disabled,
 # because content_shell does not add the about: handler. With network service
 # enabled this fails in both content_shell and chrome.
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 415302032..b6602b1c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -4298,7 +4298,7 @@
 
 crbug.com/688670 external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-rendering-after-controls-removed.html [ Pass Failure ]
 
-crbug.com/849670 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Pass Crash Failure Timeout ]
+crbug.com/849670 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Pass Timeout ]
 
 crbug.com/664858 virtual/threaded/fast/scroll-behavior/overflow-scroll-animates.html [ Skip ]
 crbug.com/664858 virtual/threaded/fast/scroll-behavior/smooth-scroll/horizontal-smooth-scroll-in-rtl.html [ Skip ]
diff --git a/third_party/blink/web_tests/css-shadow-parts/double-forward.html b/third_party/blink/web_tests/css-shadow-parts/double-forward.html
index 508f647..c938b12 100644
--- a/third_party/blink/web_tests/css-shadow-parts/double-forward.html
+++ b/third_party/blink/web_tests/css-shadow-parts/double-forward.html
@@ -22,11 +22,11 @@
     <template id="custom-element-outer-template"><custom-element-middle id="c-e-middle" exportparts="part-forwarded1: part-forwarded2"></custom-element-middle></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-outer", "c-e-middle", "c-e-inner", "green_part"]);
+        const el = getElementByShadowIds(document, ["c-e-outer", "c-e-middle", "c-e-inner", "green_part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Part in inner host is forwarded through the middle host for styling by document style sheet");
     </script>
diff --git a/third_party/blink/web_tests/css-shadow-parts/invalidation-change-part-name-forward.html b/third_party/blink/web_tests/css-shadow-parts/invalidation-change-part-name-forward.html
index 0f29f263..87d5952 100644
--- a/third_party/blink/web_tests/css-shadow-parts/invalidation-change-part-name-forward.html
+++ b/third_party/blink/web_tests/css-shadow-parts/invalidation-change-part-name-forward.html
@@ -20,13 +20,13 @@
     <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" exportparts="partp: part-forwarded"></custom-element-inner></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
-    <script type="text/javascript">
+    <script>
       "use strict";
       test(function() {
-        var part = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "part"]);
-        var before = window.getComputedStyle(part).color;
+        const part = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "part"]);
+        const before = window.getComputedStyle(part).color;
         part.setAttribute("part", "new-partp");
-        var after = window.getComputedStyle(part).color;
+        const after = window.getComputedStyle(part).color;
         assert_not_equals(before, after);
       }, "Part in selected host changed color");
     </script>
diff --git a/third_party/blink/web_tests/css-shadow-parts/invalidation-complex-selector-forward.html b/third_party/blink/web_tests/css-shadow-parts/invalidation-complex-selector-forward.html
index 51c8c91..cc374239 100644
--- a/third_party/blink/web_tests/css-shadow-parts/invalidation-complex-selector-forward.html
+++ b/third_party/blink/web_tests/css-shadow-parts/invalidation-complex-selector-forward.html
@@ -20,13 +20,13 @@
     <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" exportparts="partp: part-forwarded"></custom-element-inner></template>
     The following text should be green:
     <div id="elem"><custom-element-outer id="c-e-outer"></custom-element-outer></div>
-    <script type="text/javascript">
+    <script>
       "use strict";
       test(function() {
-        var part = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "part"]);
-        var before = window.getComputedStyle(part).color;
+        const part = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "part"]);
+        const before = window.getComputedStyle(part).color;
         document.getElementById("elem").setAttribute("id", "new-elem");
-        var after = window.getComputedStyle(part).color;
+        const after = window.getComputedStyle(part).color;
         assert_not_equals(before, after);
       }, "Part in selected host changed color");
     </script>
diff --git a/third_party/blink/web_tests/css-shadow-parts/precedence-part-vs-part.html b/third_party/blink/web_tests/css-shadow-parts/precedence-part-vs-part.html
index 4be9756..bb12535 100644
--- a/third_party/blink/web_tests/css-shadow-parts/precedence-part-vs-part.html
+++ b/third_party/blink/web_tests/css-shadow-parts/precedence-part-vs-part.html
@@ -23,11 +23,11 @@
     </template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "green_part"]);
+        const el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "green_part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Style from document overrides style from outer CE");
     </script>
diff --git a/third_party/blink/web_tests/css-shadow-parts/simple-forward-shorthand.html b/third_party/blink/web_tests/css-shadow-parts/simple-forward-shorthand.html
index 6dba9e8..720bdd0d 100644
--- a/third_party/blink/web_tests/css-shadow-parts/simple-forward-shorthand.html
+++ b/third_party/blink/web_tests/css-shadow-parts/simple-forward-shorthand.html
@@ -20,11 +20,11 @@
     <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" exportparts="partp"></custom-element-inner></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "green_part"]);
+        const el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "green_part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Part in inner host is forwarded, under the same name, for styling by document style sheet");
     </script>
diff --git a/third_party/blink/web_tests/css-shadow-parts/simple-forward.html b/third_party/blink/web_tests/css-shadow-parts/simple-forward.html
index 7f01f34..a41e0728 100644
--- a/third_party/blink/web_tests/css-shadow-parts/simple-forward.html
+++ b/third_party/blink/web_tests/css-shadow-parts/simple-forward.html
@@ -20,11 +20,11 @@
     <template id="custom-element-outer-template"><custom-element-inner id="c-e-inner" exportparts="partp: part-forwarded"></custom-element-inner></template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "green_part"]);
+        const el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "green_part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Part in inner host is forwarded for styling by document style sheet");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/all-hosts.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/all-hosts.html
index e6646c0..218535d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/all-hosts.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/all-hosts.html
@@ -20,15 +20,15 @@
     <custom-element id="c-e-1"></custom-element>
     The following text should be green:
     <custom-element id="c-e-2"></custom-element>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-1", "part"]);
+        const el = getElementByShadowIds(document, ["c-e-1", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "::part with host selector styles in first host");
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-2", "part"]);
+        const el = getElementByShadowIds(document, ["c-e-2", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "::part with host selector styles in second host");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/chaining-invalid-selector.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/chaining-invalid-selector.html
index 1130932..cb34a7b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/chaining-invalid-selector.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/chaining-invalid-selector.html
@@ -26,11 +26,11 @@
       <custom-element-inner id="c-e-inner" part="c-e-part"></custom-element-inner>
     </template>
     <custom-element-outer id="c-e-outer"></custom-element-outer>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "part"]);
+        const el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "::part cannot be chained to reach elements in the inner host");
       test(function() {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-matching.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-matching.html
index 575edabc..f8f063d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-matching.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-matching.html
@@ -18,11 +18,11 @@
     </template>
     The following text should be green:
     <div><custom-element id="c-e"></custom-element></div>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e", "part"]);
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Complex selector for host works");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-non-matching.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-non-matching.html
index 036713f..6e5bc69c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-non-matching.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/complex-non-matching.html
@@ -18,11 +18,11 @@
     </template>
     The following text should be green:
     <pre><custom-element id="c-e"></custom-element></pre>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e", "part"]);
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Non-matching complex selector for host does not style");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/different-host.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/different-host.html
index c8b5f863..7fe9744 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/different-host.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/different-host.html
@@ -18,11 +18,11 @@
     </template>
     The following text should be green:
     <custom-element id="c-e"></custom-element>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e", "part"]);
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Part is not styled when host is not selected");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/host-stylesheet.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/host-stylesheet.html
index ee8f4e7..2e65c4b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/host-stylesheet.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/host-stylesheet.html
@@ -20,11 +20,11 @@
     </template>
     The following text should be green:
     <custom-element id="c-e"></custom-element>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e", "part"]);
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Part in selected host is not styled by ::part in a stylesheet inside the host");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/inner-host.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/inner-host.html
index 8c8cec7..2dfd4b0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/inner-host.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/inner-host.html
@@ -25,16 +25,16 @@
     </template>
     The following text should be green:
     <custom-element-outer id="c-e-outer"></custom-element-outer>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorBlue = "rgb(0, 0, 255)";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-outer", "green_part"]);
+        const el = getElementByShadowIds(document, ["c-e-outer", "green_part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Part in outer host is styled by document style sheet");
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "blue_part"]);
+        const el = getElementByShadowIds(document, ["c-e-outer", "c-e-inner", "blue_part"]);
         assert_equals(window.getComputedStyle(el).color, colorBlue);
       }, "Part in inner host is not styled by document style sheet");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-change-part-name.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-change-part-name.html
index 7e7a310f..47630d9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-change-part-name.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-change-part-name.html
@@ -18,13 +18,13 @@
     </template>
     The following text should be green:
     <div><custom-element id="c-e"></custom-element></div>
-    <script type="text/javascript">
+    <script>
       "use strict";
       test(function() {
-        var part = getElementByShadowIds(document, ["c-e", "part"]);
-        var before = window.getComputedStyle(part).color;
+        const part = getElementByShadowIds(document, ["c-e", "part"]);
+        const before = window.getComputedStyle(part).color;
         part.setAttribute("part", "new-partp");
-        var after = window.getComputedStyle(part).color;
+        const after = window.getComputedStyle(part).color;
         assert_not_equals(before, after);
       }, "Part in selected host changed color");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-complex-selector.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-complex-selector.html
index e1f10e5..5b1fd80 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-complex-selector.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/invalidation-complex-selector.html
@@ -18,13 +18,13 @@
     </template>
     The following text should be green:
     <div id="elem"><custom-element id="c-e"></custom-element></div>
-    <script type="text/javascript">
+    <script>
       "use strict";
       test(function() {
-        var part = getElementByShadowIds(document, ["c-e", "part"]);
-        var before = window.getComputedStyle(part).color;
+        const part = getElementByShadowIds(document, ["c-e", "part"]);
+        const before = window.getComputedStyle(part).color;
         document.getElementById("elem").setAttribute("id", "new-elem");
-        var after = window.getComputedStyle(part).color;
+        const after = window.getComputedStyle(part).color;
         assert_not_equals(before, after);
       }, "Part in selected host changed color");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple.html
index 3733669..a7f17d9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/simple.html
@@ -18,11 +18,11 @@
     </template>
     The following text should be green:
     <custom-element id="c-e"></custom-element>
-    <script type="text/javascript">
+    <script>
       "use strict";
       const colorGreen = "rgb(0, 128, 0)";
       test(function() {
-        var el = getElementByShadowIds(document, ["c-e", "part"]);
+        const el = getElementByShadowIds(document, ["c-e", "part"]);
         assert_equals(window.getComputedStyle(el).color, colorGreen);
       }, "Part in selected host is styled");
     </script>
diff --git a/third_party/blink/web_tests/external/wpt/storage/estimate-usage-details-indexeddb.https.tentative.any.js b/third_party/blink/web_tests/external/wpt/storage/estimate-usage-details-indexeddb.https.tentative.any.js
new file mode 100644
index 0000000..c854d5b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/storage/estimate-usage-details-indexeddb.https.tentative.any.js
@@ -0,0 +1,59 @@
+// META: title=StorageManager: estimate() usage details for indexeddb
+// META: script=helpers.js
+// META: script=../IndexedDB/support-promises.js
+
+promise_test(async t => {
+  const estimate = await navigator.storage.estimate()
+  assert_equals(typeof estimate.usageDetails, 'object');
+}, 'estimate() resolves to dictionary with usageDetails member');
+
+promise_test(async t => {
+  // We use 100KB here because db compaction usually happens every few MB
+  // 100KB is large enough to avoid a false positive (small amounts of metadata
+  // getting written for some random reason), and small enough to avoid
+  // compaction with a reasonably high probability.
+  const writeSize = 1024 * 100;
+  const objectStoreName = 'store';
+  const dbname = self.location.pathname;
+
+  await indexedDB.deleteDatabase(dbname);
+  let usageAfterWrite, usageBeforeWrite;
+  // TODO(crbug.com/906867): Refactor this test to better deal with db/log
+  //                         compaction flakiness
+  // The for loop here is to help make this test less flaky.  The reason it is
+  // flaky is that database and/or log compaction could happen in the middle of
+  // this loop.  The problem is that this test runs in a large batch of tests,
+  // and previous tests might have created a lot of garbage which could trigger
+  // compaction.  Suppose the initial estimate shows 1MB usage before creating
+  // the db.  Compaction could happen after this step and before we measure
+  // usage at the end, meaning the 1MB could be wiped to 0, an extra 1024 * 100
+  // is put in, and the actual increase in usage does not reach our expected
+  // increase.  Loop 10 times here to be safe (and reduce the number of bot
+  // builds that fail); all it takes is one iteration without compaction for
+  // this to pass.
+  for (let i = 0; i < 10; i++) {
+    const db = await createDB(dbname, objectStoreName, t);
+    let estimate = await navigator.storage.estimate();
+
+    // If usage is 0, usageDetails does not include the usage (undefined)
+    usageBeforeWrite = estimate.usageDetails.indexedDB || 0;
+
+    const txn = db.transaction(objectStoreName, 'readwrite');
+    const valueToStore = largeValue(writeSize, Math.random() * 255);
+    txn.objectStore(objectStoreName).add(valueToStore, 1);
+
+    await transactionPromise(txn);
+
+    estimate = await navigator.storage.estimate();
+    usageAfterWrite = estimate.usageDetails.indexedDB;
+    db.close();
+
+    if (usageAfterWrite - usageBeforeWrite >= writeSize) {
+      break;
+    }
+  }
+
+  assert_greater_than_equal(usageAfterWrite - usageBeforeWrite,
+      writeSize);
+}, 'estimate() usage details reflects increase in indexedDB after large ' +
+   'value is stored');
diff --git a/third_party/blink/web_tests/external/wpt/storage/helpers.js b/third_party/blink/web_tests/external/wpt/storage/helpers.js
new file mode 100644
index 0000000..fbc746a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/storage/helpers.js
@@ -0,0 +1,43 @@
+/**
+ * @description - Function will create a database with the supplied name
+ *                and also create an object store with the specified name.
+ *                If a db with the name dbName exists, this will raze the
+ *                existing DB beforehand.
+ * @param {string} dbName
+ * @param {string} objectStoreName
+ * @param {testCase} t
+ * @returns {Promise} - A promise that resolves to an indexedDB open request
+ */
+function createDB(dbName, objectStoreName, t) {
+  return new Promise((resolve, reject) => {
+    const openRequest = indexedDB.open(dbName);
+    t.add_cleanup(() => {
+      indexedDB.deleteDatabase(dbName);
+    });
+
+    openRequest.onerror = () => {
+      reject(openRequest.error);
+    };
+    openRequest.onsuccess = () => {
+      resolve(openRequest.result);
+    };
+    openRequest.onupgradeneeded = event => {
+      openRequest.result.createObjectStore(objectStoreName);
+    };
+  });
+}
+
+/**
+ * @description - This function will wrap an IDBTransaction in a promise,
+ *                resolving in the oncomplete() method and rejecting with the
+ *                transaction error in the onabort() case.
+ * @param {IDBTransaction} transaction - The transaction to wrap in a promise.
+ * @returns {Promise} - A promise that resolves when the transaction is either
+ *                      aborted or completed.
+ */
+function transactionPromise(transaction) {
+  return new Promise((resolve, reject) => {
+    transaction.onabort = () => { reject(transaction.error); };
+    transaction.oncomplete = () => { resolve(); };
+  });
+}
diff --git a/third_party/blink/web_tests/http/tests/devtools/har-importer-expected.txt b/third_party/blink/web_tests/http/tests/devtools/har-importer-expected.txt
new file mode 100644
index 0000000..dc4385c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/har-importer-expected.txt
@@ -0,0 +1,242 @@
+Verifies that imported HAR files create matching NetworkRequests
+requests: [
+  {
+    "url": "http://localhost:8000/",
+    "documentURL": "http://localhost:8000/",
+    "initiator": {
+      "type": "other"
+    },
+    "requestFormData": null,
+    "connectionId": "2945",
+    "requestMethod": "GET",
+    "requestHeaders": [
+      {
+        "name": "Host",
+        "value": "localhost:8000"
+      }
+    ],
+    "mimeType": "text/html",
+    "responseHeaders": [
+      {
+        "name": "Content-Type",
+        "value": "text/html;charset=ISO-8859-1"
+      }
+    ],
+    "statusCode": 200,
+    "statusText": "OK",
+    "protocol": "http/1.1",
+    "resourceSize": 4633,
+    "transferSize": 4821,
+    "cached": false,
+    "cachedInMemory": false,
+    "contentData": {
+      "error": null,
+      "content": "fake page data",
+      "encoded": false
+    },
+    "remoteAddress": "[::1]:80",
+    "resourceType": {
+      "_name": "document",
+      "_title": "Document",
+      "_category": {
+        "title": "Documents",
+        "shortTitle": "Doc"
+      },
+      "_isTextType": true
+    },
+    "priority": "VeryHigh",
+    "finished": true,
+    "timing": {
+      "proxyStart": 2.0154479999188335,
+      "proxyEnd": 2.4644479999188333,
+      "requestTime": 1542746587.755448,
+      "dnsStart": -1,
+      "dnsEnd": -1,
+      "connectStart": -1,
+      "connectEnd": -1,
+      "sslStart": -1,
+      "sslEnd": -1,
+      "workerStart": -1,
+      "workerReady": -1,
+      "sendStart": 2.4644479999188333,
+      "sendEnd": 2.534447999918833,
+      "pushStart": 0,
+      "pushEnd": 0,
+      "receiveHeadersEnd": 5.624447982991114
+    },
+    "endTime": 1542746587.7661417
+  },
+  {
+    "url": "http://localhost:8000/post-endpoint",
+    "documentURL": "http://localhost:8000/",
+    "initiator": {
+      "type": "parser",
+      "url": "http://localhost/",
+      "lineNumber": 1
+    },
+    "requestFormData": "one=urlencodedvalueone&two=urlencodedvaluetwo",
+    "connectionId": "2945",
+    "requestMethod": "POST",
+    "requestHeaders": [],
+    "mimeType": "image/x-icon",
+    "responseHeaders": [],
+    "statusCode": 200,
+    "statusText": "OK",
+    "protocol": "http/1.1",
+    "resourceSize": 1150,
+    "transferSize": 1417,
+    "cached": false,
+    "cachedInMemory": false,
+    "contentData": {
+      "error": null,
+      "content": null,
+      "encoded": false
+    },
+    "remoteAddress": "[::1]:80",
+    "resourceType": {
+      "_name": "image",
+      "_title": "Image",
+      "_category": {
+        "title": "Images",
+        "shortTitle": "Img"
+      },
+      "_isTextType": false
+    },
+    "priority": "Low",
+    "finished": true,
+    "timing": {
+      "proxyStart": 1.7575360001232476,
+      "proxyEnd": 2.2485360001232477,
+      "requestTime": 1542746587.8705359,
+      "dnsStart": -1,
+      "dnsEnd": -1,
+      "connectStart": -1,
+      "connectEnd": -1,
+      "sslStart": -1,
+      "sslEnd": -1,
+      "workerStart": -1,
+      "workerReady": -1,
+      "sendStart": 2.2485360001232477,
+      "sendEnd": 2.3095360001232477,
+      "pushStart": 0,
+      "pushEnd": 0,
+      "receiveHeadersEnd": 2.828536113491282
+    },
+    "endTime": 1542746587.8738945
+  },
+  {
+    "url": "http://localhost:8000/js_file.js",
+    "documentURL": "http://localhost:8000/",
+    "initiator": {
+      "type": "parser",
+      "url": "http://localhost/",
+      "lineNumber": 1
+    },
+    "requestFormData": null,
+    "connectionId": "2945",
+    "requestMethod": "GET",
+    "requestHeaders": [],
+    "mimeType": "undefined",
+    "responseHeaders": [],
+    "statusCode": 200,
+    "statusText": "OK",
+    "protocol": "http/1.1",
+    "resourceSize": 1150,
+    "transferSize": 1417,
+    "cached": false,
+    "cachedInMemory": false,
+    "contentData": {
+      "error": null,
+      "content": null,
+      "encoded": false
+    },
+    "remoteAddress": "[::1]:80",
+    "resourceType": {
+      "_name": "script",
+      "_title": "Script",
+      "_category": {
+        "title": "Scripts",
+        "shortTitle": "JS"
+      },
+      "_isTextType": true
+    },
+    "priority": "Low",
+    "finished": true,
+    "timing": {
+      "proxyStart": 1.7575360001232476,
+      "proxyEnd": 2.2485360001232477,
+      "requestTime": 1542746587.8705359,
+      "dnsStart": -1,
+      "dnsEnd": -1,
+      "connectStart": -1,
+      "connectEnd": -1,
+      "sslStart": -1,
+      "sslEnd": -1,
+      "workerStart": -1,
+      "workerReady": -1,
+      "sendStart": 2.2485360001232477,
+      "sendEnd": 2.3095360001232477,
+      "pushStart": 0,
+      "pushEnd": 0,
+      "receiveHeadersEnd": 2.828536113491282
+    },
+    "endTime": 1542746587.8738945
+  },
+  {
+    "url": "http://localhost:8000/endpoint",
+    "documentURL": "http://localhost:8000/",
+    "initiator": {
+      "type": "script"
+    },
+    "requestFormData": null,
+    "connectionId": "2945",
+    "requestMethod": "GET",
+    "requestHeaders": [],
+    "mimeType": "undefined",
+    "responseHeaders": [],
+    "statusCode": 200,
+    "statusText": "OK",
+    "protocol": "http/1.1",
+    "resourceSize": 1150,
+    "transferSize": 1417,
+    "cached": false,
+    "cachedInMemory": false,
+    "contentData": {
+      "error": null,
+      "content": null,
+      "encoded": false
+    },
+    "remoteAddress": "[::1]:80",
+    "resourceType": {
+      "_name": "fetch",
+      "_title": "Fetch",
+      "_category": {
+        "title": "XHR and Fetch",
+        "shortTitle": "XHR"
+      },
+      "_isTextType": true
+    },
+    "priority": "Low",
+    "finished": true,
+    "timing": {
+      "proxyStart": 1.7575360001232476,
+      "proxyEnd": 2.2485360001232477,
+      "requestTime": 1542746587.8705359,
+      "dnsStart": -1,
+      "dnsEnd": -1,
+      "connectStart": -1,
+      "connectEnd": -1,
+      "sslStart": -1,
+      "sslEnd": -1,
+      "workerStart": -1,
+      "workerReady": -1,
+      "sendStart": 2.2485360001232477,
+      "sendEnd": 2.3095360001232477,
+      "pushStart": 0,
+      "pushEnd": 0,
+      "receiveHeadersEnd": 2.828536113491282
+    },
+    "endTime": 1542746587.8738945
+  }
+]
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/har-importer.js b/third_party/blink/web_tests/http/tests/devtools/har-importer.js
new file mode 100644
index 0000000..dd772218
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/har-importer.js
@@ -0,0 +1,264 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(
+      'Verifies that imported HAR files create matching NetworkRequests');
+  await TestRunner.loadModule('application_test_runner');
+  await TestRunner.loadModule('network_test_runner');
+  const harRoot = new HARImporter.HARRoot(harJson);
+  const requests = HARImporter.Importer.requestsFromHARLog(harRoot.log);
+  const formattedRequests = await Promise.all(requests.map(async request => {
+    return {
+      url: request.url(),
+      documentURL: request.documentURL,
+      initiator: request.initiator(),
+      requestFormData: await(request.requestFormData()),
+      connectionId: request.connectionId,
+      requestMethod: request.requestMethod,
+      requestHeaders: request.requestHeaders(),
+      mimeType: request.mimeType,
+      responseHeaders: request.responseHeaders,
+      statusCode: request.statusCode,
+      statusText: request.statusText,
+      protocol: request.protocol,
+      resourceSize: request.resourceSize,
+      transferSize: request.transferSize,
+      cached: request.cached(),
+      cachedInMemory: request.cachedInMemory(),
+      contentData: await(request.contentData()),
+      remoteAddress: request.remoteAddress(),
+      resourceType: request.resourceType(),
+      priority: request.priority(),
+      finished: request.finished,
+      timing: request.timing,
+      endTime: request.endTime
+    };
+  }));
+  TestRunner.addResult(
+      'requests: ' + JSON.stringify(formattedRequests, null, 2));
+  TestRunner.completeTest();
+})();
+
+const harJson = {
+  'log': {
+    'version': '1.2',
+    'creator': {'name': 'WebInspector', 'version': '537.36'},
+    'pages': [{
+      'startedDateTime': '2018-11-20T20:43:07.756Z',
+      'id': 'page_1',
+      'title': 'http://localhost:8000/',
+      'pageTimings':
+          {'onContentLoad': 67.84599996171892, 'onLoad': 112.05600015819073}
+    }],
+    'entries': [
+      {
+        'startedDateTime': '2018-11-20T20:43:07.755Z',
+        'time': 11.14144808263518,
+        'request': {
+          'method': 'GET',
+          'url': 'http://localhost:8000/',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [{'name': 'Host', 'value': 'localhost:8000'}],
+          'queryString': [],
+          'cookies': [],
+          'headersSize': 418,
+          'bodySize': 0
+        },
+        'response': {
+          'status': 200,
+          'statusText': 'OK',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [
+            {'name': 'Content-Type', 'value': 'text/html;charset=ISO-8859-1'}
+          ],
+          'cookies': [{
+            'name': 'test-cookie-name',
+            'value': '1',
+            'path': '/',
+            'domain': '.localhost',
+            'expires': '2018-11-19T18:17:22.000Z',
+            'httpOnly': false,
+            'secure': false
+          }],
+          'content':
+              {'size': 4633, 'mimeType': 'text/html', 'text': 'fake page data'},
+          'redirectURL': '',
+          'headersSize': 188,
+          'bodySize': 4633,
+          '_transferSize': 4821
+        },
+        'cache': {},
+        'timings': {
+          'blocked': 2.4644479999188333,
+          'dns': -1,
+          'ssl': -1,
+          'connect': -1,
+          'send': 0.06999999999999984,
+          'wait': 3.089999983072281,
+          'receive': 5.517000099644065,
+          '_blocked_queueing': 0.447999918833375,
+          '_blocked_proxy': 0.44899999999999984
+        },
+        'serverIPAddress': '[::1]',
+        '_initiator': {'type': 'other'},
+        '_priority': 'VeryHigh',
+        'connection': '2945',
+        'pageref': 'page_1'
+      },
+      {
+        'startedDateTime': '2018-11-20T20:43:07.870Z',
+        'time': 3.8945360814686865,
+        'request': {
+          'method': 'POST',
+          'url': 'http://localhost:8000/post-endpoint',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [],
+          'queryString': [],
+          'cookies': [],
+          'headersSize': 386,
+          'bodySize': 0,
+          'postData': {
+            'mimeType': 'application/x-www-form-urlencoded',
+            'text': 'one=urlencodedvalueone&two=urlencodedvaluetwo',
+            'params': [
+              {'name': 'one', 'value': 'urlencodedvalueone'},
+              {'name': 'two', 'value': 'urlencodedvaluetwo'}
+            ]
+          }
+        },
+        'response': {
+          'status': 200,
+          'statusText': 'OK',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [],
+          'cookies': [],
+          'content':
+              {'size': 1150, 'mimeType': 'image/x-icon', 'compression': 0},
+          'redirectURL': '',
+          'headersSize': 267,
+          'bodySize': 1150,
+          '_transferSize': 1417
+        },
+        'cache': {},
+        'timings': {
+          'blocked': 2.2485360001232477,
+          'dns': -1,
+          'ssl': -1,
+          'connect': -1,
+          'send': 0.06099999999999994,
+          'wait': 0.5190001133680342,
+          'receive': 1.0659999679774046,
+          '_blocked_queueing': 0.5360001232475042,
+          '_blocked_proxy': 0.4910000000000001
+        },
+        'serverIPAddress': '[::1]',
+        '_initiator':
+            {'type': 'parser', 'url': 'http://localhost/', 'lineNumber': 1},
+        '_priority': 'Low',
+        'connection': '2945',
+        'pageref': 'page_1'
+      },
+      {
+        'startedDateTime': '2018-11-20T20:43:07.870Z',
+        'time': 3.8945360814686865,
+        'request': {
+          'method': 'GET',
+          'url': 'http://localhost:8000/js_file.js',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [],
+          'queryString': [],
+          'cookies': [],
+          'headersSize': 386,
+          'bodySize': 0
+        },
+        'response': {
+          'status': 200,
+          'statusText': 'OK',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [],
+          'cookies': [],
+          'content': {'size': 1150, 'compression': 0},
+          'redirectURL': '',
+          'headersSize': 267,
+          'bodySize': 1150,
+          '_transferSize': 1417
+        },
+        'cache': {},
+        'timings': {
+          'blocked': 2.2485360001232477,
+          'dns': -1,
+          'ssl': -1,
+          'connect': -1,
+          'send': 0.06099999999999994,
+          'wait': 0.5190001133680342,
+          'receive': 1.0659999679774046,
+          '_blocked_queueing': 0.5360001232475042,
+          '_blocked_proxy': 0.4910000000000001
+        },
+        'serverIPAddress': '[::1]',
+        '_initiator':
+            {'type': 'parser', 'url': 'http://localhost/', 'lineNumber': 1},
+        '_priority': 'Low',
+        'connection': '2945',
+        'pageref': 'page_1'
+      },
+      {
+        'startedDateTime': '2018-11-20T20:43:07.870Z',
+        'time': 3.8945360814686865,
+        'request': {
+          'method': 'GET',
+          'url': 'http://localhost:8000/endpoint',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [],
+          'queryString': [],
+          'cookies': [],
+          'headersSize': 386,
+          'bodySize': 0
+        },
+        'response': {
+          'status': 200,
+          'statusText': 'OK',
+          'httpVersion': 'HTTP/1.1',
+          'headers': [],
+          'cookies': [],
+          'content': {'size': 1150, 'compression': 0},
+          'redirectURL': '',
+          'headersSize': 267,
+          'bodySize': 1150,
+          '_transferSize': 1417
+        },
+        'cache': {},
+        'timings': {
+          'blocked': 2.2485360001232477,
+          'dns': -1,
+          'ssl': -1,
+          'connect': -1,
+          'send': 0.06099999999999994,
+          'wait': 0.5190001133680342,
+          'receive': 1.0659999679774046,
+          '_blocked_queueing': 0.5360001232475042,
+          '_blocked_proxy': 0.4910000000000001
+        },
+        'serverIPAddress': '[::1]',
+        '_initiator': {
+          'type': 'script',
+          'stack': {
+            'callFrames': {
+              'functionName': '',
+              'scriptId': '32',
+              'url': 'http://localhost:8000/script.js',
+              'lineNumber': 5,
+              'colNumber': 2
+            }
+          }
+        },
+        '_priority': 'Low',
+        '_resourceType': 'fetch',
+        'connection': '2945',
+        'pageref': 'page_1'
+      }
+    ]
+  }
+};
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion-expected.txt b/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion-expected.txt
index 0922058..0547a8b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-har-conversion-expected.txt
@@ -10,6 +10,7 @@
         {
             _initiator : <object>
             _priority : <string>
+            _resourceType : "document"
             cache : {
             }
             connection : <string>
@@ -107,6 +108,7 @@
         {
             _initiator : <object>
             _priority : <string>
+            _resourceType : "xhr"
             cache : {
             }
             connection : <string>
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers-expected.txt b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers-expected.txt
index c339d1bf..2faa442b 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers-expected.txt
@@ -31,6 +31,7 @@
         url : "http://example.com/inspector-test.js"
     }
     _priority : "VeryHigh"
+    _resourceType : "fetch"
     cache : {
     }
     request : {
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
index 4d28d75a..f709eb5 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-har-headers.js
@@ -26,6 +26,7 @@
     request.resourceSize = 1000;
     request._transferSize = 539;  // 39 = header size at the end of the day
     request.setPriority('VeryHigh');
+    request.setResourceType(Common.resourceTypes.Fetch);
 
     // sample timing values used here are copied from a real request
     request.setIssueTime(357904.060558);
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-parameters-expected.txt b/third_party/blink/web_tests/http/tests/devtools/resource-parameters-expected.txt
index d8c758f..4e44c9d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-parameters-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-parameters-expected.txt
@@ -4,6 +4,7 @@
 {
     _initiator : <object>
     _priority : <string>
+    _resourceType : "document"
     cache : {
     }
     connection : <string>
diff --git a/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6-expected.txt b/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6-expected.txt
index 86b853a6..38c84c44 100644
--- a/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/resource-parameters-ipv6-expected.txt
@@ -4,6 +4,7 @@
 {
     _initiator : <object>
     _priority : <string>
+    _resourceType : "document"
     cache : {
     }
     connection : <string>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/intercept-css-enable-fetch-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/intercept-css-enable-fetch-expected.txt
new file mode 100644
index 0000000..64638cd
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/intercept-css-enable-fetch-expected.txt
@@ -0,0 +1,3 @@
+Verifies that service workers do not throw errors from devtools css enable initiated fetch.
+finished awaiting for css enable
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/intercept-css-enable-fetch.js b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/intercept-css-enable-fetch.js
new file mode 100644
index 0000000..a697d42
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/intercept-css-enable-fetch.js
@@ -0,0 +1,30 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startURL(
+      'resources/repeat-fetch-service-worker.html',
+      'Verifies that service workers do not throw errors from devtools css enable initiated fetch.');
+
+  dp.ServiceWorker.onWorkerErrorReported(error => {
+    testRunner.log(
+        'serivce worker reported error: ' + JSON.stringify(error, null, 2));
+    testRunner.completeTest();
+  });
+
+  await dp.Runtime.enable();
+  await dp.ServiceWorker.enable();
+
+  let versions;
+  do {
+    const result = await dp.ServiceWorker.onceWorkerVersionUpdated();
+    versions = result.params.versions;
+  } while (!versions.length || versions[0].status !== 'activated');
+  await versions[0].registrationId;
+
+  await dp.Page.enable();
+  await dp.Page.reload();
+
+  await dp.DOM.enable();
+  await dp.CSS.enable();
+
+  testRunner.log('finished awaiting for css enable');
+  testRunner.completeTest();
+});
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.html b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.html
new file mode 100644
index 0000000..d3dbd5ff
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>webpage for repeat-fetch-service-worker.js</title>
+<script>
+function installSW() {
+  navigator.serviceWorker.register('repeat-fetch-service-worker.js');
+}
+</script>
+</head>
+<body onload="installSW()"></body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js
new file mode 100644
index 0000000..b52e19b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/service-worker/resources/repeat-fetch-service-worker.js
@@ -0,0 +1,4 @@
+self.addEventListener('fetch', fetchEvent => {
+  console.log('service worker making fetch for url: ' + fetchEvent.request.url);
+  fetch(fetchEvent.request);
+});
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-serviceworker-disabled.js b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-serviceworker-disabled.js
deleted file mode 100644
index fb931a76..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-serviceworker-disabled.js
+++ /dev/null
@@ -1,15 +0,0 @@
-importScripts('/resources/testharness.js',
-              '/resources/origin-trials-helper.js');
-
-test(t => {
-  OriginTrialsHelper.check_interfaces_missing(
-    self,
-    ['LockManager', 'Lock']);
-}, 'Web Locks API interfaces in Origin-Trial disabled worker.');
-
-test(t => {
-  assert_false('locks' in self.navigator,
-               'locks property does not exist on navigator');
-}, 'Web Locks API entry point in Origin-Trial disabled worker.');
-
-done();
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-serviceworker-enabled.php b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-serviceworker-enabled.php
deleted file mode 100644
index 6fccca8e5..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-serviceworker-enabled.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-// Generate token with the command:
-// generate_token.py http://127.0.0.1:8000 WebLocksAPI --expire-timestamp=2000000000
-header("Origin-Trial: Aq40kr/ZTqxmfeh35cvBQcwBrmiL7pSDR6PrUZaVC7xxGe3ff4fECD/TdP+w+Ic9cXZ1ek6N4kg6oR876PQd/QoAAABTeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiV2ViTG9ja3NBUEkiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=");
-header('Content-Type: application/javascript');
-?>
-importScripts('/resources/testharness.js',
-              '/resources/origin-trials-helper.js');
-
-test(t => {
-  OriginTrialsHelper.check_properties(this,
-      {'LockManager': ['request', 'query'],
-       'Lock': ['name', 'mode'],
-       });
-}, 'Web Locks API interfaces and properties in Origin-Trial enabled serviceworker.');
-
-test(t => {
-  assert_true('locks' in self.navigator, 'locks property exists on navigator');
-}, 'Web Locks API entry point in Origin-Trial enabled serviceworker.');
-
-done();
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-worker.js b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-worker.js
deleted file mode 100644
index 69b1248..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/weblocksapi-origin-trial-interfaces-worker.js
+++ /dev/null
@@ -1,15 +0,0 @@
-importScripts('/resources/testharness.js',
-              '/resources/origin-trials-helper.js');
-
-test(t => {
-  OriginTrialsHelper.check_properties(this,
-      {'LockManager': ['request', 'query'],
-       'Lock': ['name', 'mode'],
-       });
-}, 'Web Locks API interfaces and properties in Origin-Trial enabled worker.');
-
-test(t => {
-  assert_true('locks' in self.navigator, 'locks property exists on navigator');
-}, 'Web Locks API entry point in Origin-Trial enabled worker.');
-
-done();
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/weblocksapi-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/weblocksapi-origin-trial-interfaces.html
deleted file mode 100644
index e4a5472..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/weblocksapi-origin-trial-interfaces.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<!-- Generate token with the command:
-generate_token.py http://127.0.0.1:8000 WebLocksAPI --expire-timestamp=2000000000
--- -->
-<meta http-equiv="origin-trial" content="Aq40kr/ZTqxmfeh35cvBQcwBrmiL7pSDR6PrUZaVC7xxGe3ff4fECD/TdP+w+Ic9cXZ1ek6N4kg6oR876PQd/QoAAABTeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiV2ViTG9ja3NBUEkiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=" />
-<title>Web Locks API - interfaces exposed by origin trial</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/origin-trials-helper.js"></script>
-<script src="/serviceworker/resources/test-helpers.js"></script>
-<script>
-test(t => {
-  OriginTrialsHelper.check_properties(this,
-      {'LockManager': ['request', 'query'],
-       'Lock': ['name', 'mode'],
-       });
-}, 'Web Locks API interfaces and properties in Origin-Trial enabled document.');
-
-test(t => {
-  assert_true('locks' in self.navigator, 'locks property exists on navigator');
-}, 'Web Locks API entry point in Origin-Trial enabled document.');
-
-fetch_tests_from_worker(new Worker('resources/weblocksapi-origin-trial-interfaces-worker.js'));
-
-// Only run "disabled" tests if the feature is not enabled via runtime flags.
-if (!self.internals.runtimeFlags.webLocksAPIEnabled) {
-  service_worker_test('resources/weblocksapi-origin-trial-interfaces-serviceworker-disabled.js');
-}
-
-service_worker_test('resources/weblocksapi-origin-trial-interfaces-serviceworker-enabled.php');
-
-</script>
diff --git a/third_party/blink/web_tests/platform/android/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt b/third_party/blink/web_tests/platform/android/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt
index 110fc46a..f7065a5 100644
--- a/third_party/blink/web_tests/platform/android/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt
+++ b/third_party/blink/web_tests/platform/android/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt
@@ -1,4 +1,4 @@
-Default policy for navigation to 'http://127.0.0.1:8000/LayoutTests/fast/events/blank' is 'new background tab'
+Default policy for navigation to 'http://127.0.0.1:8000/web_tests/fast/events/blank' is 'new background tab'
 Tests that hitting ctrl-enter on a link with target=_blank still opens it in the background
 
 link
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt b/third_party/blink/web_tests/platform/fuchsia/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt
index 110fc46a..f7065a5 100644
--- a/third_party/blink/web_tests/platform/fuchsia/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt
+++ b/third_party/blink/web_tests/platform/fuchsia/fast/events/simulated-click-on-anchor-with-target-blank-expected.txt
@@ -1,4 +1,4 @@
-Default policy for navigation to 'http://127.0.0.1:8000/LayoutTests/fast/events/blank' is 'new background tab'
+Default policy for navigation to 'http://127.0.0.1:8000/web_tests/fast/events/blank' is 'new background tab'
 Tests that hitting ctrl-enter on a link with target=_blank still opens it in the background
 
 link
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/storage-estimate-usage-details-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/storage-estimate-usage-details-expected.txt
new file mode 100644
index 0000000..3a3ee7ce
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/storage-estimate-usage-details-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL usageDetails should be exposed by navigator.storage.estimate(). assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/storage-estimate-usage-details.html b/third_party/blink/web_tests/webexposed/storage-estimate-usage-details.html
new file mode 100644
index 0000000..9fdcdbbe
--- /dev/null
+++ b/third_party/blink/web_tests/webexposed/storage-estimate-usage-details.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+promise_test(async function() {
+    const estimate = await navigator.storage.estimate();
+    assert_true('usageDetails' in estimate);
+}, 'usageDetails should be exposed by navigator.storage.estimate().');
+</script>
diff --git a/third_party/ced/compact_enc_det_fuzzer.cc b/third_party/ced/compact_enc_det_fuzzer.cc
index b0a73f2..9230b00 100644
--- a/third_party/ced/compact_enc_det_fuzzer.cc
+++ b/third_party/ced/compact_enc_det_fuzzer.cc
@@ -21,11 +21,12 @@
 
   CompactEncDet::TextCorpusType corpus =
       static_cast<CompactEncDet::TextCorpusType>(
-          data_provider.ConsumeInt32InRange(0, CompactEncDet::NUM_CORPA));
+          data_provider.ConsumeIntegralInRange<int32_t>(
+              0, CompactEncDet::NUM_CORPA));
   Encoding encoding_hint = static_cast<Encoding>(
-      data_provider.ConsumeInt32InRange(0, NUM_ENCODINGS));
+      data_provider.ConsumeIntegralInRange<int32_t>(0, NUM_ENCODINGS));
   Language langauge_hint = static_cast<Language>(
-      data_provider.ConsumeInt32InRange(0, NUM_LANGUAGES));
+      data_provider.ConsumeIntegralInRange<int32_t>(0, NUM_LANGUAGES));
   bool ignore_7bit_mail_encodings = data_provider.ConsumeBool();
 
   std::vector<char> text = data_provider.ConsumeRemainingBytes<char>();
diff --git a/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt b/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
index 0671774..a4b42cda 100644
--- a/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
+++ b/third_party/gvr-android-sdk/test-apks/daydream_home/apk_version_history.txt
@@ -15,3 +15,4 @@
 v1.15 d56e9ff51188bab2bc9c843aae60bd15b2fcbb96
 v1.16 a19b7e8feaa32a9d5e37de53414e8951e2b87ce7
 v1.17 686bbb327b689972721953121295becb4f5389c6
+v1.19 53d6c9f8cd18d6b48f1b62884d019914f453dfb3
diff --git a/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1 b/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
index 94bda82..4bbe003b 100644
--- a/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
+++ b/third_party/gvr-android-sdk/test-apks/daydream_home/daydream_home_current.apk.sha1
@@ -1 +1 @@
-686bbb327b689972721953121295becb4f5389c6
\ No newline at end of file
+53d6c9f8cd18d6b48f1b62884d019914f453dfb3
\ No newline at end of file
diff --git a/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt b/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
index 98b252f1..124affd7 100644
--- a/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
+++ b/third_party/gvr-android-sdk/test-apks/vr_services/apk_version_history.txt
@@ -19,3 +19,4 @@
 v1.15 712140d07ac97444cf9d1407b2bafd497e7df1fa
 v1.16 96b849029526f7d980fe66e40ee0c5641774f897
 v1.17 a8554dcd83afd1d9718bc7d341c82f3ffba2efbb
+v1.19 7378cd3efa4386491df7b010b070470f936f2e9e
diff --git a/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1 b/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
index b44a169..b86450f2 100644
--- a/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
+++ b/third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk.sha1
@@ -1 +1 @@
-a8554dcd83afd1d9718bc7d341c82f3ffba2efbb
\ No newline at end of file
+7378cd3efa4386491df7b010b070470f936f2e9e
\ No newline at end of file
diff --git a/third_party/sfntly/fuzzers/subset_font_fuzzer.cc b/third_party/sfntly/fuzzers/subset_font_fuzzer.cc
index 9bbe491..f434676 100644
--- a/third_party/sfntly/fuzzers/subset_font_fuzzer.cc
+++ b/third_party/sfntly/fuzzers/subset_font_fuzzer.cc
@@ -16,10 +16,11 @@
   constexpr int kMaxFontSize = 50 * 1024 * 1024;
   base::FuzzedDataProvider fuzzed_data(data, size);
 
-  size_t font_name_size = fuzzed_data.ConsumeUint32InRange(0, kMaxFontNameSize);
+  size_t font_name_size =
+      fuzzed_data.ConsumeIntegralInRange(0, kMaxFontNameSize);
   std::string font_name = fuzzed_data.ConsumeBytesAsString(font_name_size);
 
-  size_t font_str_size = fuzzed_data.ConsumeUint32InRange(0, kMaxFontSize);
+  size_t font_str_size = fuzzed_data.ConsumeIntegralInRange(0, kMaxFontSize);
   std::vector<unsigned char> font_str =
       fuzzed_data.ConsumeBytes<unsigned char>(font_str_size);
 
diff --git a/third_party/webdriver/README.chromium b/third_party/webdriver/README.chromium
index 839fd44..17d67f4 100644
--- a/third_party/webdriver/README.chromium
+++ b/third_party/webdriver/README.chromium
@@ -18,7 +18,7 @@
   atoms.h, atoms.cc
     These atoms are generated by the webdriver team and are to be checked in
     manually. The current version was generated from revision
-    a3444b8f4dbb6e83b8710455e6d8352439ac1874.
+    53f0818fe574877841c331e9647a35985fbfe957.
 
     To generate the atoms using the code found in selenium tree:
       $ git clone https://github.com/SeleniumHQ/selenium.git
diff --git a/third_party/webdriver/atoms.cc b/third_party/webdriver/atoms.cc
index 6a8e9fc..ad0da6d 100644
--- a/third_party/webdriver/atoms.cc
+++ b/third_party/webdriver/atoms.cc
@@ -689,17 +689,17 @@
     "f]),c=Math.min(c,a[f+1]),e=Math.max(e,a[f+1]);return new U(b,c,d-b,e-c)",
     "}return new U(0,0,0,0)}function yc(a){a=tc(a);return new hc(a.top,a.lef",
     "t+a.width,a.top+a.height,a.left)}\nfunction wc(a){var b=1,c=W(a,\"opaci",
-    "ty\");c&&(b=Number(c));(a=qc(a))&&(b*=wc(a));return b};function Ac(){th",
-    "is.N=ka.document.documentElement;var a=Wa(x(this.N));a&&Bc(this,a)}func",
-    "tion Bc(a,b){a.N=b;A(b,\"OPTION\")&&Va(b,function(a){return A(a,\"SELEC",
-    "T\")})}function Cc(a){var b=Va(a.N,function(a){return!!a&&A(a)&&kc(a)},",
-    "!0),b=b||a.N;a=Wa(x(b));if(b!=a){if(a&&ea(a.blur)&&!A(a,\"BODY\"))try{a",
-    ".blur()}catch(c){throw c;}ea(b.focus)&&b.focus()}};Ma();Ma();function D",
-    "c(a,b,c){this.B=a;this.pa=b;this.qa=c}Dc.prototype.create=function(a){a",
-    "=x(a).createEvent(\"HTMLEvents\");a.initEvent(this.B,this.pa,this.qa);r",
-    "eturn a};Dc.prototype.toString=function(){return this.B};var Ec=new Dc(",
-    "\"blur\",!1,!1),Fc=new Dc(\"change\",!0,!1);function Gc(a,b){b=b.create",
-    "(a,void 0);\"isTrusted\"in b||(b.isTrusted=!1);a.dispatchEvent(b)};func",
+    "ty\");c&&(b=Number(c));(a=qc(a))&&(b*=wc(a));return b};Ma();Ma();functi",
+    "on Ac(a,b,c){this.B=a;this.pa=b;this.qa=c}Ac.prototype.create=function(",
+    "a){a=x(a).createEvent(\"HTMLEvents\");a.initEvent(this.B,this.pa,this.q",
+    "a);return a};Ac.prototype.toString=function(){return this.B};var Bc=new",
+    " Ac(\"blur\",!1,!1),Cc=new Ac(\"change\",!0,!1);function Dc(a,b){b=b.cr",
+    "eate(a,void 0);\"isTrusted\"in b||(b.isTrusted=!1);a.dispatchEvent(b)};",
+    "function Ec(){this.N=ka.document.documentElement;var a=Wa(x(this.N));a&",
+    "&Fc(this,a)}function Fc(a,b){a.N=b;A(b,\"OPTION\")&&Va(b,function(a){re",
+    "turn A(a,\"SELECT\")})}function Gc(a){var b=Va(a.N,function(a){return!!",
+    "a&&A(a)&&kc(a)},!0),b=b||a.N;a=Wa(x(b));if(b!=a){if(a&&ea(a.blur)&&!A(a",
+    ",\"BODY\"))try{a.blur()}catch(c){throw c;}ea(b.focus)&&b.focus()}};func",
     "tion Hc(a,b){this.w={};this.g=[];this.G=0;var c=arguments.length;if(1<c",
     "){if(c%2)throw Error(\"Uneven number of arguments\");for(var d=0;d<c;d+",
     "=2)this.set(arguments[d],arguments[d+1])}else a&&this.addAll(a)}functio",
@@ -746,13 +746,13 @@
     "\\\\\",\"|\");Y(221,\"]\",\"}\");Y({c:59,b:186},\";\",\":\");Y(222,\"'",
     "\",'\"');var Qc=new Hc;Qc.set(1,Mc);Qc.set(2,Nc);Qc.set(4,Oc);Qc.set(8,",
     "Pc);(function(a){var b=new Hc;q(Ic(a),function(c){b.set(a.get(c).code,c",
-    ")});return b})(Qc);function Z(){Ac.call(this)}p(Z,Ac);Z.W=void 0;Z.ra=f",
-    "unction(){return Z.W?Z.W:Z.W=new Z};function Rc(a){var b=Z.ra();Bc(b,a)",
-    ";Cc(b)};ba(\"_\",function(a){if(!xc(a)||!nc(a)||\"none\"==W(a,\"pointer",
+    ")});return b})(Qc);function Z(){Ec.call(this)}p(Z,Ec);Z.W=void 0;Z.ra=f",
+    "unction(){return Z.W?Z.W:Z.W=new Z};function Rc(a){var b=Z.ra();Fc(b,a)",
+    ";Gc(b)};ba(\"_\",function(a){if(!xc(a)||!nc(a)||\"none\"==W(a,\"pointer",
     "-events\"))throw new u(12,\"Element is not currently interactable and m",
     "ay not be manipulated\");if(!lc(a))throw new u(12,\"Element must be use",
-    "r-editable in order to clear it.\");if(a.value){Rc(a);a.value=\"\";Gc(a",
-    ",Fc);Gc(a,Ec);var b=ka.document.body;if(b)Rc(b);else throw new u(13,\"C",
+    "r-editable in order to clear it.\");if(a.value){Rc(a);a.value=\"\";Dc(a",
+    ",Cc);Dc(a,Bc);var b=ka.document.body;if(b)Rc(b);else throw new u(13,\"C",
     "annot unfocus element after clearing.\");}else A(a,\"INPUT\")&&a.getAtt",
     "ribute(\"type\")&&\"number\"==a.getAttribute(\"type\").toLowerCase()&&(",
     "Rc(a),a.value=\"\");pc(a)&&(Rc(a),a.innerHTML=\n\" \")});; return this.",
@@ -1447,158 +1447,158 @@
     ",a.top),a.bottom),a.right=Math.min(Math.max(a.left+b.width,a.left),a.ri",
     "ght),a.bottom=Math.min(Math.max(a.top+b.height,a.top),a.bottom));return",
     " a}function Ic(a){var b=1,c=T(a,\"opacity\");c&&(b=Number(c));(a=Cc(a))",
-    "&&(b*=Ic(a));return b};function Lc(a,b){this.g=ka.document.documentElem",
-    "ent;this.G=null;var c=ab(A(this.g));c&&Mc(this,c);this.$=a||new Nc;this",
-    ".pa=b||new Oc}Lc.prototype.I=function(){return this.g};function Mc(a,b)",
-    "{a.g=b;a.G=B(b,\"OPTION\")?$a(b,function(a){return B(a,\"SELECT\")}):nu",
-    "ll}\nLc.prototype.W=function(a,b,c,d,e,f,h,l){if(!f&&!tc(this.g))return",
-    "!1;if(d&&Pc!=a&&Qc!=a)throw new u(12,\"Event type does not allow relate",
-    "d target: \"+a);b={clientX:b.x,clientY:b.y,button:c,altKey:0!=(this.$.c",
-    "a&4),ctrlKey:0!=(this.$.ca&2),shiftKey:0!=(this.$.ca&1),metaKey:0!=(thi",
-    "s.$.ca&8),wheelDelta:e||0,relatedTarget:d||null,count:l||1};h=h||1;c=th",
-    "is.g;a!=Rc&&a!=Sc&&h in Tc?c=Tc[h]:this.G&&(c=Uc(this,a));return c?this",
-    ".pa.W(c,a,b):!0};\nLc.prototype.S=function(a,b,c,d,e,f,h,l){if(!l&&!tc(",
-    "this.g))return!1;if(h&&Vc!=a&&Wc!=a)throw new u(12,\"Event type does no",
-    "t allow related target: \"+a);b={clientX:b.x,clientY:b.y,button:c,altKe",
-    "y:!1,ctrlKey:!1,shiftKey:!1,metaKey:!1,relatedTarget:h||null,width:0,he",
-    "ight:0,pressure:0,rotation:0,pointerId:d,tiltX:0,tiltY:0,pointerType:e,",
-    "isPrimary:f};c=this.G?Uc(this,a):this.g;Tc[d]&&(c=Tc[d]);d=Sa(A(this.g)",
-    ");if(d&&a==Xc){var q=d.Element.prototype.msSetPointerCapture;d.Element.",
-    "prototype.msSetPointerCapture=\nfunction(a){Tc[a]=this}}a=c?this.pa.S(c",
-    ",a,b):!0;q&&(d.Element.prototype.msSetPointerCapture=q);return a};funct",
-    "ion Uc(a,b){switch(b){case Rc:case Yc:return a.G.multiple?a.g:a.G;defau",
-    "lt:return a.G.multiple?a.g:null}}function Zc(a){var b=$a(a.g,function(a",
-    "){return!!a&&B(a)&&xc(a)},!0),b=b||a.g;a=ab(A(b));if(b!=a){if(a&&ea(a.b",
-    "lur)&&!B(a,\"BODY\"))try{a.blur()}catch(c){throw c;}ea(b.focus)&&b.focu",
-    "s()}}function Nc(){this.ca=0}var Tc={};function Oc(){}Oc.prototype.W=fu",
-    "nction(a,b,c){return $c(a,b,c)};\nOc.prototype.S=function(a,b,c){return",
-    " $c(a,b,c)};Na();Na();function ad(a,b,c){this.B=a;this.la=b;this.ma=c}a",
-    "d.prototype.create=function(a){a=A(a).createEvent(\"HTMLEvents\");a.ini",
-    "tEvent(this.B,this.la,this.ma);return a};ad.prototype.toString=function",
-    "(){return this.B};function W(a,b,c){ad.call(this,a,b,c)}p(W,ad);\nW.pro",
-    "totype.create=function(a,b){if(this==bd)throw new u(9,\"Browser does no",
-    "t support a mouse pixel scroll event.\");var c=A(a);a=Sa(c);c=c.createE",
-    "vent(\"MouseEvents\");this==cd&&(c.wheelDelta=b.wheelDelta);c.initMouse",
-    "Event(this.B,this.la,this.ma,a,1,b.clientX,b.clientY,b.clientX,b.client",
-    "Y,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget);ret",
-    "urn c};function X(a,b,c){ad.call(this,a,b,c)}p(X,ad);X.prototype.create",
-    "=function(){throw new u(9,\"Browser does not support MSPointer events.",
-    "\");};\nvar dd=new ad(\"change\",!0,!1),Rc=new W(\"click\",!0,!0),ed=ne",
-    "w W(\"contextmenu\",!0,!0),fd=new W(\"dblclick\",!0,!0),Sc=new W(\"mous",
-    "edown\",!0,!0),gd=new W(\"mousemove\",!0,!1),Qc=new W(\"mouseout\",!0,!",
-    "0),Pc=new W(\"mouseover\",!0,!0),Yc=new W(\"mouseup\",!0,!0),cd=new W(",
-    "\"mousewheel\",!0,!0),bd=new W(\"MozMousePixelScroll\",!0,!0),hd=new X(",
-    "\"MSGotPointerCapture\",!0,!1),id=new X(\"MSLostPointerCapture\",!0,!1)",
-    ",Xc=new X(\"MSPointerDown\",!0,!0),jd=new X(\"MSPointerMove\",!0,!0),Vc",
-    "=new X(\"MSPointerOver\",!0,!0),Wc=new X(\"MSPointerOut\",\n!0,!0),kd=n",
-    "ew X(\"MSPointerUp\",!0,!0);function $c(a,b,c){b=b.create(a,c);\"isTrus",
-    "ted\"in b||(b.isTrusted=!1);return a.dispatchEvent(b)};function ld(a,b)",
-    "{this.D={};this.h=[];this.M=0;var c=arguments.length;if(1<c){if(c%2)thr",
-    "ow Error(\"Uneven number of arguments\");for(var d=0;d<c;d+=2)this.set(",
-    "arguments[d],arguments[d+1])}else a&&this.addAll(a)}function md(a){nd(a",
-    ");return a.h.concat()}g=ld.prototype;g.clear=function(){this.D={};this.",
-    "M=this.h.length=0};g.remove=function(a){return Object.prototype.hasOwnP",
-    "roperty.call(this.D,a)?(delete this.D[a],this.M--,this.h.length>2*this.",
-    "M&&nd(this),!0):!1};\nfunction nd(a){var b,c;if(a.M!=a.h.length){for(b=",
-    "c=0;c<a.h.length;){var d=a.h[c];Object.prototype.hasOwnProperty.call(a.",
-    "D,d)&&(a.h[b++]=d);c++}a.h.length=b}if(a.M!=a.h.length){var e={};for(b=",
-    "c=0;c<a.h.length;)d=a.h[c],Object.prototype.hasOwnProperty.call(e,d)||(",
-    "a.h[b++]=d,e[d]=1),c++;a.h.length=b}}g.get=function(a,b){return Object.",
-    "prototype.hasOwnProperty.call(this.D,a)?this.D[a]:b};g.set=function(a,b",
-    "){Object.prototype.hasOwnProperty.call(this.D,a)||(this.M++,this.h.push",
-    "(a));this.D[a]=b};\ng.addAll=function(a){if(a instanceof ld){var b=md(a",
-    ");nd(a);for(var c=[],d=0;d<a.h.length;d++)c.push(a.D[a.h[d]]);a=c}else{",
-    "b=[];var d=0;for(e in a)b[d++]=e;d=[];var e=0;for(c in a)d[e++]=a[c];a=",
-    "d}for(c=0;c<b.length;c++)this.set(b[c],a[c])};g.forEach=function(a,b){f",
-    "or(var c=md(this),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b",
-    ",f,e,this)}};g.clone=function(){return new ld(this)};var od={};function",
-    " Y(a,b,c){fa(a)&&(a=a.b);a=new pd(a);!b||b in od&&!c||(od[b]={key:a,shi",
-    "ft:!1},c&&(od[c]={key:a,shift:!0}));return a}function pd(a){this.code=a",
-    "}Y(8);Y(9);Y(13);var qd=Y(16),rd=Y(17),sd=Y(18);Y(19);Y(20);Y(27);Y(32,",
-    "\" \");Y(33);Y(34);Y(35);Y(36);Y(37);Y(38);Y(39);Y(40);Y(44);Y(45);Y(46",
-    ");Y(48,\"0\",\")\");Y(49,\"1\",\"!\");Y(50,\"2\",\"@\");Y(51,\"3\",\"#",
-    "\");Y(52,\"4\",\"$\");Y(53,\"5\",\"%\");Y(54,\"6\",\"^\");Y(55,\"7\",\"",
-    "&\");Y(56,\"8\",\"*\");Y(57,\"9\",\"(\");Y(65,\"a\",\"A\");Y(66,\"b\",",
-    "\"B\");Y(67,\"c\",\"C\");Y(68,\"d\",\"D\");\nY(69,\"e\",\"E\");Y(70,\"f",
-    "\",\"F\");Y(71,\"g\",\"G\");Y(72,\"h\",\"H\");Y(73,\"i\",\"I\");Y(74,\"",
-    "j\",\"J\");Y(75,\"k\",\"K\");Y(76,\"l\",\"L\");Y(77,\"m\",\"M\");Y(78,",
-    "\"n\",\"N\");Y(79,\"o\",\"O\");Y(80,\"p\",\"P\");Y(81,\"q\",\"Q\");Y(82",
-    ",\"r\",\"R\");Y(83,\"s\",\"S\");Y(84,\"t\",\"T\");Y(85,\"u\",\"U\");Y(8",
-    "6,\"v\",\"V\");Y(87,\"w\",\"W\");Y(88,\"x\",\"X\");Y(89,\"y\",\"Y\");Y(",
-    "90,\"z\",\"Z\");var td=Y(Ja?{c:91,b:91}:Ia?{c:224,b:91}:{c:0,b:91});Y(J",
-    "a?{c:92,b:92}:Ia?{c:224,b:93}:{c:0,b:92});Y(Ja?{c:93,b:93}:Ia?{c:0,b:0}",
-    ":{c:93,b:null});Y({c:96,b:96},\"0\");Y({c:97,b:97},\"1\");\nY({c:98,b:9",
-    "8},\"2\");Y({c:99,b:99},\"3\");Y({c:100,b:100},\"4\");Y({c:101,b:101},",
-    "\"5\");Y({c:102,b:102},\"6\");Y({c:103,b:103},\"7\");Y({c:104,b:104},\"",
-    "8\");Y({c:105,b:105},\"9\");Y({c:106,b:106},\"*\");Y({c:107,b:107},\"+",
-    "\");Y({c:109,b:109},\"-\");Y({c:110,b:110},\".\");Y({c:111,b:111},\"/\"",
-    ");Y(144);Y(112);Y(113);Y(114);Y(115);Y(116);Y(117);Y(118);Y(119);Y(120)",
-    ";Y(121);Y(122);Y(123);Y({c:107,b:187},\"=\",\"+\");Y(108,\",\");Y({c:10",
-    "9,b:189},\"-\",\"_\");Y(188,\",\",\"<\");Y(190,\".\",\">\");Y(191,\"/\"",
-    ",\"?\");Y(192,\"`\",\"~\");Y(219,\"[\",\"{\");\nY(220,\"\\\\\",\"|\");Y",
-    "(221,\"]\",\"}\");Y({c:59,b:186},\";\",\":\");Y(222,\"'\",'\"');var ud=",
-    "new ld;ud.set(1,qd);ud.set(2,rd);ud.set(4,sd);ud.set(8,td);(function(a)",
-    "{var b=new ld;t(md(a),function(c){b.set(a.get(c).code,c)});return b})(u",
-    "d);function vd(a,b,c){Lc.call(this,b,c);this.K=this.j=null;this.C=new z",
-    "(0,0);this.Y=this.N=!1;if(a){n(a.buttonPressed)&&(this.j=a.buttonPresse",
-    "d);try{B(a.elementPressed)&&(this.K=a.elementPressed)}catch(d){this.j=n",
-    "ull}this.C=new z(a.clientXY.x,a.clientXY.y);this.N=!!a.nextClickIsDoubl",
-    "eClick;this.Y=!!a.hasEverInteracted;try{a.element&&B(a.element)&&Mc(thi",
-    "s,a.element)}catch(d){this.j=null}}}p(vd,Lc);var Z={};Z[Rc]=[0,1,2,null",
-    "];Z[ed]=[null,null,2,null];Z[Yc]=[0,1,2,null];Z[Qc]=[0,1,2,4];Z[gd]=[0,",
-    "1,2,4];\nQa&&(Z[Xc]=Z[Yc],Z[kd]=Z[Yc],Z[jd]=[-1,-1,-1,-1],Z[Wc]=Z[jd],Z",
-    "[Vc]=Z[jd]);Z[fd]=Z[Rc];Z[Sc]=Z[Yc];Z[Pc]=Z[Qc];var wd={};wd[Sc]=Xc;wd[",
-    "gd]=jd;wd[Qc]=Wc;wd[Pc]=Vc;wd[Yc]=kd;vd.prototype.move=function(a,b){va",
-    "r c=tc(a),d=Fc(a);this.C.x=b.x+d.left;this.C.y=b.y+d.top;b=this.I();if(",
-    "a!=b){try{Sa(A(b)).closed&&(b=null)}catch(e){b=null}b&&(d=b===ka.docume",
-    "nt.documentElement||b===ka.document.body,b=!this.Y&&d?null:b,xd(this,Qc",
-    ",a));Mc(this,a);xd(this,Pc,b,null,c)}xd(this,gd,null,null,c);this.N=!1}",
-    ";\nvd.prototype.scroll=function(a){if(0==a)throw new u(13,\"Must scroll",
-    " a non-zero number of ticks.\");for(var b=0<a?-120:120,c=0;c<Math.abs(a",
-    ");c++)xd(this,cd,null,b)};function xd(a,b,c,d,e,f){a.Y=!0;if(Qa){var h=",
-    "wd[b];if(h&&!a.S(h,a.C,yd(a,h),1,MSPointerEvent.MSPOINTER_TYPE_MOUSE,!0",
-    ",c,e))return!1}return a.W(b,a.C,yd(a,b),c,d,e,null,f)}function yd(a,b){",
-    "if(!(b in Z))return 0;a=Z[b][null===a.j?3:a.j];if(null===a)throw new u(",
-    "13,\"Event does not permit the specified mouse button.\");return a}\nvd",
-    ".prototype.getState=function(){return{buttonPressed:this.j,elementPress",
-    "ed:this.K,clientXY:{x:this.C.x,y:this.C.y},nextClickIsDoubleClick:this.",
-    "N,hasEverInteracted:this.Y,element:this.I()}};function zd(a,b){this.x=a",
-    ";this.y=b}p(zd,z);g=zd.prototype;g.clone=function(){return new zd(this.",
-    "x,this.y)};g.scale=z.prototype.scale;g.normalize=function(){return this",
-    ".scale(1/Math.sqrt(this.x*this.x+this.y*this.y))};g.add=function(a){thi",
-    "s.x+=a.x;this.y+=a.y;return this};g.rotate=function(a){var b=Math.cos(a",
-    ");a=Math.sin(a);var c=this.y*b+this.x*a;this.x=this.x*b-this.y*a;this.y",
-    "=c;return this};function Ad(a){var b;(b=qc(a,\"display\"))||(b=a.curren",
-    "tStyle?a.currentStyle.display:null);if(\"none\"!=(b||a.style&&a.style.d",
-    "isplay))b=rc(a);else{b=a.style;var c=b.display,d=b.visibility,e=b.posit",
-    "ion;b.visibility=\"hidden\";b.position=\"absolute\";b.display=\"inline",
-    "\";var f=rc(a);b.display=c;b.position=e;b.visibility=d;b=f}return 0<b.w",
-    "idth&&0<b.height||!a.offsetParent?b:Ad(a.offsetParent)};ba(\"_\",functi",
-    "on(a,b,c,d){if(!uc(a))throw new u(11,\"Element is not currently visible",
-    " and may not be manipulated\");b:{var e=b||void 0;if(\"scroll\"==Gc(a,e",
-    ")){if(a.scrollIntoView&&(a.scrollIntoView(),\"none\"==Gc(a,e)))break b;",
-    "for(var f=Jc(a,e),h=Cc(a);h;h=Cc(h)){var l=h,q=Fc(l);var y=l;var r=qc(y",
-    ",\"borderLeftWidth\");var v=qc(y,\"borderRightWidth\");var J=qc(y,\"bor",
-    "derTopWidth\");y=qc(y,\"borderBottomWidth\");v=new pc(parseFloat(J),par",
-    "seFloat(v),parseFloat(y),parseFloat(r));r=f.left-q.left-v.left;q=f.top-",
-    "q.top-\nv.top;v=l.clientHeight+f.top-f.bottom;l.scrollLeft+=Math.min(r,",
-    "Math.max(r-(l.clientWidth+f.left-f.right),0));l.scrollTop+=Math.min(q,M",
-    "ath.max(q-v,0))}Gc(a,e)}}b?b=new zd(b.x,b.y):(b=Ad(a),b=new zd(b.width/",
-    "2,b.height/2));c=c||new vd;c.move(a,b);if(null!==c.j)throw new u(13,\"C",
-    "annot press more than one button or an already pressed button.\");c.j=0",
-    ";c.K=c.I();if(B(c.I(),\"OPTION\")||B(c.I(),\"SELECT\")||xd(c,Sc,null,nu",
-    "ll,!1,void 0))Qa&&0==c.j&&B(c.K,\"OPTION\")&&c.S(hd,c.C,0,1,MSPointerEv",
-    "ent.MSPOINTER_TYPE_MOUSE,\n!0),Zc(c);if(null===c.j)throw new u(13,\"Can",
-    "not release a button when no button is pressed.\");c.G&&tc(c.g)&&(a=c.G",
-    ",b=gb(c.g),!b||a.multiple)&&(c.g.selected=!b,a.multiple&&!(0<=na(Pa,28)",
-    ")||$c(a,dd));a=tc(c.I());xd(c,Yc,null,null,d,void 0);try{if(0==c.j&&c.I",
-    "()==c.K){var U=c.C,ma=yd(c,Rc);if(a||tc(c.g))!c.G&&fb(c.g)&&gb(c.g),c.W",
-    "(Rc,U,ma,null,0,a,void 0);c.N&&xd(c,fd);c.N=!c.N;Qa&&0==c.j&&B(c.K,\"OP",
-    "TION\")&&c.S(id,new z(0,0),0,1,MSPointerEvent.MSPOINTER_TYPE_MOUSE,!1)}",
-    "else 2==c.j&&xd(c,ed)}catch(Bd){}Tc=\n{};c.j=null;c.K=null});; return t",
-    "his._.apply(null,arguments);}.apply({navigator:typeof window!='undefine",
-    "d'?window.navigator:null,document:typeof window!='undefined'?window.doc",
-    "ument:null}, arguments);}",
+    "&&(b*=Ic(a));return b};Na();Na();function Lc(a,b,c){this.B=a;this.la=b;",
+    "this.ma=c}Lc.prototype.create=function(a){a=A(a).createEvent(\"HTMLEven",
+    "ts\");a.initEvent(this.B,this.la,this.ma);return a};Lc.prototype.toStri",
+    "ng=function(){return this.B};function W(a,b,c){Lc.call(this,a,b,c)}p(W,",
+    "Lc);\nW.prototype.create=function(a,b){if(this==Mc)throw new u(9,\"Brow",
+    "ser does not support a mouse pixel scroll event.\");var c=A(a);a=Sa(c);",
+    "c=c.createEvent(\"MouseEvents\");this==Nc&&(c.wheelDelta=b.wheelDelta);",
+    "c.initMouseEvent(this.B,this.la,this.ma,a,1,b.clientX,b.clientY,b.clien",
+    "tX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.related",
+    "Target);return c};function X(a,b,c){Lc.call(this,a,b,c)}p(X,Lc);X.proto",
+    "type.create=function(){throw new u(9,\"Browser does not support MSPoint",
+    "er events.\");};\nvar Oc=new Lc(\"change\",!0,!1),Pc=new W(\"click\",!0",
+    ",!0),Qc=new W(\"contextmenu\",!0,!0),Rc=new W(\"dblclick\",!0,!0),Sc=ne",
+    "w W(\"mousedown\",!0,!0),Tc=new W(\"mousemove\",!0,!1),Uc=new W(\"mouse",
+    "out\",!0,!0),Vc=new W(\"mouseover\",!0,!0),Wc=new W(\"mouseup\",!0,!0),",
+    "Nc=new W(\"mousewheel\",!0,!0),Mc=new W(\"MozMousePixelScroll\",!0,!0),",
+    "Xc=new X(\"MSGotPointerCapture\",!0,!1),Yc=new X(\"MSLostPointerCapture",
+    "\",!0,!1),Zc=new X(\"MSPointerDown\",!0,!0),$c=new X(\"MSPointerMove\",",
+    "!0,!0),ad=new X(\"MSPointerOver\",!0,!0),bd=new X(\"MSPointerOut\",\n!0",
+    ",!0),cd=new X(\"MSPointerUp\",!0,!0);function dd(a,b,c){b=b.create(a,c)",
+    ";\"isTrusted\"in b||(b.isTrusted=!1);return a.dispatchEvent(b)};functio",
+    "n ed(a,b){this.g=ka.document.documentElement;this.G=null;var c=ab(A(thi",
+    "s.g));c&&fd(this,c);this.$=a||new gd;this.pa=b||new hd}ed.prototype.I=f",
+    "unction(){return this.g};function fd(a,b){a.g=b;a.G=B(b,\"OPTION\")?$a(",
+    "b,function(a){return B(a,\"SELECT\")}):null}\ned.prototype.W=function(a",
+    ",b,c,d,e,f,h,l){if(!f&&!tc(this.g))return!1;if(d&&Vc!=a&&Uc!=a)throw ne",
+    "w u(12,\"Event type does not allow related target: \"+a);b={clientX:b.x",
+    ",clientY:b.y,button:c,altKey:0!=(this.$.ca&4),ctrlKey:0!=(this.$.ca&2),",
+    "shiftKey:0!=(this.$.ca&1),metaKey:0!=(this.$.ca&8),wheelDelta:e||0,rela",
+    "tedTarget:d||null,count:l||1};h=h||1;c=this.g;a!=Pc&&a!=Sc&&h in id?c=i",
+    "d[h]:this.G&&(c=jd(this,a));return c?this.pa.W(c,a,b):!0};\ned.prototyp",
+    "e.S=function(a,b,c,d,e,f,h,l){if(!l&&!tc(this.g))return!1;if(h&&ad!=a&&",
+    "bd!=a)throw new u(12,\"Event type does not allow related target: \"+a);",
+    "b={clientX:b.x,clientY:b.y,button:c,altKey:!1,ctrlKey:!1,shiftKey:!1,me",
+    "taKey:!1,relatedTarget:h||null,width:0,height:0,pressure:0,rotation:0,p",
+    "ointerId:d,tiltX:0,tiltY:0,pointerType:e,isPrimary:f};c=this.G?jd(this,",
+    "a):this.g;id[d]&&(c=id[d]);d=Sa(A(this.g));if(d&&a==Zc){var q=d.Element",
+    ".prototype.msSetPointerCapture;d.Element.prototype.msSetPointerCapture=",
+    "\nfunction(a){id[a]=this}}a=c?this.pa.S(c,a,b):!0;q&&(d.Element.prototy",
+    "pe.msSetPointerCapture=q);return a};function jd(a,b){switch(b){case Pc:",
+    "case Wc:return a.G.multiple?a.g:a.G;default:return a.G.multiple?a.g:nul",
+    "l}}function kd(a){var b=$a(a.g,function(a){return!!a&&B(a)&&xc(a)},!0),",
+    "b=b||a.g;a=ab(A(b));if(b!=a){if(a&&ea(a.blur)&&!B(a,\"BODY\"))try{a.blu",
+    "r()}catch(c){throw c;}ea(b.focus)&&b.focus()}}function gd(){this.ca=0}v",
+    "ar id={};function hd(){}hd.prototype.W=function(a,b,c){return dd(a,b,c)",
+    "};\nhd.prototype.S=function(a,b,c){return dd(a,b,c)};function ld(a,b){t",
+    "his.D={};this.h=[];this.M=0;var c=arguments.length;if(1<c){if(c%2)throw",
+    " Error(\"Uneven number of arguments\");for(var d=0;d<c;d+=2)this.set(ar",
+    "guments[d],arguments[d+1])}else a&&this.addAll(a)}function md(a){nd(a);",
+    "return a.h.concat()}g=ld.prototype;g.clear=function(){this.D={};this.M=",
+    "this.h.length=0};g.remove=function(a){return Object.prototype.hasOwnPro",
+    "perty.call(this.D,a)?(delete this.D[a],this.M--,this.h.length>2*this.M&",
+    "&nd(this),!0):!1};\nfunction nd(a){var b,c;if(a.M!=a.h.length){for(b=c=",
+    "0;c<a.h.length;){var d=a.h[c];Object.prototype.hasOwnProperty.call(a.D,",
+    "d)&&(a.h[b++]=d);c++}a.h.length=b}if(a.M!=a.h.length){var e={};for(b=c=",
+    "0;c<a.h.length;)d=a.h[c],Object.prototype.hasOwnProperty.call(e,d)||(a.",
+    "h[b++]=d,e[d]=1),c++;a.h.length=b}}g.get=function(a,b){return Object.pr",
+    "ototype.hasOwnProperty.call(this.D,a)?this.D[a]:b};g.set=function(a,b){",
+    "Object.prototype.hasOwnProperty.call(this.D,a)||(this.M++,this.h.push(a",
+    "));this.D[a]=b};\ng.addAll=function(a){if(a instanceof ld){var b=md(a);",
+    "nd(a);for(var c=[],d=0;d<a.h.length;d++)c.push(a.D[a.h[d]]);a=c}else{b=",
+    "[];var d=0;for(e in a)b[d++]=e;d=[];var e=0;for(c in a)d[e++]=a[c];a=d}",
+    "for(c=0;c<b.length;c++)this.set(b[c],a[c])};g.forEach=function(a,b){for",
+    "(var c=md(this),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f",
+    ",e,this)}};g.clone=function(){return new ld(this)};var od={};function Y",
+    "(a,b,c){fa(a)&&(a=a.b);a=new pd(a);!b||b in od&&!c||(od[b]={key:a,shift",
+    ":!1},c&&(od[c]={key:a,shift:!0}));return a}function pd(a){this.code=a}Y",
+    "(8);Y(9);Y(13);var qd=Y(16),rd=Y(17),sd=Y(18);Y(19);Y(20);Y(27);Y(32,\"",
+    " \");Y(33);Y(34);Y(35);Y(36);Y(37);Y(38);Y(39);Y(40);Y(44);Y(45);Y(46);",
+    "Y(48,\"0\",\")\");Y(49,\"1\",\"!\");Y(50,\"2\",\"@\");Y(51,\"3\",\"#\")",
+    ";Y(52,\"4\",\"$\");Y(53,\"5\",\"%\");Y(54,\"6\",\"^\");Y(55,\"7\",\"&\"",
+    ");Y(56,\"8\",\"*\");Y(57,\"9\",\"(\");Y(65,\"a\",\"A\");Y(66,\"b\",\"B",
+    "\");Y(67,\"c\",\"C\");Y(68,\"d\",\"D\");\nY(69,\"e\",\"E\");Y(70,\"f\",",
+    "\"F\");Y(71,\"g\",\"G\");Y(72,\"h\",\"H\");Y(73,\"i\",\"I\");Y(74,\"j\"",
+    ",\"J\");Y(75,\"k\",\"K\");Y(76,\"l\",\"L\");Y(77,\"m\",\"M\");Y(78,\"n",
+    "\",\"N\");Y(79,\"o\",\"O\");Y(80,\"p\",\"P\");Y(81,\"q\",\"Q\");Y(82,\"",
+    "r\",\"R\");Y(83,\"s\",\"S\");Y(84,\"t\",\"T\");Y(85,\"u\",\"U\");Y(86,",
+    "\"v\",\"V\");Y(87,\"w\",\"W\");Y(88,\"x\",\"X\");Y(89,\"y\",\"Y\");Y(90",
+    ",\"z\",\"Z\");var td=Y(Ja?{c:91,b:91}:Ia?{c:224,b:91}:{c:0,b:91});Y(Ja?",
+    "{c:92,b:92}:Ia?{c:224,b:93}:{c:0,b:92});Y(Ja?{c:93,b:93}:Ia?{c:0,b:0}:{",
+    "c:93,b:null});Y({c:96,b:96},\"0\");Y({c:97,b:97},\"1\");\nY({c:98,b:98}",
+    ",\"2\");Y({c:99,b:99},\"3\");Y({c:100,b:100},\"4\");Y({c:101,b:101},\"5",
+    "\");Y({c:102,b:102},\"6\");Y({c:103,b:103},\"7\");Y({c:104,b:104},\"8\"",
+    ");Y({c:105,b:105},\"9\");Y({c:106,b:106},\"*\");Y({c:107,b:107},\"+\");",
+    "Y({c:109,b:109},\"-\");Y({c:110,b:110},\".\");Y({c:111,b:111},\"/\");Y(",
+    "144);Y(112);Y(113);Y(114);Y(115);Y(116);Y(117);Y(118);Y(119);Y(120);Y(1",
+    "21);Y(122);Y(123);Y({c:107,b:187},\"=\",\"+\");Y(108,\",\");Y({c:109,b:",
+    "189},\"-\",\"_\");Y(188,\",\",\"<\");Y(190,\".\",\">\");Y(191,\"/\",\"?",
+    "\");Y(192,\"`\",\"~\");Y(219,\"[\",\"{\");\nY(220,\"\\\\\",\"|\");Y(221",
+    ",\"]\",\"}\");Y({c:59,b:186},\";\",\":\");Y(222,\"'\",'\"');var ud=new ",
+    "ld;ud.set(1,qd);ud.set(2,rd);ud.set(4,sd);ud.set(8,td);(function(a){var",
+    " b=new ld;t(md(a),function(c){b.set(a.get(c).code,c)});return b})(ud);f",
+    "unction vd(a,b,c){ed.call(this,b,c);this.K=this.j=null;this.C=new z(0,0",
+    ");this.Y=this.N=!1;if(a){n(a.buttonPressed)&&(this.j=a.buttonPressed);t",
+    "ry{B(a.elementPressed)&&(this.K=a.elementPressed)}catch(d){this.j=null}",
+    "this.C=new z(a.clientXY.x,a.clientXY.y);this.N=!!a.nextClickIsDoubleCli",
+    "ck;this.Y=!!a.hasEverInteracted;try{a.element&&B(a.element)&&fd(this,a.",
+    "element)}catch(d){this.j=null}}}p(vd,ed);var Z={};Z[Pc]=[0,1,2,null];Z[",
+    "Qc]=[null,null,2,null];Z[Wc]=[0,1,2,null];Z[Uc]=[0,1,2,4];Z[Tc]=[0,1,2,",
+    "4];\nQa&&(Z[Zc]=Z[Wc],Z[cd]=Z[Wc],Z[$c]=[-1,-1,-1,-1],Z[bd]=Z[$c],Z[ad]",
+    "=Z[$c]);Z[Rc]=Z[Pc];Z[Sc]=Z[Wc];Z[Vc]=Z[Uc];var wd={};wd[Sc]=Zc;wd[Tc]=",
+    "$c;wd[Uc]=bd;wd[Vc]=ad;wd[Wc]=cd;vd.prototype.move=function(a,b){var c=",
+    "tc(a),d=Fc(a);this.C.x=b.x+d.left;this.C.y=b.y+d.top;b=this.I();if(a!=b",
+    "){try{Sa(A(b)).closed&&(b=null)}catch(e){b=null}b&&(d=b===ka.document.d",
+    "ocumentElement||b===ka.document.body,b=!this.Y&&d?null:b,xd(this,Uc,a))",
+    ";fd(this,a);xd(this,Vc,b,null,c)}xd(this,Tc,null,null,c);this.N=!1};\nv",
+    "d.prototype.scroll=function(a){if(0==a)throw new u(13,\"Must scroll a n",
+    "on-zero number of ticks.\");for(var b=0<a?-120:120,c=0;c<Math.abs(a);c+",
+    "+)xd(this,Nc,null,b)};function xd(a,b,c,d,e,f){a.Y=!0;if(Qa){var h=wd[b",
+    "];if(h&&!a.S(h,a.C,yd(a,h),1,MSPointerEvent.MSPOINTER_TYPE_MOUSE,!0,c,e",
+    "))return!1}return a.W(b,a.C,yd(a,b),c,d,e,null,f)}function yd(a,b){if(!",
+    "(b in Z))return 0;a=Z[b][null===a.j?3:a.j];if(null===a)throw new u(13,",
+    "\"Event does not permit the specified mouse button.\");return a}\nvd.pr",
+    "ototype.getState=function(){return{buttonPressed:this.j,elementPressed:",
+    "this.K,clientXY:{x:this.C.x,y:this.C.y},nextClickIsDoubleClick:this.N,h",
+    "asEverInteracted:this.Y,element:this.I()}};function zd(a,b){this.x=a;th",
+    "is.y=b}p(zd,z);g=zd.prototype;g.clone=function(){return new zd(this.x,t",
+    "his.y)};g.scale=z.prototype.scale;g.normalize=function(){return this.sc",
+    "ale(1/Math.sqrt(this.x*this.x+this.y*this.y))};g.add=function(a){this.x",
+    "+=a.x;this.y+=a.y;return this};g.rotate=function(a){var b=Math.cos(a);a",
+    "=Math.sin(a);var c=this.y*b+this.x*a;this.x=this.x*b-this.y*a;this.y=c;",
+    "return this};function Ad(a){var b;(b=qc(a,\"display\"))||(b=a.currentSt",
+    "yle?a.currentStyle.display:null);if(\"none\"!=(b||a.style&&a.style.disp",
+    "lay))b=rc(a);else{b=a.style;var c=b.display,d=b.visibility,e=b.position",
+    ";b.visibility=\"hidden\";b.position=\"absolute\";b.display=\"inline\";v",
+    "ar f=rc(a);b.display=c;b.position=e;b.visibility=d;b=f}return 0<b.width",
+    "&&0<b.height||!a.offsetParent?b:Ad(a.offsetParent)};ba(\"_\",function(a",
+    ",b,c,d){if(!uc(a))throw new u(11,\"Element is not currently visible and",
+    " may not be manipulated\");b:{var e=b||void 0;if(\"scroll\"==Gc(a,e)){i",
+    "f(a.scrollIntoView&&(a.scrollIntoView(),\"none\"==Gc(a,e)))break b;for(",
+    "var f=Jc(a,e),h=Cc(a);h;h=Cc(h)){var l=h,q=Fc(l);var y=l;var r=qc(y,\"b",
+    "orderLeftWidth\");var v=qc(y,\"borderRightWidth\");var J=qc(y,\"borderT",
+    "opWidth\");y=qc(y,\"borderBottomWidth\");v=new pc(parseFloat(J),parseFl",
+    "oat(v),parseFloat(y),parseFloat(r));r=f.left-q.left-v.left;q=f.top-q.to",
+    "p-\nv.top;v=l.clientHeight+f.top-f.bottom;l.scrollLeft+=Math.min(r,Math",
+    ".max(r-(l.clientWidth+f.left-f.right),0));l.scrollTop+=Math.min(q,Math.",
+    "max(q-v,0))}Gc(a,e)}}b?b=new zd(b.x,b.y):(b=Ad(a),b=new zd(b.width/2,b.",
+    "height/2));c=c||new vd;c.move(a,b);if(null!==c.j)throw new u(13,\"Canno",
+    "t press more than one button or an already pressed button.\");c.j=0;c.K",
+    "=c.I();if(B(c.I(),\"OPTION\")||B(c.I(),\"SELECT\")||xd(c,Sc,null,null,!",
+    "1,void 0))Qa&&0==c.j&&B(c.K,\"OPTION\")&&c.S(Xc,c.C,0,1,MSPointerEvent.",
+    "MSPOINTER_TYPE_MOUSE,\n!0),kd(c);if(null===c.j)throw new u(13,\"Cannot ",
+    "release a button when no button is pressed.\");c.G&&tc(c.g)&&(a=c.G,b=g",
+    "b(c.g),!b||a.multiple)&&(c.g.selected=!b,a.multiple&&!(0<=na(Pa,28))||d",
+    "d(a,Oc));a=tc(c.I());xd(c,Wc,null,null,d,void 0);try{if(0==c.j&&c.I()==",
+    "c.K){var U=c.C,ma=yd(c,Pc);if(a||tc(c.g))!c.G&&fb(c.g)&&gb(c.g),c.W(Pc,",
+    "U,ma,null,0,a,void 0);c.N&&xd(c,Rc);c.N=!c.N;Qa&&0==c.j&&B(c.K,\"OPTION",
+    "\")&&c.S(Yc,new z(0,0),0,1,MSPointerEvent.MSPOINTER_TYPE_MOUSE,!1)}else",
+    " 2==c.j&&xd(c,Qc)}catch(Bd){}id=\n{};c.j=null;c.K=null});; return this.",
+    "_.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?w",
+    "indow.navigator:null,document:typeof window!='undefined'?window.documen",
+    "t:null}, arguments);}",
     NULL
 };
 
@@ -2554,10 +2554,10 @@
     "xt:vc,\"partial link text\":vc,tagName:wc,\"tag name\":wc,xpath:U};ba(",
     "\"_\",function(a,b){a:{for(c in a)if(a.hasOwnProperty(c))break a;var c=",
     "null}if(c){var d=xc[c];if(d&&p(d.l))return d.l(a[c],b||ja.document)}thr",
-    "ow Error(\"Unsupported locator strategy: \"+c);});; return this._.apply",
-    "(null,arguments);}.apply({navigator:typeof window!='undefined'?window.n",
-    "avigator:null,document:typeof window!='undefined'?window.document:null}",
-    ", arguments);}",
+    "ow new t(61,\"Unsupported locator strategy: \"+c);});; return this._.ap",
+    "ply(null,arguments);}.apply({navigator:typeof window!='undefined'?windo",
+    "w.navigator:null,document:typeof window!='undefined'?window.document:nu",
+    "ll}, arguments);}",
     NULL
 };
 
@@ -4540,14 +4540,14 @@
     "t&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=V,c",
     ".evaluate=function(a,b,c,h){return(new zb(a,c)).evaluate(b,h)},c.create",
     "Expression=function(a,b){return new zb(a,b)},c.createNSResolver=functio",
-    "n(a){return new Ab(a)}});function Bb(){this.$=ga.document.documentEleme",
-    "nt;a:{var a=za(this.$);try{var b=a&&a.activeElement;break a}catch(c){}b",
-    "=null}b&&Cb(this,b)}function Cb(a,b){a.$=b;x(b,\"OPTION\")&&Ba(b,functi",
-    "on(a){return x(a,\"SELECT\")})}function Db(a){return x(a,\"FORM\")};ua(",
-    ");ua();function Eb(a,b,c){this.A=a;this.la=b;this.ma=c}Eb.prototype.cre",
-    "ate=function(a){a=za(a).createEvent(\"HTMLEvents\");a.initEvent(this.A,",
-    "this.la,this.ma);return a};Eb.prototype.toString=function(){return this",
-    ".A};var Gb=new Eb(\"submit\",!0,!0);function W(a,b){this.v={};this.g=[]",
+    "n(a){return new Ab(a)}});ua();ua();function Bb(a,b,c){this.A=a;this.la=",
+    "b;this.ma=c}Bb.prototype.create=function(a){a=za(a).createEvent(\"HTMLE",
+    "vents\");a.initEvent(this.A,this.la,this.ma);return a};Bb.prototype.toS",
+    "tring=function(){return this.A};var Cb=new Bb(\"submit\",!0,!0);functio",
+    "n Db(){this.$=ga.document.documentElement;a:{var a=za(this.$);try{var b",
+    "=a&&a.activeElement;break a}catch(c){}b=null}b&&Eb(this,b)}function Eb(",
+    "a,b){a.$=b;x(b,\"OPTION\")&&Ba(b,function(a){return x(a,\"SELECT\")})}f",
+    "unction Gb(a){return x(a,\"FORM\")};function W(a,b){this.v={};this.g=[]",
     ";this.G=0;var c=arguments.length;if(1<c){if(c%2)throw Error(\"Uneven nu",
     "mber of arguments\");for(var d=0;d<c;d+=2)this.set(arguments[d],argumen",
     "ts[d+1])}else a&&this.addAll(a)}function Hb(a){Ib(a);return a.g.concat(",
@@ -4594,11 +4594,11 @@
     "\");X(220,\"\\\\\",\"|\");X(221,\"]\",\"}\");X({c:59,b:186},\";\",\":\"",
     ");X(222,\"'\",'\"');var Y=new W;Y.set(1,Lb);Y.set(2,Mb);Y.set(4,Nb);Y.s",
     "et(8,Ob);(function(a){var b=new W;p(Hb(a),function(c){b.set(a.get(c).co",
-    "de,c)});return b})(Y);function Z(){Bb.call(this)}n(Z,Bb);Z.U=void 0;Z.n",
+    "de,c)});return b})(Y);function Z(){Db.call(this)}n(Z,Db);Z.U=void 0;Z.n",
     "a=function(){return Z.U?Z.U:Z.U=new Z};aa(\"_\",function(a){var b=Ba(a,",
-    "Db,!0);if(!b)throw new t(7,\"Element was not in a form, so could not su",
-    "bmit.\");var c=Z.na();Cb(c,a);if(!Db(b))throw new t(12,\"Element is not",
-    " a form, so could not submit.\");a=Gb.create(b,void 0);\"isTrusted\"in ",
+    "Gb,!0);if(!b)throw new t(7,\"Element was not in a form, so could not su",
+    "bmit.\");var c=Z.na();Eb(c,a);if(!Gb(b))throw new t(12,\"Element is not",
+    " a form, so could not submit.\");a=Cb.create(b,void 0);\"isTrusted\"in ",
     "a||(a.isTrusted=!1);b.dispatchEvent(a)&&(x(b.submit)?b.constructor.prot",
     "otype.submit.call(b):b.submit())});; return this._.apply(null,arguments",
     ");}.apply({navigator:typeof window!='undefined'?window.navigator:null,d",
@@ -5334,13 +5334,13 @@
     "tion yc(a){for(var b in a)if(a.hasOwnProperty(b))return b;return null};",
     "var zc=\"function\"===typeof ShadowRoot;ba(\"_\",function(a,b){var c;a:",
     "{if(c=yc(a)){var d=xc[c];if(d&&p(d.u)){c=d.u(a[c],b||ja.document);break",
-    " a}}throw Error(\"Unsupported locator strategy: \"+c);}if(c)return c;if",
-    "(zc&&b){for(c=b;c.parentNode;)c=c.parentNode;if(c instanceof ShadowRoot",
-    "){a:{if((c=yc(a))&&(d=xc[c])&&p(d.l)){a=d.l(a[c],b||ja.document);break ",
-    "a}throw Error(\"Unsupported locator strategy: \"+c);}if(c=a[0])return c",
-    "}}return null});; return this._.apply(null,arguments);}.apply({navigato",
-    "r:typeof window!='undefined'?window.navigator:null,document:typeof wind",
-    "ow!='undefined'?window.document:null}, arguments);}",
+    " a}}throw new v(61,\"Unsupported locator strategy: \"+c);}if(c)return c",
+    ";if(zc&&b){for(c=b;c.parentNode;)c=c.parentNode;if(c instanceof ShadowR",
+    "oot){a:{if((c=yc(a))&&(d=xc[c])&&p(d.l)){a=d.l(a[c],b||ja.document);bre",
+    "ak a}throw new v(61,\"Unsupported locator strategy: \"+c);}if(c=a[0])re",
+    "turn c}}return null});; return this._.apply(null,arguments);}.apply({na",
+    "vigator:typeof window!='undefined'?window.navigator:null,document:typeo",
+    "f window!='undefined'?window.document:null}, arguments);}",
     NULL
 };
 
@@ -7771,62 +7771,61 @@
 
 const char* const GET_ATTRIBUTE[] = {
     "function(){return function(){function d(a){return\"string\"==typeof a};",
-    "function g(a,b){this.code=a;this.state=h[a]||k;this.message=b||\"\";a=t",
-    "his.state.replace(/((?:^|\\s+)[a-z])/g,function(a){return a.toUpperCase",
-    "().replace(/^[\\s\\xa0]+/g,\"\")});b=a.length-5;if(0>b||a.indexOf(\"Err",
-    "or\",b)!=b)a+=\"Error\";this.name=a;a=Error(this.message);a.name=this.n",
-    "ame;this.stack=a.stack||\"\"}\n(function(){var a=Error;function b(){}b.",
-    "prototype=a.prototype;g.b=a.prototype;g.prototype=new b;g.prototype.con",
-    "structor=g;g.a=function(b,e,f){for(var c=Array(arguments.length-2),m=2;",
-    "m<arguments.length;m++)c[m-2]=arguments[m];return a.prototype[e].apply(",
-    "b,c)}})();var k=\"unknown error\",h={15:\"element not selectable\",11:",
-    "\"element not visible\"};h[31]=k;h[30]=k;h[24]=\"invalid cookie domain",
-    "\";h[29]=\"invalid element coordinates\";h[12]=\"invalid element state",
-    "\";h[32]=\"invalid selector\";h[51]=\"invalid selector\";\nh[52]=\"inva",
-    "lid selector\";h[17]=\"javascript error\";h[405]=\"unsupported operatio",
-    "n\";h[34]=\"move target out of bounds\";h[27]=\"no such alert\";h[7]=\"",
-    "no such element\";h[8]=\"no such frame\";h[23]=\"no such window\";h[28]",
-    "=\"script timeout\";h[33]=\"session not created\";h[10]=\"stale element",
-    " reference\";h[21]=\"timeout\";h[25]=\"unable to set cookie\";h[26]=\"u",
-    "nexpected alert open\";h[13]=k;h[9]=\"unknown command\";g.prototype.toS",
-    "tring=function(){return this.name+\": \"+this.message};function n(a,b){",
-    "for(var c=a.length,e=d(a)?a.split(\"\"):a,f=0;f<c;f++)f in e&&b.call(vo",
-    "id 0,e[f],f,a)};function p(a,b){b=b.toLowerCase();return\"style\"==b?q(",
-    "a.style.cssText):(a=a.getAttributeNode(b))&&a.specified?a.value:null}va",
-    "r r=/[;]+(?=(?:(?:[^\"]*\"){2})*[^\"]*$)(?=(?:(?:[^']*'){2})*[^']*$)(?=",
-    "(?:[^()]*\\([^()]*\\))*[^()]*$)/;function q(a){var b=[];n(a.split(r),fu",
-    "nction(a){var c=a.indexOf(\":\");0<c&&(a=[a.slice(0,c),a.slice(c+1)],2=",
-    "=a.length&&b.push(a[0].toLowerCase(),\":\",a[1],\";\"))});b=b.join(\"\"",
-    ");return b=\";\"==b.charAt(b.length-1)?b:b+\";\"}\nfunction t(a,b){b&&",
-    "\"string\"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||",
-    "a.tagName.toUpperCase()==b)}function u(a){return t(a,\"OPTION\")?!0:t(a",
-    ",\"INPUT\")?(a=a.type.toLowerCase(),\"checkbox\"==a||\"radio\"==a):!1};",
-    "var v={\"class\":\"className\",readonly:\"readOnly\"},w=\"allowfullscre",
-    "en allowpaymentrequest allowusermedia async autofocus autoplay checked ",
-    "compact complete controls declare default defaultchecked defaultselecte",
-    "d defer disabled ended formnovalidate hidden indeterminate iscontentedi",
-    "table ismap itemscope loop multiple muted nohref nomodule noresize nosh",
-    "ade novalidate nowrap open paused playsinline pubdate readonly required",
-    " reversed scoped seamless seeking selected truespeed typemustmatch will",
-    "validate\".split(\" \");function x(a,b){var c=b.toLowerCase();if(\"styl",
-    "e\"==c)return(b=a.style)&&!d(b)&&(b=b.cssText),b;if((\"selected\"==c||",
-    "\"checked\"==c)&&u(a)){if(!u(a))throw new g(15,\"Element is not selecta",
-    "ble\");b=\"selected\";var e=a.type&&a.type.toLowerCase();if(\"checkbox",
-    "\"==e||\"radio\"==e)b=\"checked\";return a[b]?\"true\":null}var f=t(a,",
-    "\"A\");if(t(a,\"IMG\")&&\"src\"==c||f&&\"href\"==c)return(b=p(a,c))&&(b",
-    "=a[c]),b;if(\"spellcheck\"==c){b=p(a,c);if(null!==b){if(\"false\"==b.to",
-    "LowerCase())return\"false\";if(\"true\"==b.toLowerCase())return\"true\"",
-    "}return a[c]+\n\"\"}f=v[b]||b;a:if(d(w))c=d(c)&&1==c.length?w.indexOf(c",
-    ",0):-1;else{for(var l=0;l<w.length;l++)if(l in w&&w[l]===c){c=l;break a",
-    "}c=-1}if(0<=c)return(b=null!==p(a,b)||a[f])?\"true\":null;try{e=a[f]}ca",
-    "tch(m){}(c=null==e)||(c=typeof e,c=\"object\"==c&&null!=e||\"function\"",
-    "==c);b=c?p(a,b):e;return null!=b?b.toString():null}var y=[\"_\"],z=this",
-    ";y[0]in z||!z.execScript||z.execScript(\"var \"+y[0]);\nfor(var A;y.len",
-    "gth&&(A=y.shift());){var B;if(B=!y.length)B=void 0!==x;B?z[A]=x:z=z[A]&",
-    "&z[A]!==Object.prototype[A]?z[A]:z[A]={}};; return this._.apply(null,ar",
-    "guments);}.apply({navigator:typeof window!='undefined'?window.navigator",
-    ":null,document:typeof window!='undefined'?window.document:null}, argume",
-    "nts);}",
+    "function g(a,b){for(var c=a.length,e=d(a)?a.split(\"\"):a,f=0;f<c;f++)f",
+    " in e&&b.call(void 0,e[f],f,a)};function h(a,b){this.code=a;this.state=",
+    "k[a]||n;this.message=b||\"\";a=this.state.replace(/((?:^|\\s+)[a-z])/g,",
+    "function(a){return a.toUpperCase().replace(/^[\\s\\xa0]+/g,\"\")});b=a.",
+    "length-5;if(0>b||a.indexOf(\"Error\",b)!=b)a+=\"Error\";this.name=a;a=E",
+    "rror(this.message);a.name=this.name;this.stack=a.stack||\"\"}\n(functio",
+    "n(){var a=Error;function b(){}b.prototype=a.prototype;h.b=a.prototype;h",
+    ".prototype=new b;h.prototype.constructor=h;h.a=function(b,e,f){for(var ",
+    "c=Array(arguments.length-2),m=2;m<arguments.length;m++)c[m-2]=arguments",
+    "[m];return a.prototype[e].apply(b,c)}})();var n=\"unknown error\",k={15",
+    ":\"element not selectable\",11:\"element not visible\"};k[31]=n;k[30]=n",
+    ";k[24]=\"invalid cookie domain\";k[29]=\"invalid element coordinates\";",
+    "k[12]=\"invalid element state\";k[32]=\"invalid selector\";k[51]=\"inva",
+    "lid selector\";\nk[52]=\"invalid selector\";k[17]=\"javascript error\";",
+    "k[405]=\"unsupported operation\";k[34]=\"move target out of bounds\";k[",
+    "27]=\"no such alert\";k[7]=\"no such element\";k[8]=\"no such frame\";k",
+    "[23]=\"no such window\";k[28]=\"script timeout\";k[33]=\"session not cr",
+    "eated\";k[10]=\"stale element reference\";k[21]=\"timeout\";k[25]=\"una",
+    "ble to set cookie\";k[26]=\"unexpected alert open\";k[13]=n;k[9]=\"unkn",
+    "own command\";h.prototype.toString=function(){return this.name+\": \"+t",
+    "his.message};function p(a,b){b=b.toLowerCase();return\"style\"==b?q(a.s",
+    "tyle.cssText):(a=a.getAttributeNode(b))&&a.specified?a.value:null}var r",
+    "=/[;]+(?=(?:(?:[^\"]*\"){2})*[^\"]*$)(?=(?:(?:[^']*'){2})*[^']*$)(?=(?:",
+    "[^()]*\\([^()]*\\))*[^()]*$)/;function q(a){var b=[];g(a.split(r),funct",
+    "ion(a){var c=a.indexOf(\":\");0<c&&(a=[a.slice(0,c),a.slice(c+1)],2==a.",
+    "length&&b.push(a[0].toLowerCase(),\":\",a[1],\";\"))});b=b.join(\"\");r",
+    "eturn b=\";\"==b.charAt(b.length-1)?b:b+\";\"}\nfunction t(a,b){b&&\"st",
+    "ring\"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.ta",
+    "gName.toUpperCase()==b)}function u(a){return t(a,\"OPTION\")?!0:t(a,\"I",
+    "NPUT\")?(a=a.type.toLowerCase(),\"checkbox\"==a||\"radio\"==a):!1};var ",
+    "v={\"class\":\"className\",readonly:\"readOnly\"},w=\"allowfullscreen a",
+    "llowpaymentrequest allowusermedia async autofocus autoplay checked comp",
+    "act complete controls declare default defaultchecked defaultselected de",
+    "fer disabled ended formnovalidate hidden indeterminate iscontenteditabl",
+    "e ismap itemscope loop multiple muted nohref nomodule noresize noshade ",
+    "novalidate nowrap open paused playsinline pubdate readonly required rev",
+    "ersed scoped seamless seeking selected truespeed typemustmatch willvali",
+    "date\".split(\" \");function x(a,b){var c=b.toLowerCase();if(\"style\"=",
+    "=c)return(b=a.style)&&!d(b)&&(b=b.cssText),b;if((\"selected\"==c||\"che",
+    "cked\"==c)&&u(a)){if(!u(a))throw new h(15,\"Element is not selectable\"",
+    ");b=\"selected\";var e=a.type&&a.type.toLowerCase();if(\"checkbox\"==e|",
+    "|\"radio\"==e)b=\"checked\";return a[b]?\"true\":null}var f=t(a,\"A\");",
+    "if(t(a,\"IMG\")&&\"src\"==c||f&&\"href\"==c)return(b=p(a,c))&&(b=a[c]),",
+    "b;if(\"spellcheck\"==c){b=p(a,c);if(null!==b){if(\"false\"==b.toLowerCa",
+    "se())return\"false\";if(\"true\"==b.toLowerCase())return\"true\"}return",
+    " a[c]+\n\"\"}f=v[b]||b;a:if(d(w))c=d(c)&&1==c.length?w.indexOf(c,0):-1;",
+    "else{for(var l=0;l<w.length;l++)if(l in w&&w[l]===c){c=l;break a}c=-1}i",
+    "f(0<=c)return(b=null!==p(a,b)||a[f])?\"true\":null;try{e=a[f]}catch(m){",
+    "}(c=null==e)||(c=typeof e,c=\"object\"==c&&null!=e||\"function\"==c);b=",
+    "c?p(a,b):e;return null!=b?b.toString():null}var y=[\"_\"],z=this;y[0]in",
+    " z||!z.execScript||z.execScript(\"var \"+y[0]);\nfor(var A;y.length&&(A",
+    "=y.shift());){var B;if(B=!y.length)B=void 0!==x;B?z[A]=x:z=z[A]&&z[A]!=",
+    "=Object.prototype[A]?z[A]:z[A]={}};; return this._.apply(null,arguments",
+    ");}.apply({navigator:typeof window!='undefined'?window.navigator:null,d",
+    "ocument:typeof window!='undefined'?window.document:null}, arguments);}",
     NULL
 };
 
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index 155fec1..6d0b79a 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -310,6 +310,7 @@
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/robolectric/robolectric_utils_java.jar" sourcepath="third_party/robolectric/robolectric/robolectric-utils/src/main/java"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/robolectric/shadows-core-3.2.jar" sourcepath="third_party/robolectric/robolectric/robolectric-shadows/shadows-core/src/main/java"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/robolectric/shadows-multidex-3.0.jar" sourcepath="third_party/robolectric/robolectric/robolectric-shadows/shadows-multidex/src/main/java"/>
+    <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_deps/com_android_support_support_compat_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_support_test_runner/runner-0.5-release-no-dep.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/android_support_test_runner/rules_java.jar"/>
     <classpathentry kind="lib" path="out/Debug/lib.java/third_party/blink/public/blink_headers_java.jar"/>
diff --git a/tools/gdb/util/class_methods.py b/tools/gdb/util/class_methods.py
index 2cb7e25..53bf5ef 100644
--- a/tools/gdb/util/class_methods.py
+++ b/tools/gdb/util/class_methods.py
@@ -88,7 +88,7 @@
       # Constructs a regular expression to match this type.
       self._class_regex = re.compile(
           '^' + re.escape(class_name) +
-          ('<.*>' if template_types > 0 else '') + '$')
+          ('<.*>' if len(template_types) > 0 else '') + '$')
 
       # Construct a dictionary and array of methods
       self.dict = {}
diff --git a/tools/generate_stubs/rules.gni b/tools/generate_stubs/rules.gni
deleted file mode 100644
index 548efd6..0000000
--- a/tools/generate_stubs/rules.gni
+++ /dev/null
@@ -1,88 +0,0 @@
-# Create a source_set with generated stubs for a POSIX shared library.
-#
-# Based on C-style signatures, it will generate a source and a header file
-# and expose it as a source_set.
-#
-# See //tools/generate_stubs/generate_stubs.py for more info.
-#
-# Variables
-#   sigs: list of files with C-style signatures (*.sig)
-#   output_name: name of the generated files: $output_name.h and $output_name.cc
-#   extra_header: prepend the contents of this file to the generated .cc file
-#   logging_function: override the used logging function (default: VLOG(1))
-#   logging_include: override the additional include (default: base/logging.h)
-#
-# Example
-#   generate_stubs("libfoo_stubs") {
-#     sigs = [ "foo/foo.sigs" ]
-#     extra_header = "foo/foo_stub_header.fragment"
-#     output_name = "foo/foo_stubs"
-#     deps = [
-#       "//base",
-#     ]
-#   }
-#
-# Targets that depend on this target can `#include "path/to/foo/foo_stubs.h"`
-template("generate_stubs") {
-  forward_variables_from(invoker, [ "testonly" ])
-
-  _gen_dir = get_path_info(invoker.output_name, "gen_dir")
-
-  action("${target_name}__stubs_gen") {
-    script = "//tools/generate_stubs/generate_stubs.py"
-    sources = invoker.sigs
-    inputs = [
-      invoker.extra_header,
-    ]
-    outputs = [
-      "${target_gen_dir}/${invoker.output_name}.cc",
-      "${target_gen_dir}/${invoker.output_name}.h",
-    ]
-    args = [
-      "--intermediate_dir",
-      rebase_path(_gen_dir, root_build_dir),
-      "--output",
-      rebase_path(_gen_dir, root_build_dir),
-      "--type",
-      "posix_stubs",
-      "--extra_stub_header",
-      rebase_path(invoker.extra_header, root_build_dir),
-      "--stubfile_name",
-      get_path_info(invoker.output_name, "name"),
-      "--path_from_source",
-      rebase_path(_gen_dir, root_gen_dir),
-    ]
-    if (defined(invoker.logging_function)) {
-      args += [
-        "--logging-function",
-        invoker.logging_function,
-      ]
-    }
-    if (defined(invoker.logging_include)) {
-      args += [
-        "--logging-include",
-        invoker.logging_include,
-      ]
-    }
-    args += rebase_path(invoker.sigs, root_build_dir)
-  }
-
-  source_set(target_name) {
-    forward_variables_from(invoker,
-                           [
-                             "deps",
-                             "public_deps",
-                             "visibility",
-                           ])
-    if (!defined(deps)) {
-      deps = []
-    }
-    deps += [ ":${target_name}__stubs_gen" ]
-    sources = [
-      "${target_gen_dir}/${invoker.output_name}.cc",
-      "${target_gen_dir}/${invoker.output_name}.h",
-    ]
-    libs = [ "dl" ]
-    include_dirs = [ target_gen_dir ]
-  }
-}
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 00d99e9..e4325da 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -2348,6 +2348,14 @@
   </description>
 </action>
 
+<action name="AutofillCreditCardsAdded">
+  <owner>nikunjb@chromium.org</owner>
+  <owner>sebsg@chromium.org</owner>
+  <description>
+    Recorded when user adds card via chrome://settings/payments page.
+  </description>
+</action>
+
 <action name="AutofillCreditCardsViewed">
   <owner>nikunjb@chromium.org</owner>
   <owner>sebsg@chromium.org</owner>
@@ -4765,6 +4773,23 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="DemoMode.ExitFromShelf">
+  <owner>michaelpg@chromium.org</owner>
+  <owner>wzang@chromium.org</owner>
+  <description>
+    Recorded when user exits Demo Mode from the logout button on the shelf.
+  </description>
+</action>
+
+<action name="DemoMode.ExitFromSystemTray">
+  <owner>michaelpg@chromium.org</owner>
+  <owner>wzang@chromium.org</owner>
+  <description>
+    Recorded when user exits Demo Mode from the logout button in the system
+    tray.
+  </description>
+</action>
+
 <action name="Desktop_SwitchTask">
   <owner>bruthig@google.com</owner>
   <owner>tdanderson@google.com</owner>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4ec96cd..7c641fd5 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -34218,6 +34218,18 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.WebRequest.TotalExtraHeadersRequestTime" units="ms"
+    expires_after="2019-05-27">
+  <owner>karandeepb@chromium.org</owner>
+  <owner>rdevlin.cronin@chromium.org</owner>
+  <summary>
+    The total time a network request took when at least one Web Request listener
+    with 'extraHeaders' in the extraInfoSpec was registered at the start of the
+    request. Measures from onBeforeRequest to onCompleted/onErrorOccurred, and
+    does not include canceled or redirected requests.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.WebRequest.TotalRequestTime" units="ms"
     expires_after="2019-05-13">
   <owner>karandeepb@chromium.org</owner>
@@ -71885,7 +71897,7 @@
 </histogram>
 
 <histogram name="Omnibox.DocumentSuggest.ResultCount" units="count"
-    expires_after="M72">
+    expires_after="M74">
   <owner>skare@chromium.org</owner>
   <summary>
     Number of results returned in each document suggestion reply. Logged for
@@ -99552,6 +99564,26 @@
 <histogram
     name="ServiceWorker.LoadTiming.MainFrame.MainResource.ResponseReceivedToCompleted"
     units="ms" expires_after="2021-10-31">
+  <obsolete>
+    Deprecated 2018-11 in favor of
+    ServiceWorker.LoadTiming.MainFrame.MainResource.ResponseReceivedToCompleted2.
+  </obsolete>
+  <owner>bashi@chromium.org</owner>
+  <owner>chrome-worker@google.com</owner>
+  <summary>
+    The time taken from (a) response headers from service worker are received,
+    to (b) reading response body is completed. Recorded when a fetch event
+    handler handled the request.
+
+    Recorded for each navigation request (including redirects) where there is a
+    fetch event handler and the fetch event was successfully dispatched to the
+    service worker.
+  </summary>
+</histogram>
+
+<histogram
+    name="ServiceWorker.LoadTiming.MainFrame.MainResource.ResponseReceivedToCompleted2"
+    units="ms" expires_after="2021-10-31">
   <owner>bashi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -99634,6 +99666,25 @@
 <histogram
     name="ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady"
     units="ms" expires_after="2021-10-31">
+  <obsolete>
+    Deprecated 2018-11 in favor of
+    ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady2.
+  </obsolete>
+  <owner>bashi@chromium.org</owner>
+  <owner>chrome-worker@google.com</owner>
+  <summary>
+    The time taken from (a) a subresource request is routed to the URLLoader (on
+    a background thread) for service worker controlled loads starts handling a
+    subresource request, to (b) a service worker is ready to handle the request.
+
+    Recorded for each subresource request where there is a fetch event handler
+    and the fetch event was successfully dispatched to the service worker.
+  </summary>
+</histogram>
+
+<histogram
+    name="ServiceWorker.LoadTiming.Subresource.ForwardServiceWorkerToWorkerReady2"
+    units="ms" expires_after="2021-10-31">
   <owner>bashi@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 825038e..5527f6c 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -5,7 +5,7 @@
 base_perftests,"skyostil@chromium.org, gab@chromium.org",Internals>SequenceManager,https://chromium.googlesource.com/chromium/src/+/HEAD/base/README.md#performance-testing,
 blink_perf.accessibility,dmazzoni@chromium.org,Blink>Accessibility,https://bit.ly/blink-perf-benchmarks,
 blink_perf.bindings,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",Blink>Bindings,https://bit.ly/blink-perf-benchmarks,
-blink_perf.canvas,fserb@chromium.org,Blink>Canvas,https://bit.ly/blink-perf-benchmarks,
+blink_perf.canvas,"aaronhk@chromium.org, fserb@chromium.org",Blink>Canvas,https://bit.ly/blink-perf-benchmarks,
 blink_perf.css,"futhark@chromium.org, andruud@chromium.org",Blink>CSS,https://bit.ly/blink-perf-benchmarks,
 blink_perf.dom,"hayato@chromium.org, tkent@chromium.org",Blink>DOM,https://bit.ly/blink-perf-benchmarks,
 blink_perf.events,hayato@chromium.org,Blink>DOM,https://bit.ly/blink-perf-benchmarks,
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index 75b64cf..8c52292 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -39,10 +39,18 @@
     action_runner.ExecuteJavaScript('testRunner.scheduleTestRun()')
     action_runner.WaitForJavaScriptCondition('testRunner.isDone', timeout=600)
 
+def StoryNameFromUrl(url, prefix):
+  filename = url[len(prefix):].strip('/')
+  baseName, extension = filename.split('.')
+  if extension.find('?') != -1:
+    query = extension.split('?')[1]
+    baseName += "_" + query # So that queried page-names don't collide
+  return "{b}.{e}".format(b=baseName, e=extension)
 
 def CreateStorySetFromPath(path, skipped_file,
                            shared_page_state_class=(
-                               shared_page_state.SharedPageState)):
+                               shared_page_state.SharedPageState),
+                           append_query=None):
   assert os.path.exists(path)
 
   page_urls = []
@@ -54,7 +62,11 @@
     if '../' in open(path, 'r').read():
       # If the page looks like it references its parent dir, include it.
       serving_dirs.add(os.path.dirname(os.path.dirname(path)))
-    page_urls.append('file://' + path.replace('\\', '/'))
+    page_url = 'file://' + path.replace('\\', '/')
+    if append_query:
+      page_url += '?' + append_query
+    page_urls.append(page_url)
+
 
   def _AddDir(dir_path, skipped):
     for candidate_path in os.listdir(dir_path):
@@ -85,7 +97,7 @@
   all_urls = [p.rstrip('/') for p in page_urls]
   common_prefix = os.path.dirname(os.path.commonprefix(all_urls))
   for url in sorted(page_urls):
-    name = url[len(common_prefix):].strip('/')
+    name = StoryNameFromUrl(url, common_prefix)
     ps.AddStory(_BlinkPerfPage(
         url, ps, ps.base_dir,
         shared_page_state_class=shared_page_state_class,
@@ -349,7 +361,7 @@
   test = _BlinkPerfMeasurement
 
   def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, self.subdir)
+    path = os.path.join(BLINK_PERF_BASE_DIR, self.SUBDIR)
     return CreateStorySetFromPath(path, SKIPPED_FILE)
 
 
@@ -357,8 +369,7 @@
                 component='Blink>Accessibility',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfAccessibility(_BlinkPerfBenchmark):
-  tag = 'accessibility'
-  subdir = 'accessibility'
+  SUBDIR = 'accessibility'
 
   @classmethod
   def Name(cls):
@@ -376,7 +387,7 @@
             'haraken@chromium.org'],
     documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfBindings(_BlinkPerfBenchmark):
-  subdir = 'bindings'
+  SUBDIR = 'bindings'
 
   @classmethod
   def Name(cls):
@@ -387,43 +398,47 @@
                 documentation_url='https://bit.ly/blink-perf-benchmarks',
                 component='Blink>CSS')
 class BlinkPerfCSS(_BlinkPerfBenchmark):
-  subdir = 'css'
+  SUBDIR = 'css'
 
   @classmethod
   def Name(cls):
     return 'blink_perf.css'
 
-
-
-@benchmark.Info(emails=['fserb@chromium.org'],
+@benchmark.Info(emails=['aaronhk@chromium.org', 'fserb@chromium.org'],
                 documentation_url='https://bit.ly/blink-perf-benchmarks',
                 component='Blink>Canvas')
 class BlinkPerfCanvas(_BlinkPerfBenchmark):
-  subdir = 'canvas'
+  SUBDIR = 'canvas'
 
   @classmethod
   def Name(cls):
     return 'blink_perf.canvas'
 
   def CreateStorySet(self, options):
-    path = os.path.join(BLINK_PERF_BASE_DIR, self.subdir)
+    path = os.path.join(BLINK_PERF_BASE_DIR, self.SUBDIR)
     story_set = CreateStorySetFromPath(
         path, SKIPPED_FILE,
         shared_page_state_class=(
             webgl_supported_shared_state.WebGLSupportedSharedState))
+    raf_story_set = CreateStorySetFromPath(
+        path, SKIPPED_FILE,
+        shared_page_state_class=(
+            webgl_supported_shared_state.WebGLSupportedSharedState),
+        append_query="RAF")
+    for raf_story in raf_story_set:
+      story_set.AddStory(raf_story)
     # WebGLSupportedSharedState requires the skipped_gpus property to
     # be set on each page.
     for page in story_set:
       page.skipped_gpus = []
     return story_set
 
-
 @benchmark.Info(emails=['hayato@chromium.org',
                         'tkent@chromium.org'],
                 component='Blink>DOM',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfDOM(_BlinkPerfBenchmark):
-  subdir = 'dom'
+  SUBDIR = 'dom'
 
   @classmethod
   def Name(cls):
@@ -434,7 +449,7 @@
                 component='Blink>DOM',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfEvents(_BlinkPerfBenchmark):
-  subdir = 'events'
+  SUBDIR = 'events'
 
   @classmethod
   def Name(cls):
@@ -445,8 +460,7 @@
                 component='Internals>Images>Codecs',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfImageDecoder(_BlinkPerfBenchmark):
-  tag = 'image_decoder'
-  subdir = 'image_decoder'
+  SUBDIR = 'image_decoder'
 
   @classmethod
   def Name(cls):
@@ -461,7 +475,7 @@
 @benchmark.Info(emails=['eae@chromium.org'],
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfLayout(_BlinkPerfBenchmark):
-  subdir = 'layout'
+  SUBDIR = 'layout'
 
   @classmethod
   def Name(cls):
@@ -471,7 +485,7 @@
 @benchmark.Info(emails=['dmurph@chromium.org'],
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfOWPStorage(_BlinkPerfBenchmark):
-  subdir = 'owp_storage'
+  SUBDIR = 'owp_storage'
 
   @classmethod
   def Name(cls):
@@ -491,7 +505,7 @@
                 component='Blink>Paint',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfPaint(_BlinkPerfBenchmark):
-  subdir = 'paint'
+  SUBDIR = 'paint'
 
   @classmethod
   def Name(cls):
@@ -504,7 +518,7 @@
                          'haraken@chromium.org'],
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfParser(_BlinkPerfBenchmark):
-  subdir = 'parser'
+  SUBDIR = 'parser'
 
   @classmethod
   def Name(cls):
@@ -515,7 +529,7 @@
                 component='Blink>SVG',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfSVG(_BlinkPerfBenchmark):
-  subdir = 'svg'
+  SUBDIR = 'svg'
 
   @classmethod
   def Name(cls):
@@ -526,7 +540,7 @@
                 component='Blink>DOM>ShadowDOM',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfShadowDOM(_BlinkPerfBenchmark):
-  subdir = 'shadow_dom'
+  SUBDIR = 'shadow_dom'
 
   @classmethod
   def Name(cls):
diff --git a/tools/perf/contrib/blink_layoutng_perf/blink_layoutng_perf.py b/tools/perf/contrib/blink_layoutng_perf/blink_layoutng_perf.py
index 87d1344..f916abf 100644
--- a/tools/perf/contrib/blink_layoutng_perf/blink_layoutng_perf.py
+++ b/tools/perf/contrib/blink_layoutng_perf/blink_layoutng_perf.py
@@ -8,7 +8,7 @@
 @benchmark.Info(emails=['cbiesinger@chromium.org'],
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfLayoutNg(blink_perf._BlinkPerfBenchmark):
-  subdir = 'layout'
+  SUBDIR = 'layout'
 
   def SetExtraBrowserOptions(self, options):
     super(BlinkPerfLayoutNg, self).SetExtraBrowserOptions(options)
@@ -22,7 +22,7 @@
 @benchmark.Info(emails=['cbiesinger@chromium.org'],
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfParserLayoutNg(blink_perf._BlinkPerfBenchmark):
-  subdir = 'parser'
+  SUBDIR = 'parser'
 
   def SetExtraBrowserOptions(self, options):
     super(BlinkPerfParserLayoutNg, self).SetExtraBrowserOptions(options)
@@ -36,7 +36,7 @@
 @benchmark.Info(emails=['cbiesinger@chromium.org'],
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfPaintLayoutNg(blink_perf._BlinkPerfBenchmark):
-  subdir = 'paint'
+  SUBDIR = 'paint'
 
   def SetExtraBrowserOptions(self, options):
     super(BlinkPerfPaintLayoutNg, self).SetExtraBrowserOptions(options)
diff --git a/tools/perf/contrib/blink_perf_cmdline/blink_perf_cmdline.py b/tools/perf/contrib/blink_perf_cmdline/blink_perf_cmdline.py
index d146e162..929321b 100644
--- a/tools/perf/contrib/blink_perf_cmdline/blink_perf_cmdline.py
+++ b/tools/perf/contrib/blink_perf_cmdline/blink_perf_cmdline.py
@@ -19,8 +19,8 @@
                       default=blink_perf.BLINK_PERF_BASE_DIR,
                       help=('Path to blink perf tests. Could be an absolute '
                             'path, a relative path with respect to your '
-                            'current directory, or a relative path with '
-                            'respect to third_party/WebKit/PerformanceTest/)'))
+                            'current directory or a relative path with '
+                            'respect to third_party/blink/perf_tests)'))
 
   def CreateStorySet(self, options):
     if os.path.exists(options.test_path):
diff --git a/tools/perf/contrib/blink_perf_xml_http_request/blink_perf_xml_http_request.py b/tools/perf/contrib/blink_perf_xml_http_request/blink_perf_xml_http_request.py
index 1d0a8453..8da0de9 100644
--- a/tools/perf/contrib/blink_perf_xml_http_request/blink_perf_xml_http_request.py
+++ b/tools/perf/contrib/blink_perf_xml_http_request/blink_perf_xml_http_request.py
@@ -6,5 +6,4 @@
 
 # pylint: disable=protected-access
 class BlinkPerfXMLHttpRequest(blink_perf._BlinkPerfBenchmark):
-  tag = 'xml_http_request'
-  subdir = 'xml_http_request'
+  SUBDIR = 'xml_http_request'
diff --git a/tools/perf/generate_perf_sharding b/tools/perf/generate_perf_sharding
index 898ca08..8414c55 100755
--- a/tools/perf/generate_perf_sharding
+++ b/tools/perf/generate_perf_sharding
@@ -7,16 +7,34 @@
 import json
 import multiprocessing
 import sys
+import textwrap
 
 from core import benchmark_utils
 from core import bot_platforms
 from core import retrieve_story_timing
 from core import sharding_map_generator
 
+_SCRIPT_USAGE = """
+Generate sharding maps for Telemetry benchmarks.
+
+Every performance benchmark should be run on a same machine as long as possible
+to preserve high fidelity of data monitoring. Hence in order to shard the
+Telemetry benchmarks on multiple machines, we generate a JSON map that
+specifies how benchmarks should be distributed on machines. There is one
+sharding JSON map for every builder in the perf & perf.fyi waterfalls which are
+specified by PerfPlatform classes in //tools/perf/core/bot_platforms.py.
+
+Generating these JSON maps depends on how many Telemetry benchmarks
+actually exist at the time. Because of this, CLs to generate the JSON maps
+should never be automatically reverted, since the reverted state of the JSON map
+files may not match with the true state of world.
+
+"""
+
 
 def GetParser():
   parser = argparse.ArgumentParser(
-      description='Generate perf test sharding map.')
+      description=_SCRIPT_USAGE, formatter_class=argparse.RawTextHelpFormatter)
   subparsers = parser.add_subparsers()
 
   parser_update = subparsers.add_parser('update')
@@ -35,7 +53,7 @@
             'specified, use all perf builders by default'))
   parser.add_argument(
       '--debug', action='store_true',
-      help=('Whether to include detailed debug info of the sharding map in the'
+      help=('Whether to include detailed debug info of the sharding map in the '
             'shard maps.'), default=False)
 
   parser_update.set_defaults(func=_UpdateShardsForBuilders)
@@ -117,16 +135,38 @@
     json.dump(sharding_map, output_file, indent=4, separators=(',', ': '))
 
 
+def _PromptWarning():
+  message = ('This will regenerate the sharding maps for all perf benchmarks. '
+             'Note that this will shuffle all the benchmarks on the shards, '
+             'which can cause false regressions. In general this operation '
+             'should only be done when the shards are too unbalanced or when '
+             'benchmarks are added/removed. '
+             'In addition, this a tricky operation and should '
+             'only be done by Telemetry or Chrome Client Infrastructure '
+             'team members. Upon landing the CL to update the shards maps, '
+             'please notify Chromium perf sheriffs in '
+             'perf-sheriffs@chromium.org and put a warning about expected '
+             'false regressions in your CL '
+             'description')
+  print textwrap.fill(message, 70), '\n'
+  answer = raw_input("Enter 'y' to continue: ")
+  if answer != 'y':
+    print 'Abort updating shard maps for benchmarks on perf waterfall'
+    sys.exit(0)
+
+
 def _UpdateShardsForBuilders(args):
   if args.builders:
     builders = {b for b in bot_platforms.ALL_PLATFORMS if b.name in
                 args.builders}
   elif args.waterfall == 'perf':
     builders = bot_platforms.ALL_PERF_PLATFORMS
+    _PromptWarning()
   elif args.waterfall == 'perf-fyi':
     builders = bot_platforms.ALL_PERF_FYI_PLATFORMS
   else:
     builders = bot_platforms.ALL_PLATFORMS
+    _PromptWarning()
 
   if args.regenerate_timing_data:
     print 'Update shards timing data. May take a while...'
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index a1a8b75..dff0137 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -286,6 +286,6 @@
  <item id="webstore_install_helper" hash_code="25921771" type="0" content_hash_code="10206361" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_install_helper.cc"/>
  <item id="webstore_installer" hash_code="18764319" type="0" content_hash_code="11030110" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_installer.cc"/>
  <item id="webui_content_scripts_download" hash_code="100545943" type="0" content_hash_code="119898059" os_list="linux,windows" file_path="extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc"/>
- <item id="worker_script_load" hash_code="72087791" type="0" content_hash_code="24889169" os_list="linux,windows" file_path="content/browser/shared_worker/worker_script_fetcher.cc"/>
+ <item id="worker_script_load" hash_code="72087791" type="0" content_hash_code="24889169" os_list="linux,windows" file_path="content/browser/worker_host/worker_script_fetcher.cc"/>
  <item id="xmpp_signal_strategy" hash_code="88906454" type="0" content_hash_code="88958321" os_list="linux,windows" file_path="remoting/signaling/xmpp_signal_strategy.cc"/>
 </annotations>
diff --git a/ui/aura/window_tree_host_platform.cc b/ui/aura/window_tree_host_platform.cc
index fb11cb1..a116ba0 100644
--- a/ui/aura/window_tree_host_platform.cc
+++ b/ui/aura/window_tree_host_platform.cc
@@ -145,7 +145,7 @@
   // problems with event routing (i.e. which Hook takes precedence) and
   // destruction ordering.
   DCHECK(!keyboard_hook_);
-  keyboard_hook_ = ui::KeyboardHook::Create(
+  keyboard_hook_ = ui::KeyboardHook::CreateModifierKeyboardHook(
       std::move(dom_codes), GetAcceleratedWidget(),
       base::BindRepeating(
           [](ui::PlatformWindowDelegate* delegate, ui::KeyEvent* event) {
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index 27aee57..278550b 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -211,8 +211,10 @@
     "system_input_injector.cc",
     "win/events_win.cc",
     "win/events_win_utils.cc",
-    "win/keyboard_hook_win.cc",
-    "win/keyboard_hook_win.h",
+    "win/keyboard_hook_win_base.cc",
+    "win/keyboard_hook_win_base.h",
+    "win/media_keyboard_hook_win.cc",
+    "win/modifier_keyboard_hook_win.cc",
     "win/system_event_state_lookup.cc",
   ]
 
@@ -509,7 +511,8 @@
       "platform/platform_event_source_unittest.cc",
       "scoped_target_handler_unittest.cc",
       "win/event_utils_win_unittest.cc",
-      "win/keyboard_hook_win_unittest.cc",
+      "win/media_keyboard_hook_win_unittest.cc",
+      "win/modifier_keyboard_hook_win_unittest.cc",
     ]
 
     deps = [
@@ -631,3 +634,21 @@
     classes = [ "android/view/KeyEvent.class" ]
   }
 }
+
+# This target is added as a dependency of browser interactive_ui_tests. It must
+# be source_set, otherwise the linker will drop the tests as dead code.
+source_set("events_interactive_ui_tests") {
+  testonly = true
+  if (is_win) {
+    sources = [
+      "win/media_keyboard_hook_win_interactive_test.cc",
+    ]
+
+    deps = [
+      ":events",
+      ":test_support",
+      "//base/test:test_support",
+      "//testing/gtest",
+    ]
+  }
+}
diff --git a/ui/events/android/keyboard_hook_android.cc b/ui/events/android/keyboard_hook_android.cc
index be373af..31937535 100644
--- a/ui/events/android/keyboard_hook_android.cc
+++ b/ui/events/android/keyboard_hook_android.cc
@@ -14,11 +14,17 @@
 namespace ui {
 
 // static
-std::unique_ptr<KeyboardHook> KeyboardHook::Create(
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
     base::Optional<base::flat_set<DomCode>> dom_codes,
     gfx::AcceleratedWidget accelerated_widget,
     KeyboardHook::KeyEventCallback callback) {
   return nullptr;
 }
 
+// static
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
+    KeyboardHook::KeyEventCallback callback) {
+  return nullptr;
+}
+
 }  // namespace ui
diff --git a/ui/events/keyboard_hook.h b/ui/events/keyboard_hook.h
index 36e9b88..ca88fec 100644
--- a/ui/events/keyboard_hook.h
+++ b/ui/events/keyboard_hook.h
@@ -32,11 +32,19 @@
   // |callback| is called for each key which is intercepted.
   // Returns a valid instance if the hook was created and successfully
   // registered otherwise nullptr.
-  static std::unique_ptr<KeyboardHook> Create(
+  static std::unique_ptr<KeyboardHook> CreateModifierKeyboardHook(
       base::Optional<base::flat_set<DomCode>> dom_codes,
       gfx::AcceleratedWidget accelerated_widget,
       KeyEventCallback callback);
 
+  // Creates a platform-specific KeyboardHook implementation that captures the
+  // play/pause, stop, and next/previous track media keys.
+  // |callback| is called for each key which is intercepted.
+  // Returns a valid instance if the hook was created and successfully
+  // registered otherwise nullptr.
+  static std::unique_ptr<KeyboardHook> CreateMediaKeyboardHook(
+      KeyEventCallback callback);
+
   // True if |dom_code| is reserved for an active KeyboardLock request.
   virtual bool IsKeyLocked(DomCode dom_code) const = 0;
 };
diff --git a/ui/events/keyboard_hook_base.cc b/ui/events/keyboard_hook_base.cc
index 56381738..327c2d4 100644
--- a/ui/events/keyboard_hook_base.cc
+++ b/ui/events/keyboard_hook_base.cc
@@ -34,9 +34,8 @@
   return !dom_codes_ || base::ContainsKey(dom_codes_.value(), dom_code);
 }
 
-void KeyboardHookBase::ForwardCapturedKeyEvent(
-    std::unique_ptr<KeyEvent> event) {
-  key_event_callback_.Run(event.get());
+void KeyboardHookBase::ForwardCapturedKeyEvent(KeyEvent* event) {
+  key_event_callback_.Run(event);
 }
 
 }  // namespace ui
diff --git a/ui/events/keyboard_hook_base.h b/ui/events/keyboard_hook_base.h
index 81ffae2..2974081 100644
--- a/ui/events/keyboard_hook_base.h
+++ b/ui/events/keyboard_hook_base.h
@@ -29,7 +29,9 @@
   bool ShouldCaptureKeyEvent(DomCode dom_code) const;
 
   // Forwards the key event using |key_event_callback_|.
-  void ForwardCapturedKeyEvent(std::unique_ptr<KeyEvent> event);
+  // |event| is owned by the calling method and will live until this method
+  // returns.
+  void ForwardCapturedKeyEvent(KeyEvent* event);
 
   const base::Optional<base::flat_set<DomCode>>& dom_codes() {
     return dom_codes_;
diff --git a/ui/events/mac/keyboard_hook_mac.mm b/ui/events/mac/keyboard_hook_mac.mm
index e032b63..1da9d4ce 100644
--- a/ui/events/mac/keyboard_hook_mac.mm
+++ b/ui/events/mac/keyboard_hook_mac.mm
@@ -9,11 +9,17 @@
 namespace ui {
 
 // static
-std::unique_ptr<KeyboardHook> KeyboardHook::Create(
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
     base::Optional<base::flat_set<DomCode>> dom_codes,
     gfx::AcceleratedWidget accelerated_widget,
     KeyEventCallback callback) {
   return nullptr;
 }
 
+// static
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
+    KeyEventCallback callback) {
+  return nullptr;
+}
+
 }  // namespace ui
diff --git a/ui/events/ozone/keyboard_hook_ozone.cc b/ui/events/ozone/keyboard_hook_ozone.cc
index 04a190a3..991b7d720 100644
--- a/ui/events/ozone/keyboard_hook_ozone.cc
+++ b/ui/events/ozone/keyboard_hook_ozone.cc
@@ -46,7 +46,7 @@
 }  // namespace
 
 // static
-std::unique_ptr<KeyboardHook> KeyboardHook::Create(
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
     base::Optional<base::flat_set<DomCode>> dom_codes,
     gfx::AcceleratedWidget accelerated_widget,
     KeyEventCallback callback) {
@@ -60,4 +60,10 @@
   return keyboard_hook;
 }
 
+// static
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
+    KeyEventCallback callback) {
+  return nullptr;
+}
+
 }  // namespace ui
diff --git a/ui/events/win/keyboard_hook_win.h b/ui/events/win/keyboard_hook_win.h
deleted file mode 100644
index 627ba313..0000000
--- a/ui/events/win/keyboard_hook_win.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_
-#define UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_
-
-#include <memory>
-
-#include <windows.h>
-
-#include "base/containers/flat_set.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "ui/events/event.h"
-#include "ui/events/events_export.h"
-#include "ui/events/keyboard_hook_base.h"
-#include "ui/events/keycodes/dom/dom_code.h"
-
-namespace ui {
-
-// Exposes a method to drive the Windows KeyboardHook implementation by feeding
-// it key event data.  This method is used by both the low-level keyboard hook
-// and by unit tests which simulate the hooked behavior w/o actually installing
-// a hook (doing so would cause problems with test parallelization).
-class EVENTS_EXPORT KeyboardHookWin : public KeyboardHookBase {
- public:
-  KeyboardHookWin(base::Optional<base::flat_set<DomCode>> dom_codes,
-                  KeyEventCallback callback);
-  ~KeyboardHookWin() override;
-
-  // Create a KeyboardHookWin instance which does not register a low-level hook.
-  static std::unique_ptr<KeyboardHookWin> CreateForTesting(
-      base::Optional<base::flat_set<DomCode>> dom_codes,
-      KeyEventCallback callback);
-
-  // Called when a key event message is delivered via the low-level hook.
-  // Exposed here to allow for testing w/o engaging the low-level hook.
-  // Returns true if the message was handled.
-  virtual bool ProcessKeyEventMessage(WPARAM w_param,
-                                      DWORD vk,
-                                      DWORD scan_code,
-                                      DWORD time_stamp) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWin);
-};
-
-}  // namespace ui
-
-#endif  // UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_
diff --git a/ui/events/win/keyboard_hook_win_base.cc b/ui/events/win/keyboard_hook_win_base.cc
new file mode 100644
index 0000000..1841b06d
--- /dev/null
+++ b/ui/events/win/keyboard_hook_win_base.cc
@@ -0,0 +1,81 @@
+// 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/events/win/keyboard_hook_win_base.h"
+
+namespace ui {
+
+KeyboardHookWinBase::KeyboardHookWinBase(
+    base::Optional<base::flat_set<DomCode>> dom_codes,
+    KeyEventCallback callback,
+    bool enable_hook_registration)
+    : KeyboardHookBase(std::move(dom_codes), std::move(callback)),
+      enable_hook_registration_(enable_hook_registration) {}
+
+KeyboardHookWinBase::~KeyboardHookWinBase() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (!enable_hook_registration_)
+    return;
+
+  if (!UnhookWindowsHookEx(hook_))
+    DPLOG(ERROR) << "UnhookWindowsHookEx failed";
+}
+
+bool KeyboardHookWinBase::Register(HOOKPROC hook_proc) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // If the hook was created for testing, |Register()| should not be called.
+  DCHECK(enable_hook_registration_);
+
+  DCHECK(!hook_);
+
+  // Don't register hooks when there is a debugger to avoid painful user input
+  // delays.
+  if (IsDebuggerPresent())
+    return false;
+
+  // Per MSDN this Hook procedure will be called in the context of the thread
+  // which installed it.
+  hook_ = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc,
+                           /*hMod=*/nullptr,
+                           /*dwThreadId=*/0);
+  DPLOG_IF(ERROR, !hook_) << "SetWindowsHookEx failed";
+
+  return hook_ != nullptr;
+}
+
+// static
+LRESULT CALLBACK
+KeyboardHookWinBase::ProcessKeyEvent(KeyboardHookWinBase* instance,
+                                     int code,
+                                     WPARAM w_param,
+                                     LPARAM l_param) {
+  // If there is an error unhooking, this method could be called with a null
+  // |instance_|.  Ensure we have a valid instance and that |code| is correct
+  // before proceeding.
+  if (!instance || code != HC_ACTION)
+    return CallNextHookEx(nullptr, code, w_param, l_param);
+
+  DCHECK_CALLED_ON_VALID_THREAD(instance->thread_checker_);
+
+  KBDLLHOOKSTRUCT* ll_hooks = reinterpret_cast<KBDLLHOOKSTRUCT*>(l_param);
+
+  // This vkey represents both a vkey and a location on the keyboard such as
+  // VK_LCONTROL or VK_RCONTROL.
+  DWORD vk = ll_hooks->vkCode;
+
+  // Apply the extended flag prior to passing |scan_code| since |instance_| does
+  // not have access to the low-level hook flags.
+  DWORD scan_code = ll_hooks->scanCode;
+  if (ll_hooks->flags & LLKHF_EXTENDED)
+    scan_code |= 0xE000;
+
+  if (instance->ProcessKeyEventMessage(w_param, vk, scan_code, ll_hooks->time))
+    return 1;
+
+  return CallNextHookEx(nullptr, code, w_param, l_param);
+}
+
+}  // namespace ui
diff --git a/ui/events/win/keyboard_hook_win_base.h b/ui/events/win/keyboard_hook_win_base.h
new file mode 100644
index 0000000..c9da794a
--- /dev/null
+++ b/ui/events/win/keyboard_hook_win_base.h
@@ -0,0 +1,74 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_BASE_H_
+#define UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_BASE_H_
+
+#include <memory>
+
+#include <windows.h>
+
+#include "base/containers/flat_set.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "ui/events/event.h"
+#include "ui/events/events_export.h"
+#include "ui/events/keyboard_hook_base.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+
+namespace ui {
+
+// Exposes a method to drive the Windows KeyboardHook implementation by feeding
+// it key event data.  This method is used by both the low-level keyboard hook
+// and by unit tests which simulate the hooked behavior w/o actually installing
+// a hook (doing so would cause problems with test parallelization).
+class EVENTS_EXPORT KeyboardHookWinBase : public KeyboardHookBase {
+ public:
+  KeyboardHookWinBase(base::Optional<base::flat_set<DomCode>> dom_codes,
+                      KeyEventCallback callback,
+                      bool enable_hook_registration);
+  ~KeyboardHookWinBase() override;
+
+  // Create a KeyboardHookWinBase instance which does not register a
+  // low-level hook and captures modifier keys.
+  static std::unique_ptr<KeyboardHookWinBase>
+  CreateModifierKeyboardHookForTesting(
+      base::Optional<base::flat_set<DomCode>> dom_codes,
+      KeyEventCallback callback);
+
+  // Create a KeyboardHookWinBase instance which does not register a
+  // low-level hook and captures media keys.
+  static std::unique_ptr<KeyboardHookWinBase> CreateMediaKeyboardHookForTesting(
+      KeyEventCallback callback);
+
+  // Called when a key event message is delivered via the low-level hook.
+  // Exposed here to allow for testing w/o engaging the low-level hook.
+  // Returns true if the message was handled.
+  virtual bool ProcessKeyEventMessage(WPARAM w_param,
+                                      DWORD vk,
+                                      DWORD scan_code,
+                                      DWORD time_stamp) = 0;
+
+ protected:
+  bool Register(HOOKPROC hook_proc);
+  bool enable_hook_registration() const { return enable_hook_registration_; }
+
+  static LRESULT CALLBACK ProcessKeyEvent(KeyboardHookWinBase* instance,
+                                          int code,
+                                          WPARAM w_param,
+                                          LPARAM l_param);
+
+ private:
+  const bool enable_hook_registration_ = true;
+  HHOOK hook_ = nullptr;
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWinBase);
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_BASE_H_
diff --git a/ui/events/win/media_keyboard_hook_win.cc b/ui/events/win/media_keyboard_hook_win.cc
new file mode 100644
index 0000000..141ac572
--- /dev/null
+++ b/ui/events/win/media_keyboard_hook_win.cc
@@ -0,0 +1,139 @@
+// 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/events/win/keyboard_hook_win_base.h"
+
+#include "ui/events/event.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/win/events_win_utils.h"
+
+namespace ui {
+
+namespace {
+
+bool IsMediaKey(DWORD vk) {
+  return vk == VK_MEDIA_NEXT_TRACK || vk == VK_MEDIA_PREV_TRACK ||
+         vk == VK_MEDIA_PLAY_PAUSE || vk == VK_MEDIA_STOP;
+}
+
+class MediaKeyboardHookWinImpl : public KeyboardHookWinBase {
+ public:
+  MediaKeyboardHookWinImpl(KeyEventCallback callback,
+                           bool enable_hook_registration);
+  ~MediaKeyboardHookWinImpl() override;
+
+  // KeyboardHookWinBase implementation.
+  bool ProcessKeyEventMessage(WPARAM w_param,
+                              DWORD vk,
+                              DWORD scan_code,
+                              DWORD time_stamp) override;
+
+  bool Register();
+
+ private:
+  static LRESULT CALLBACK ProcessKeyEvent(int code,
+                                          WPARAM w_param,
+                                          LPARAM l_param);
+
+  static MediaKeyboardHookWinImpl* instance_;
+
+  // Tracks the last non-located key down seen in order to determine if the
+  // current key event should be marked as a repeated key press.
+  DWORD last_key_down_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaKeyboardHookWinImpl);
+};
+
+// static
+MediaKeyboardHookWinImpl* MediaKeyboardHookWinImpl::instance_ = nullptr;
+
+MediaKeyboardHookWinImpl::MediaKeyboardHookWinImpl(
+    KeyEventCallback callback,
+    bool enable_hook_registration)
+    : KeyboardHookWinBase(
+          base::Optional<base::flat_set<DomCode>>(
+              {DomCode::MEDIA_PLAY_PAUSE, DomCode::MEDIA_STOP,
+               DomCode::MEDIA_TRACK_NEXT, DomCode::MEDIA_TRACK_PREVIOUS}),
+          std::move(callback),
+          enable_hook_registration) {}
+
+MediaKeyboardHookWinImpl::~MediaKeyboardHookWinImpl() {
+  if (!enable_hook_registration())
+    return;
+
+  DCHECK_EQ(instance_, this);
+  instance_ = nullptr;
+}
+
+bool MediaKeyboardHookWinImpl::Register() {
+  // Only one instance of this class can be registered at a time.
+  DCHECK(!instance_);
+  instance_ = this;
+
+  return KeyboardHookWinBase::Register(
+      reinterpret_cast<HOOKPROC>(&MediaKeyboardHookWinImpl::ProcessKeyEvent));
+}
+
+// static
+LRESULT CALLBACK MediaKeyboardHookWinImpl::ProcessKeyEvent(int code,
+                                                           WPARAM w_param,
+                                                           LPARAM l_param) {
+  return KeyboardHookWinBase::ProcessKeyEvent(instance_, code, w_param,
+                                              l_param);
+}
+
+bool MediaKeyboardHookWinImpl::ProcessKeyEventMessage(WPARAM w_param,
+                                                      DWORD vk,
+                                                      DWORD scan_code,
+                                                      DWORD time_stamp) {
+  if (!IsMediaKey(vk))
+    return false;
+
+  bool is_repeat = false;
+  MSG msg = {nullptr, w_param, vk, GetLParamFromScanCode(scan_code),
+             time_stamp};
+  EventType event_type = EventTypeFromMSG(msg);
+  if (event_type == ET_KEY_PRESSED) {
+    is_repeat = (last_key_down_ == vk);
+    last_key_down_ = vk;
+  } else {
+    DCHECK_EQ(event_type, ET_KEY_RELEASED);
+    last_key_down_ = 0;
+  }
+
+  std::unique_ptr<KeyEvent> key_event =
+      std::make_unique<KeyEvent>(KeyEventFromMSG(msg));
+  if (is_repeat)
+    key_event->set_flags(key_event->flags() | EF_IS_REPEAT);
+  ForwardCapturedKeyEvent(key_event.get());
+
+  // If the event is handled, don't propagate to the OS.
+  return key_event->handled();
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
+    KeyEventCallback callback) {
+  std::unique_ptr<MediaKeyboardHookWinImpl> keyboard_hook =
+      std::make_unique<MediaKeyboardHookWinImpl>(
+          std::move(callback),
+          /*enable_hook_registration=*/true);
+
+  if (!keyboard_hook->Register())
+    return nullptr;
+
+  return keyboard_hook;
+}
+
+std::unique_ptr<KeyboardHookWinBase>
+KeyboardHookWinBase::CreateMediaKeyboardHookForTesting(
+    KeyEventCallback callback) {
+  return std::make_unique<MediaKeyboardHookWinImpl>(
+      std::move(callback),
+      /*enable_hook_registration=*/false);
+}
+
+}  // namespace ui
diff --git a/ui/events/win/media_keyboard_hook_win_interactive_test.cc b/ui/events/win/media_keyboard_hook_win_interactive_test.cc
new file mode 100644
index 0000000..18a2df9
--- /dev/null
+++ b/ui/events/win/media_keyboard_hook_win_interactive_test.cc
@@ -0,0 +1,120 @@
+// 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/events/keyboard_hook.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+
+namespace ui {
+
+class MediaKeyboardHookWinInteractiveTest : public testing::Test {
+ public:
+  MediaKeyboardHookWinInteractiveTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
+
+ protected:
+  void SetUp() override {
+    keyboard_hook_ = KeyboardHook::CreateMediaKeyboardHook(base::BindRepeating(
+        &MediaKeyboardHookWinInteractiveTest::HandleKeyEvent,
+        base::Unretained(this)));
+    ASSERT_NE(nullptr, keyboard_hook_);
+  }
+
+  // Loop until we've received |num_events| key events from the hook.
+  void WaitForKeyEvents(uint32_t num_events) {
+    if (key_events_.size() >= num_events)
+      return;
+
+    num_key_events_to_wait_for_ = num_events;
+    key_event_wait_loop_.Run();
+  }
+
+  void SendKeyDown(KeyboardCode code) {
+    INPUT input;
+    input.type = INPUT_KEYBOARD;
+    input.ki.wVk = code;
+    input.ki.time = time_stamp_++;
+    input.ki.dwFlags = 0;
+    SendInput(1, &input, sizeof(INPUT));
+  }
+
+  void SendKeyUp(KeyboardCode code) {
+    INPUT input;
+    input.type = INPUT_KEYBOARD;
+    input.ki.wVk = code;
+    input.ki.time = time_stamp_++;
+    input.ki.dwFlags = KEYEVENTF_KEYUP;
+    SendInput(1, &input, sizeof(INPUT));
+  }
+
+  // Expect that we have received the correct number of key events.
+  void ExpectReceivedEventsCount(uint32_t count) {
+    EXPECT_EQ(count, key_events_.size());
+  }
+
+  // Expect that the key event received at |index| has the specified key code
+  // and type.
+  void ExpectReceivedEvent(uint32_t index, KeyboardCode code, EventType type) {
+    ASSERT_LT(index, key_events_.size());
+    KeyEvent* key_event = &key_events_.at(index);
+    EXPECT_EQ(code, key_event->key_code());
+    EXPECT_EQ(type, key_event->type());
+  }
+
+ private:
+  void HandleKeyEvent(KeyEvent* key_event) {
+    key_events_.push_back(*key_event);
+    key_event->SetHandled();
+
+    // If we've received the events we're waiting for, stop waiting.
+    if (key_event_wait_loop_.running() &&
+        key_events_.size() >= num_key_events_to_wait_for_) {
+      key_event_wait_loop_.Quit();
+    }
+  }
+
+  std::vector<KeyEvent> key_events_;
+  std::unique_ptr<KeyboardHook> keyboard_hook_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::RunLoop key_event_wait_loop_;
+  uint32_t num_key_events_to_wait_for_ = 0;
+  DWORD time_stamp_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaKeyboardHookWinInteractiveTest);
+};
+
+// Test that we catch the different media key events.
+TEST_F(MediaKeyboardHookWinInteractiveTest, AllMediaKeysAreCaught) {
+  SendKeyDown(ui::VKEY_MEDIA_PLAY_PAUSE);
+  SendKeyUp(ui::VKEY_MEDIA_PLAY_PAUSE);
+  SendKeyDown(ui::VKEY_MEDIA_STOP);
+  SendKeyUp(ui::VKEY_MEDIA_STOP);
+  SendKeyDown(ui::VKEY_MEDIA_NEXT_TRACK);
+  SendKeyUp(ui::VKEY_MEDIA_NEXT_TRACK);
+  SendKeyDown(ui::VKEY_MEDIA_PREV_TRACK);
+  SendKeyUp(ui::VKEY_MEDIA_PREV_TRACK);
+
+  // We should receive 8 different key events.
+  WaitForKeyEvents(8);
+}
+
+// Test that the received events have the proper state.
+TEST_F(MediaKeyboardHookWinInteractiveTest, CallbackReceivesProperEvents) {
+  // Send a key down event and validate it when received through the hook.
+  SendKeyDown(ui::VKEY_MEDIA_PLAY_PAUSE);
+  WaitForKeyEvents(1);
+  ExpectReceivedEvent(/*index=*/0, ui::VKEY_MEDIA_PLAY_PAUSE, ET_KEY_PRESSED);
+
+  // Send a key up event and validate it when received through the hook.
+  SendKeyUp(ui::VKEY_MEDIA_PLAY_PAUSE);
+  WaitForKeyEvents(2);
+  ExpectReceivedEvent(/*index=*/1, ui::VKEY_MEDIA_PLAY_PAUSE, ET_KEY_RELEASED);
+}
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/events/win/media_keyboard_hook_win_unittest.cc b/ui/events/win/media_keyboard_hook_win_unittest.cc
new file mode 100644
index 0000000..d043877
--- /dev/null
+++ b/ui/events/win/media_keyboard_hook_win_unittest.cc
@@ -0,0 +1,206 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+#include "ui/events/keyboard_hook.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/events/win/keyboard_hook_win_base.h"
+
+namespace ui {
+
+class MediaKeyboardHookWinTest : public testing::Test {
+ public:
+  MediaKeyboardHookWinTest();
+  ~MediaKeyboardHookWinTest() override;
+
+  // testing::Test overrides.
+  void SetUp() override;
+
+  void HandleKeyPress(KeyEvent* key_event);
+
+ protected:
+  KeyboardHookWinBase* keyboard_hook() { return keyboard_hook_.get(); }
+
+  uint32_t next_time_stamp() { return time_stamp_++; }
+
+  std::vector<KeyEvent>* key_events() { return &key_events_; }
+
+  // Used for sending key events which are handled by the hook.
+  void SendMediaKeyDownEvent(KeyboardCode key_code,
+                             DomCode dom_code,
+                             int repeat_count = 1);
+  void SendMediaKeyUpEvent(KeyboardCode key_code, DomCode dom_code);
+
+  // Set the return value for the HandleKeyPress callback.
+  void StartHandlingKeys() { should_handle_keys_ = true; }
+  void StopHandlingKeys() { should_handle_keys_ = false; }
+
+ private:
+  uint32_t time_stamp_ = 0;
+  std::unique_ptr<KeyboardHookWinBase> keyboard_hook_;
+  std::vector<KeyEvent> key_events_;
+  bool should_handle_keys_ = true;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaKeyboardHookWinTest);
+};
+
+MediaKeyboardHookWinTest::MediaKeyboardHookWinTest() = default;
+
+MediaKeyboardHookWinTest::~MediaKeyboardHookWinTest() = default;
+
+void MediaKeyboardHookWinTest::SetUp() {
+  keyboard_hook_ = KeyboardHookWinBase::CreateMediaKeyboardHookForTesting(
+      base::BindRepeating(&MediaKeyboardHookWinTest::HandleKeyPress,
+                          base::Unretained(this)));
+}
+
+void MediaKeyboardHookWinTest::HandleKeyPress(KeyEvent* key_event) {
+  key_events_.push_back(*key_event);
+  if (should_handle_keys_)
+    key_event->SetHandled();
+}
+
+void MediaKeyboardHookWinTest::SendMediaKeyDownEvent(KeyboardCode key_code,
+                                                     DomCode dom_code,
+                                                     int repeat_count /*=1*/) {
+  ASSERT_GT(repeat_count, 0);
+  // This should only be used when we're handling keys.
+  ASSERT_TRUE(should_handle_keys_);
+
+  for (int i = 0; i < repeat_count; i++) {
+    ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+        WM_KEYDOWN, key_code,
+        KeycodeConverter::DomCodeToNativeKeycode(dom_code), next_time_stamp()));
+  }
+}
+
+void MediaKeyboardHookWinTest::SendMediaKeyUpEvent(KeyboardCode key_code,
+                                                   DomCode dom_code) {
+  // This should only be used when we're handling keys.
+  ASSERT_TRUE(should_handle_keys_);
+
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, key_code, KeycodeConverter::DomCodeToNativeKeycode(dom_code),
+      next_time_stamp()));
+}
+
+void VerifyKeyEvent(KeyEvent* key_event,
+                    KeyboardCode non_located_key_code,
+                    DomCode dom_code,
+                    bool key_down,
+                    bool is_repeat) {
+  if (key_down) {
+    ASSERT_EQ(key_event->type(), ET_KEY_PRESSED);
+    ASSERT_EQ(key_event->is_repeat(), is_repeat);
+  } else {
+    ASSERT_EQ(key_event->type(), ET_KEY_RELEASED);
+    ASSERT_FALSE(key_event->is_repeat());
+  }
+  ASSERT_EQ(key_event->key_code(), non_located_key_code);
+  ASSERT_EQ(key_event->code(), dom_code);
+}
+
+TEST_F(MediaKeyboardHookWinTest, SimpleKeypressTest) {
+  const KeyboardCode key_code = KeyboardCode::VKEY_MEDIA_PLAY_PAUSE;
+  const DomCode dom_code = DomCode::MEDIA_PLAY_PAUSE;
+  SendMediaKeyDownEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 1u);
+  SendMediaKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(key_events()->size(), 2u);
+
+  KeyEvent down_event = key_events()->at(0);
+  ASSERT_NO_FATAL_FAILURE(
+      VerifyKeyEvent(&down_event, key_code, dom_code, true, false));
+
+  KeyEvent up_event = key_events()->at(1);
+  ASSERT_NO_FATAL_FAILURE(
+      VerifyKeyEvent(&up_event, key_code, dom_code, false, false));
+}
+
+TEST_F(MediaKeyboardHookWinTest, RepeatingKeypressTest) {
+  const int repeat_count = 10;
+  const KeyboardCode key_code = KeyboardCode::VKEY_MEDIA_PLAY_PAUSE;
+  const DomCode dom_code = DomCode::MEDIA_PLAY_PAUSE;
+  SendMediaKeyDownEvent(key_code, dom_code, repeat_count);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count);
+  SendMediaKeyUpEvent(key_code, dom_code);
+  ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1);
+
+  bool should_repeat = false;
+  for (int i = 0; i < repeat_count; i++) {
+    KeyEvent event = key_events()->at(i);
+    ASSERT_NO_FATAL_FAILURE(
+        VerifyKeyEvent(&event, key_code, dom_code, true, should_repeat));
+    should_repeat = true;
+  }
+
+  KeyEvent up_event = key_events()->at(repeat_count);
+  ASSERT_NO_FATAL_FAILURE(
+      VerifyKeyEvent(&up_event, key_code, dom_code, false, false));
+}
+
+TEST_F(MediaKeyboardHookWinTest, UnhandledKeysArePropagated) {
+  StopHandlingKeys();
+
+  // Ensure media keys are propagated to the OS.
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_MEDIA_STOP,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP),
+      next_time_stamp()));
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_MEDIA_STOP,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP),
+      next_time_stamp()));
+
+  StartHandlingKeys();
+
+  // Ensure media keys are not propagated to the OS.
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_MEDIA_STOP,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP),
+      next_time_stamp()));
+  ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_MEDIA_STOP,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_STOP),
+      next_time_stamp()));
+}
+
+TEST_F(MediaKeyboardHookWinTest, NonInterceptedKeysTest) {
+  // Here we try a few keys we do not expect to be intercepted / handled.
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_RSHIFT,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT),
+      next_time_stamp()));
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_RSHIFT,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT),
+      next_time_stamp()));
+
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_ESCAPE,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::ESCAPE),
+      next_time_stamp()));
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_ESCAPE,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::ESCAPE),
+      next_time_stamp()));
+
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYDOWN, KeyboardCode::VKEY_A,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A),
+      next_time_stamp()));
+  ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
+      WM_KEYUP, KeyboardCode::VKEY_A,
+      KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A),
+      next_time_stamp()));
+}
+
+}  // namespace ui
diff --git a/ui/events/win/keyboard_hook_win.cc b/ui/events/win/modifier_keyboard_hook_win.cc
similarity index 73%
rename from ui/events/win/keyboard_hook_win.cc
rename to ui/events/win/modifier_keyboard_hook_win.cc
index 042e2956..f1f5af4 100644
--- a/ui/events/win/keyboard_hook_win.cc
+++ b/ui/events/win/modifier_keyboard_hook_win.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/events/win/keyboard_hook_win.h"
+#include "ui/events/win/keyboard_hook_win_base.h"
 
 #include <utility>
 
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/optional.h"
-#include "base/threading/thread_checker.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -108,14 +107,14 @@
   return IsAltKey(vk) || IsControlKey(vk) || IsWindowsKey(vk);
 }
 
-class KeyboardHookWinImpl : public KeyboardHookWin {
+class ModifierKeyboardHookWinImpl : public KeyboardHookWinBase {
  public:
-  KeyboardHookWinImpl(base::Optional<base::flat_set<DomCode>> dom_codes,
-                      KeyEventCallback callback,
-                      bool enable_hook_registration);
-  ~KeyboardHookWinImpl() override;
+  ModifierKeyboardHookWinImpl(base::Optional<base::flat_set<DomCode>> dom_codes,
+                              KeyEventCallback callback,
+                              bool enable_hook_registration);
+  ~ModifierKeyboardHookWinImpl() override;
 
-  // KeyboardHookWin implementation.
+  // KeyboardHookWinBase implementation.
   bool ProcessKeyEventMessage(WPARAM w_param,
                               DWORD vk,
                               DWORD scan_code,
@@ -132,11 +131,7 @@
 
   void ClearModifierStates();
 
-  static KeyboardHookWinImpl* instance_;
-
-  THREAD_CHECKER(thread_checker_);
-
-  HHOOK hook_ = nullptr;
+  static ModifierKeyboardHookWinImpl* instance_;
 
   // Tracks the last non-located key down seen in order to determine if the
   // current key event should be marked as a repeated key press.
@@ -148,59 +143,40 @@
   // This sequence occurs on the initial keypress and every repeat.
   int altgr_sequence_count_ = 0;
 
-  const bool enable_hook_registration_ = true;
-
-  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWinImpl);
+  DISALLOW_COPY_AND_ASSIGN(ModifierKeyboardHookWinImpl);
 };
 
 // static
-KeyboardHookWinImpl* KeyboardHookWinImpl::instance_ = nullptr;
+ModifierKeyboardHookWinImpl* ModifierKeyboardHookWinImpl::instance_ = nullptr;
 
-KeyboardHookWinImpl::KeyboardHookWinImpl(
+ModifierKeyboardHookWinImpl::ModifierKeyboardHookWinImpl(
     base::Optional<base::flat_set<DomCode>> dom_codes,
     KeyEventCallback callback,
     bool enable_hook_registration)
-    : KeyboardHookWin(std::move(dom_codes), std::move(callback)),
-      enable_hook_registration_(enable_hook_registration) {}
+    : KeyboardHookWinBase(std::move(dom_codes),
+                          std::move(callback),
+                          enable_hook_registration) {}
 
-KeyboardHookWinImpl::~KeyboardHookWinImpl() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
+ModifierKeyboardHookWinImpl::~ModifierKeyboardHookWinImpl() {
   ClearModifierStates();
 
-  if (!enable_hook_registration_)
+  if (!enable_hook_registration())
     return;
 
   DCHECK_EQ(instance_, this);
   instance_ = nullptr;
-
-  if (!UnhookWindowsHookEx(hook_))
-    DPLOG(ERROR) << "UnhookWindowsHookEx failed";
 }
 
-bool KeyboardHookWinImpl::Register() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  // If the hook was created for testing, |Register()| should not be called.
-  DCHECK(enable_hook_registration_);
-
+bool ModifierKeyboardHookWinImpl::Register() {
   // Only one instance of this class can be registered at a time.
   DCHECK(!instance_);
   instance_ = this;
 
-  // Per MSDN this Hook procedure will be called in the context of the thread
-  // which installed it.
-  hook_ = SetWindowsHookEx(
-      WH_KEYBOARD_LL,
-      reinterpret_cast<HOOKPROC>(&KeyboardHookWinImpl::ProcessKeyEvent),
-      /*hMod=*/nullptr,
-      /*dwThreadId=*/0);
-  DPLOG_IF(ERROR, !hook_) << "SetWindowsHookEx failed";
-
-  return hook_ != nullptr;
+  return KeyboardHookWinBase::Register(reinterpret_cast<HOOKPROC>(
+      &ModifierKeyboardHookWinImpl::ProcessKeyEvent));
 }
 
-void KeyboardHookWinImpl::ClearModifierStates() {
+void ModifierKeyboardHookWinImpl::ClearModifierStates() {
   BYTE keyboard_state[kKeyboardStateArraySize] = {0};
   if (!GetKeyboardState(keyboard_state)) {
     DPLOG(ERROR) << "GetKeyboardState() failed: ";
@@ -221,10 +197,10 @@
     DPLOG(ERROR) << "SetKeyboardState() failed: ";
 }
 
-bool KeyboardHookWinImpl::ProcessKeyEventMessage(WPARAM w_param,
-                                                 DWORD vk,
-                                                 DWORD scan_code,
-                                                 DWORD time_stamp) {
+bool ModifierKeyboardHookWinImpl::ProcessKeyEventMessage(WPARAM w_param,
+                                                         DWORD vk,
+                                                         DWORD scan_code,
+                                                         DWORD time_stamp) {
   // The |vk| delivered to the low-level hook includes a location which is
   // needed to track individual keystates such as when both left and right
   // control keys are pressed.  Make sure that location information was retained
@@ -288,12 +264,13 @@
       std::make_unique<KeyEvent>(KeyEventFromMSG(msg));
   if (is_repeat)
     key_event->set_flags(key_event->flags() | EF_IS_REPEAT);
-  ForwardCapturedKeyEvent(std::move(key_event));
+  ForwardCapturedKeyEvent(key_event.get());
 
   return true;
 }
 
-void KeyboardHookWinImpl::UpdateModifierState(DWORD vk, bool is_key_down) {
+void ModifierKeyboardHookWinImpl::UpdateModifierState(DWORD vk,
+                                                      bool is_key_down) {
   BYTE keyboard_state[kKeyboardStateArraySize] = {0};
   if (!GetKeyboardState(keyboard_state)) {
     DPLOG(ERROR) << "GetKeyboardState() failed: ";
@@ -318,46 +295,24 @@
 }
 
 // static
-LRESULT CALLBACK KeyboardHookWinImpl::ProcessKeyEvent(int code,
-                                                      WPARAM w_param,
-                                                      LPARAM l_param) {
-  // If there is an error unhooking, this method could be called with a null
-  // |instance_|.  Ensure we have a valid instance and that |code| is correct
-  // before proceeding.
-  if (!instance_ || code != HC_ACTION)
-    return CallNextHookEx(nullptr, code, w_param, l_param);
-
-  DCHECK_CALLED_ON_VALID_THREAD(instance_->thread_checker_);
-
-  KBDLLHOOKSTRUCT* ll_hooks = reinterpret_cast<KBDLLHOOKSTRUCT*>(l_param);
-
-  // This vkey represents both a vkey and a location on the keyboard such as
-  // VK_LCONTROL or VK_RCONTROL.
-  DWORD vk = ll_hooks->vkCode;
-
-  // Apply the extended flag prior to passing |scan_code| since |instance_| does
-  // not have access to the low-level hook flags.
-  DWORD scan_code = ll_hooks->scanCode;
-  if (ll_hooks->flags & LLKHF_EXTENDED)
-    scan_code |= 0xE000;
-
-  if (instance_->ProcessKeyEventMessage(w_param, vk, scan_code, ll_hooks->time))
-    return 1;
-
-  return CallNextHookEx(nullptr, code, w_param, l_param);
+LRESULT CALLBACK ModifierKeyboardHookWinImpl::ProcessKeyEvent(int code,
+                                                              WPARAM w_param,
+                                                              LPARAM l_param) {
+  return KeyboardHookWinBase::ProcessKeyEvent(instance_, code, w_param,
+                                              l_param);
 }
 
 }  // namespace
 
 // static
-std::unique_ptr<KeyboardHook> KeyboardHook::Create(
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
     base::Optional<base::flat_set<DomCode>> dom_codes,
     gfx::AcceleratedWidget accelerated_widget,
     KeyEventCallback callback) {
-  std::unique_ptr<KeyboardHookWinImpl> keyboard_hook =
-      std::make_unique<KeyboardHookWinImpl>(std::move(dom_codes),
-                                            std::move(callback),
-                                            /*enable_hook_registration=*/true);
+  std::unique_ptr<ModifierKeyboardHookWinImpl> keyboard_hook =
+      std::make_unique<ModifierKeyboardHookWinImpl>(
+          std::move(dom_codes), std::move(callback),
+          /*enable_hook_registration=*/true);
 
   if (!keyboard_hook->Register())
     return nullptr;
@@ -365,19 +320,13 @@
   return keyboard_hook;
 }
 
-std::unique_ptr<KeyboardHookWin> KeyboardHookWin::CreateForTesting(
+std::unique_ptr<KeyboardHookWinBase>
+KeyboardHookWinBase::CreateModifierKeyboardHookForTesting(
     base::Optional<base::flat_set<DomCode>> dom_codes,
     KeyEventCallback callback) {
-  return std::make_unique<KeyboardHookWinImpl>(
+  return std::make_unique<ModifierKeyboardHookWinImpl>(
       std::move(dom_codes), std::move(callback),
       /*enable_hook_registration=*/false);
 }
 
-KeyboardHookWin::KeyboardHookWin(
-    base::Optional<base::flat_set<DomCode>> dom_codes,
-    KeyEventCallback callback)
-    : KeyboardHookBase(std::move(dom_codes), std::move(callback)) {}
-
-KeyboardHookWin::~KeyboardHookWin() = default;
-
 }  // namespace ui
diff --git a/ui/events/win/keyboard_hook_win_unittest.cc b/ui/events/win/modifier_keyboard_hook_win_unittest.cc
similarity index 93%
rename from ui/events/win/keyboard_hook_win_unittest.cc
rename to ui/events/win/modifier_keyboard_hook_win_unittest.cc
index d8f3dea..ff1dcf4a1 100644
--- a/ui/events/win/keyboard_hook_win_unittest.cc
+++ b/ui/events/win/modifier_keyboard_hook_win_unittest.cc
@@ -15,15 +15,15 @@
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/keyboard_layout.h"
-#include "ui/events/win/keyboard_hook_win.h"
+#include "ui/events/win/keyboard_hook_win_base.h"
 #include "ui/events/win/system_event_state_lookup.h"
 
 namespace ui {
 
-class KeyboardHookWinTest : public testing::Test {
+class ModifierKeyboardHookWinTest : public testing::Test {
  public:
-  KeyboardHookWinTest();
-  ~KeyboardHookWinTest() override;
+  ModifierKeyboardHookWinTest();
+  ~ModifierKeyboardHookWinTest() override;
 
   // testing::Test overrides.
   void SetUp() override;
@@ -31,7 +31,7 @@
   void HandleKeyPress(KeyEvent* key_event);
 
  protected:
-  KeyboardHookWin* keyboard_hook() { return keyboard_hook_.get(); }
+  KeyboardHookWinBase* keyboard_hook() { return keyboard_hook_.get(); }
 
   uint32_t next_time_stamp() { return time_stamp_++; }
 
@@ -47,34 +47,35 @@
 
  private:
   uint32_t time_stamp_ = 0;
-  std::unique_ptr<KeyboardHookWin> keyboard_hook_;
+  std::unique_ptr<KeyboardHookWinBase> keyboard_hook_;
   std::vector<KeyEvent> key_events_;
   std::unique_ptr<ScopedKeyboardLayout> keyboard_layout_;
 
-  DISALLOW_COPY_AND_ASSIGN(KeyboardHookWinTest);
+  DISALLOW_COPY_AND_ASSIGN(ModifierKeyboardHookWinTest);
 };
 
-KeyboardHookWinTest::KeyboardHookWinTest() = default;
+ModifierKeyboardHookWinTest::ModifierKeyboardHookWinTest() = default;
 
-KeyboardHookWinTest::~KeyboardHookWinTest() = default;
+ModifierKeyboardHookWinTest::~ModifierKeyboardHookWinTest() = default;
 
-void KeyboardHookWinTest::SetUp() {
-  keyboard_hook_ = KeyboardHookWin::CreateForTesting(
+void ModifierKeyboardHookWinTest::SetUp() {
+  keyboard_hook_ = KeyboardHookWinBase::CreateModifierKeyboardHookForTesting(
       base::Optional<base::flat_set<DomCode>>(),
-      base::BindRepeating(&KeyboardHookWinTest::HandleKeyPress,
+      base::BindRepeating(&ModifierKeyboardHookWinTest::HandleKeyPress,
                           base::Unretained(this)));
 
   keyboard_layout_ = std::make_unique<ScopedKeyboardLayout>(
       KeyboardLayout::KEYBOARD_LAYOUT_ENGLISH_US);
 }
 
-void KeyboardHookWinTest::HandleKeyPress(KeyEvent* key_event) {
+void ModifierKeyboardHookWinTest::HandleKeyPress(KeyEvent* key_event) {
   key_events_.push_back(*key_event);
 }
 
-void KeyboardHookWinTest::SendModifierKeyDownEvent(KeyboardCode key_code,
-                                                   DomCode dom_code,
-                                                   int repeat_count /*=1*/) {
+void ModifierKeyboardHookWinTest::SendModifierKeyDownEvent(
+    KeyboardCode key_code,
+    DomCode dom_code,
+    int repeat_count /*=1*/) {
   // Ensure we have a valid repeat count and the modifer passed in contains
   // location information.
   DCHECK_GT(repeat_count, 0);
@@ -88,8 +89,8 @@
   }
 }
 
-void KeyboardHookWinTest::SendModifierKeyUpEvent(KeyboardCode key_code,
-                                                 DomCode dom_code) {
+void ModifierKeyboardHookWinTest::SendModifierKeyUpEvent(KeyboardCode key_code,
+                                                         DomCode dom_code) {
   // Ensure we have a valid repeat count and the modifer passed in contains
   // location information.
   DCHECK_NE(key_code, KeyboardCode::VKEY_CONTROL);
@@ -116,7 +117,7 @@
   ASSERT_EQ(key_event->code(), dom_code);
 }
 
-TEST_F(KeyboardHookWinTest, SimpleLeftControlKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleLeftControlKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL;
   const DomCode dom_code = DomCode::CONTROL_LEFT;
   SendModifierKeyDownEvent(key_code, dom_code);
@@ -136,7 +137,7 @@
   ASSERT_FALSE(up_event.IsControlDown());
 }
 
-TEST_F(KeyboardHookWinTest, RepeatingLeftControlKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, RepeatingLeftControlKeypressTest) {
   const int repeat_count = 10;
   const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL;
   const DomCode dom_code = DomCode::CONTROL_LEFT;
@@ -161,7 +162,7 @@
   ASSERT_FALSE(up_event.IsControlDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleRightControlKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleRightControlKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL;
   const DomCode dom_code = DomCode::CONTROL_RIGHT;
   SendModifierKeyDownEvent(key_code, dom_code);
@@ -181,7 +182,7 @@
   ASSERT_FALSE(up_event.IsControlDown());
 }
 
-TEST_F(KeyboardHookWinTest, RepeatingRightControlKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, RepeatingRightControlKeypressTest) {
   const int repeat_count = 10;
   const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL;
   const DomCode dom_code = DomCode::CONTROL_RIGHT;
@@ -206,7 +207,7 @@
   ASSERT_FALSE(up_event.IsControlDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleLifoControlSequenceTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleLifoControlSequenceTest) {
   const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL;
   const DomCode left_dom_code = DomCode::CONTROL_LEFT;
   const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL;
@@ -253,7 +254,7 @@
   ASSERT_FALSE(event.IsControlDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleFifoControlSequenceTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleFifoControlSequenceTest) {
   const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL;
   const DomCode left_dom_code = DomCode::CONTROL_LEFT;
   const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL;
@@ -299,7 +300,7 @@
   ASSERT_FALSE(event.IsControlDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleLeftAltKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleLeftAltKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_LMENU;
   const DomCode dom_code = DomCode::ALT_LEFT;
   SendModifierKeyDownEvent(key_code, dom_code);
@@ -318,7 +319,7 @@
   ASSERT_FALSE(up_event.IsAltDown());
 }
 
-TEST_F(KeyboardHookWinTest, RepeatingLeftAltKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, RepeatingLeftAltKeypressTest) {
   const int repeat_count = 10;
   const KeyboardCode key_code = KeyboardCode::VKEY_LMENU;
   const DomCode dom_code = DomCode::ALT_LEFT;
@@ -343,7 +344,7 @@
   ASSERT_FALSE(up_event.IsAltDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleRightAltKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleRightAltKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_RMENU;
   const DomCode dom_code = DomCode::ALT_LEFT;
   SendModifierKeyDownEvent(key_code, dom_code);
@@ -362,7 +363,7 @@
   ASSERT_FALSE(up_event.IsAltDown());
 }
 
-TEST_F(KeyboardHookWinTest, RepeatingRightAltKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, RepeatingRightAltKeypressTest) {
   const int repeat_count = 10;
   const KeyboardCode key_code = KeyboardCode::VKEY_RMENU;
   const DomCode dom_code = DomCode::ALT_RIGHT;
@@ -387,7 +388,7 @@
   ASSERT_FALSE(up_event.IsAltDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleLifoAltSequenceTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleLifoAltSequenceTest) {
   const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU;
   const DomCode left_dom_code = DomCode::ALT_LEFT;
   const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU;
@@ -429,7 +430,7 @@
   ASSERT_FALSE(event.IsAltDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleFifoAltSequenceTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleFifoAltSequenceTest) {
   const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU;
   const DomCode left_dom_code = DomCode::ALT_LEFT;
   const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU;
@@ -471,7 +472,7 @@
   ASSERT_FALSE(event.IsAltDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleLeftWinKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleLeftWinKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_LWIN;
   const DomCode dom_code = DomCode::META_LEFT;
   SendModifierKeyDownEvent(key_code, dom_code);
@@ -491,7 +492,7 @@
   ASSERT_FALSE(up_event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, RepeatingLeftWinKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, RepeatingLeftWinKeypressTest) {
   const int repeat_count = 10;
   const KeyboardCode key_code = KeyboardCode::VKEY_LWIN;
   const DomCode dom_code = DomCode::META_LEFT;
@@ -517,7 +518,7 @@
   ASSERT_FALSE(up_event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleRightWinKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleRightWinKeypressTest) {
   const KeyboardCode key_code = KeyboardCode::VKEY_RWIN;
   const DomCode dom_code = DomCode::META_RIGHT;
   SendModifierKeyDownEvent(key_code, dom_code);
@@ -537,7 +538,7 @@
   ASSERT_FALSE(up_event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, RepeatingRightWinKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, RepeatingRightWinKeypressTest) {
   const int repeat_count = 10;
   const KeyboardCode key_code = KeyboardCode::VKEY_RWIN;
   const DomCode dom_code = DomCode::META_RIGHT;
@@ -563,7 +564,7 @@
   ASSERT_FALSE(up_event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleLifoWinSequenceTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleLifoWinSequenceTest) {
   const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN;
   const DomCode left_dom_code = DomCode::META_LEFT;
   const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN;
@@ -605,7 +606,7 @@
   ASSERT_FALSE(event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleFifoWinSequenceTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleFifoWinSequenceTest) {
   const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN;
   const DomCode left_dom_code = DomCode::META_LEFT;
   const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN;
@@ -647,7 +648,7 @@
   ASSERT_FALSE(event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, CombinedModifierLifoSequenceKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, CombinedModifierLifoSequenceKeypressTest) {
   const KeyboardCode first_key_code = KeyboardCode::VKEY_LCONTROL;
   const DomCode first_dom_code = DomCode::CONTROL_LEFT;
   const KeyboardCode second_key_code = KeyboardCode::VKEY_RWIN;
@@ -730,7 +731,7 @@
   ASSERT_FALSE(event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, CombinedModifierFifoSequenceKeypressTest) {
+TEST_F(ModifierKeyboardHookWinTest, CombinedModifierFifoSequenceKeypressTest) {
   const KeyboardCode first_key_code = KeyboardCode::VKEY_RCONTROL;
   const DomCode first_dom_code = DomCode::CONTROL_RIGHT;
   const KeyboardCode second_key_code = KeyboardCode::VKEY_LWIN;
@@ -813,7 +814,7 @@
   ASSERT_FALSE(event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, VerifyPlatformModifierStateTest) {
+TEST_F(ModifierKeyboardHookWinTest, VerifyPlatformModifierStateTest) {
   SendModifierKeyDownEvent(KeyboardCode::VKEY_LCONTROL, DomCode::CONTROL_LEFT);
   ASSERT_TRUE(win::IsCtrlPressed());
   ASSERT_FALSE(win::IsAltPressed());
@@ -850,7 +851,7 @@
   ASSERT_TRUE(win::IsAltRightPressed());
 }
 
-TEST_F(KeyboardHookWinTest, SimpleAltGrKeyPressTest) {
+TEST_F(ModifierKeyboardHookWinTest, SimpleAltGrKeyPressTest) {
   ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);
 
   // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
@@ -909,7 +910,7 @@
   ASSERT_FALSE(event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, RepeatingAltGrKeyPressTest) {
+TEST_F(ModifierKeyboardHookWinTest, RepeatingAltGrKeyPressTest) {
   ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);
 
   // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
@@ -1010,7 +1011,7 @@
   ASSERT_FALSE(event.IsCommandDown());
 }
 
-TEST_F(KeyboardHookWinTest, VerifyAltGrPlatformModifierStateTest) {
+TEST_F(ModifierKeyboardHookWinTest, VerifyAltGrPlatformModifierStateTest) {
   ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN);
 
   // AltGr produces two events, an injected, modified scan code for VK_LCONTROL,
@@ -1052,7 +1053,7 @@
   ASSERT_FALSE(win::IsWindowsKeyPressed());
 }
 
-TEST_F(KeyboardHookWinTest, NonInterceptedKeysTest) {
+TEST_F(ModifierKeyboardHookWinTest, NonInterceptedKeysTest) {
   // Here we try a few keys we do not expect to be intercepted / handled.
   ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage(
       WM_KEYDOWN, KeyboardCode::VKEY_RSHIFT,
diff --git a/ui/events/x/keyboard_hook_x11.cc b/ui/events/x/keyboard_hook_x11.cc
index 588124a..0df8bcb 100644
--- a/ui/events/x/keyboard_hook_x11.cc
+++ b/ui/events/x/keyboard_hook_x11.cc
@@ -156,7 +156,7 @@
 }  // namespace
 
 // static
-std::unique_ptr<KeyboardHook> KeyboardHook::Create(
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateModifierKeyboardHook(
     base::Optional<base::flat_set<DomCode>> dom_codes,
     gfx::AcceleratedWidget accelerated_widget,
     KeyboardHook::KeyEventCallback callback) {
@@ -169,4 +169,10 @@
   return keyboard_hook;
 }
 
+// static
+std::unique_ptr<KeyboardHook> KeyboardHook::CreateMediaKeyboardHook(
+    KeyEventCallback callback) {
+  return nullptr;
+}
+
 }  // namespace ui
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index 74c92999..4732f28 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -92,7 +92,6 @@
     "../../../externs/file_operation_progress_event.js",
     "../../../externs/launcher_search_provider.js",
     "../../../externs/platform.js",
-    "//third_party/analytics/externs.js",
   ]
 }
 
@@ -243,6 +242,7 @@
   testonly = true
   deps = [
     ":file_operation_manager",
+    "//ui/webui/resources/js/cr:event_target",
   ]
   externs_list = [ "../../../externs/background/file_operation_manager.js" ]
 }
@@ -254,6 +254,17 @@
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js/cr:event_target",
   ]
+  externs_list = [ "../../../externs/background/file_operation_manager.js" ]
+}
+
+js_unittest("file_operation_manager_unittest") {
+  deps = [
+    ":file_operation_manager",
+    ":metadata_proxy",
+    "//ui/file_manager/base/js:mock_chrome",
+    "//ui/file_manager/base/js:test_error_reporting",
+    "//ui/file_manager/file_manager/common/js:mock_entry",
+  ]
 }
 
 js_library("file_operation_util") {
@@ -477,6 +488,7 @@
     ":device_handler_unittest",
     ":drive_sync_handler_unittest",
     ":duplicate_finder_unittest",
+    ":file_operation_manager_unittest",
     ":import_history_unittest",
     ":media_scanner_unittest",
     ":task_queue_unittest",
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.html b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.html
deleted file mode 100644
index 7dd9c2b..0000000
--- a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<!-- Copyright 2014 The Chromium Authors. All rights reserved.
-  -- Use of this source code is governed by a BSD-style license that can be
-  -- found in the LICENSE file.
-  -->
-
-<html>
-<body>
-
-<script src="../../../../../ui/webui/resources/js/cr.js"></script>
-<script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
-
-<script src="../../common/js/async_util.js"></script>
-<script src="../../../base/js/test_error_reporting.js"></script>
-<script src="../../common/js/util.js"></script>
-<script src="../../common/js/lru_cache.js"></script>
-<script src="../../common/js/mock_entry.js"></script>
-<script src="metadata_proxy.js"></script>
-<script src="file_operation_manager.js"></script>
-<script src="file_operation_util.js"></script>
-
-<script src="file_operation_manager_unittest.js"></script>
-
-</body>
-</html>
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
index 19fc3a2..5e0cad7 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
@@ -4,50 +4,41 @@
 'use strict';
 
 /**
- * Mock of chrome.runtime.
+ * Mock chrome APIs.
  * @type {Object}
- * @const
  */
-chrome.runtime = {
+var mockChrome = {};
+
+mockChrome.runtime = {
   lastError: null
 };
 
-/**
- * Mock of chrome.power.
- * @type {Object}
- * @const
- */
-chrome.power = {
+mockChrome.power = {
   requestKeepAwake: function() {
-    chrome.power.keepAwakeRequested = true;
+    mockChrome.power.keepAwakeRequested = true;
   },
   releaseKeepAwake: function() {
-    chrome.power.keepAwakeRequested = false;
+    mockChrome.power.keepAwakeRequested = false;
   },
   keepAwakeRequested: false
 };
 
-/**
- * Mock of chrome.fileManagerPrivate.
- * @type {Object}
- * @const
- */
-chrome.fileManagerPrivate = {
+mockChrome.fileManagerPrivate = {
   onCopyProgress: {
     addListener: function(callback) {
-      chrome.fileManagerPrivate.onCopyProgress.listener_ = callback;
+      mockChrome.fileManagerPrivate.onCopyProgress.listener_ = callback;
     },
     removeListener: function() {
-      chrome.fileManagerPrivate.onCopyProgress.listener_ = null;
+      mockChrome.fileManagerPrivate.onCopyProgress.listener_ = null;
     },
     listener_: null
   }
 };
 
 /**
- * Logs events of file operation manager.
- * @param {!FileOperationManager} fileOperationManager A target file operation
- *     manager.
+ * Logs copy-progress events from a file operation manager.
+ * @param {!FileOperationManager} fileOperationManager The target file
+ *    operation manager.
  * @constructor
  * @struct
  */
@@ -61,11 +52,12 @@
 }
 
 /**
- * Handles copy-progress event.
+ * Log file operation manager copy-progress event details.
  * @param {Event} event An event.
  * @private
  */
 EventLogger.prototype.onCopyProgress_ = function(event) {
+  event = /** @type {FileOperationProgressEvent} */ (event);
   if (event.reason === 'BEGIN') {
     this.events.push(event);
     this.numberOfBeginEvents++;
@@ -85,7 +77,7 @@
  * @param {string} blockedDestination Destination url of an entry whose request
  *     should be blocked.
  * @param {!Entry} sourceEntry Source entry. Single source entry is supported.
- * @param {!Array<!FakeFileSystem>} fileSystems File systems.
+ * @param {!Array<!MockFileSystem>} fileSystems File systems array.
  * @constructor
  * @struct
  */
@@ -98,7 +90,7 @@
 }
 
 /**
- * A fake implemencation of startCopy function.
+ * Fake implementation of startCopy function.
  * @param {!Entry} source
  * @param {!Entry} destination
  * @param {string} newName
@@ -118,7 +110,9 @@
     var newPath = joinPath('/', newName);
     var fileSystem = getFileSystemForURL(
         this.fileSystems_, destination.toURL());
-    fileSystem.entries[newPath] = this.sourceEntry_.clone(newPath);
+    var mockEntry = /** @type {!MockEntry} */ (this.sourceEntry_);
+    fileSystem.entries[newPath] =
+        /** @type {!MockEntry} */ (mockEntry.clone(newPath));
     listener(copyId, makeStatus('end_copy_entry'));
     listener(copyId, makeStatus('success'));
   }.bind(this);
@@ -126,7 +120,7 @@
   this.startCopyId_++;
 
   callback(this.startCopyId_);
-  var listener = chrome.fileManagerPrivate.onCopyProgress.listener_;
+  var listener = mockChrome.fileManagerPrivate.onCopyProgress.listener_;
   listener(this.startCopyId_, makeStatus('begin_copy_entry'));
   listener(this.startCopyId_, makeStatus('progress'));
 
@@ -141,14 +135,14 @@
 /**
  * Fake volume manager.
  * @constructor
- * @structs
+ * @struct
  */
 function FakeVolumeManager() {}
 
 /**
  * Returns fake volume info.
  * @param {!Entry} entry
- * @return {VolumeInfo} A fake volume info.
+ * @return {!Object}
  */
 FakeVolumeManager.prototype.getVolumeInfo = function(entry) {
   return { volumeId: entry.filesystem.name };
@@ -156,16 +150,18 @@
 
 /**
  * Returns file system of the url.
- * @param {!Array<!FakeFileSystem>} fileSystems
+ * @param {!Array<!MockFileSystem>} fileSystems
  * @param {string} url
- * @return {!FakeFileSystem}
+ * @return {!MockFileSystem}
  */
 function getFileSystemForURL(fileSystems, url) {
   for (var i = 0; i < fileSystems.length; i++) {
-    if (new RegExp('^filesystem:' + fileSystems[i].name + '/').test(url))
+    if (new RegExp('^filesystem:' + fileSystems[i].name + '/').test(url)) {
       return fileSystems[i];
+    }
   }
-  throw new Error('Unexpected url: ' + url + '.');
+
+  throw new Error('Unexpected url: ' + url);
 }
 
 /**
@@ -177,9 +173,10 @@
 
 /**
  * Creates test file system.
- * @param {string} id File system ID.
- * @param {Object<number>} entries Map of entries' paths and their size.
- *     If the size is equals to DIRECTORY_SIZE, the entry is directory.
+ * @param {string} id File system Id.
+ * @param {Object<number>} entries Map of entry paths and their size.
+ *     If the entry size is DIRECTORY_SIZE, the entry is a directory.
+ * @return {!MockFileSystem}
  */
 function createTestFileSystem(id, entries) {
   var fileSystem = new MockFileSystem(id, 'filesystem:' + id);
@@ -187,8 +184,8 @@
     if (entries[path] === DIRECTORY_SIZE) {
       fileSystem.entries[path] = new MockDirectoryEntry(fileSystem, path);
     } else {
-      fileSystem.entries[path] =
-          new MockFileEntry(fileSystem, path, {size: entries[path]});
+      var metadata = /** @type {!Metadata} */ ({size: entries[path]});
+      fileSystem.entries[path] = new MockFileEntry(fileSystem, path, metadata);
     }
   }
   return fileSystem;
@@ -196,12 +193,12 @@
 
 /**
  * Resolves URL on the file system.
- * @param {FakeFileSystem} fileSystem Fake file system.
+ * @param {!MockFileSystem} fileSystem File system.
  * @param {string} url URL.
- * @param {function(MockEntry)} success Success callback.
- * @param {function()} failure Failure callback.
+ * @param {function(!Entry)} success Success callback.
+ * @param {function(!FileError)=} opt_failure Failure callback.
  */
-function resolveTestFileSystemURL(fileSystem, url, success, failure) {
+function resolveTestFileSystemURL(fileSystem, url, success, opt_failure) {
   for (var name in fileSystem.entries) {
     var entry = fileSystem.entries[name];
     if (entry.toURL() == url) {
@@ -209,7 +206,10 @@
       return;
     }
   }
-  failure();
+
+  if (opt_failure) {
+    opt_failure(new FileError());
+  }
 }
 
 /**
@@ -221,14 +221,17 @@
   return new Promise(function(fulfill) {
     var events = [];
     fileOperationManager.addEventListener('copy-progress', function(event) {
+      event = /** @type {FileOperationProgressEvent} */ (event);
       events.push(event);
       if (event.reason === 'SUCCESS')
         fulfill(events);
     });
     fileOperationManager.addEventListener('entries-changed', function(event) {
+      event = /** @type {FileOperationProgressEvent} */ (event);
       events.push(event);
     });
     fileOperationManager.addEventListener('delete', function(event) {
+      event = /** @type {FileOperationProgressEvent} */ (event);
       events.push(event);
       if (event.reason === 'SUCCESS')
         fulfill(events);
@@ -245,9 +248,9 @@
 var volumeManagerFactory = {};
 
 /**
- * Provide VolumeManager.getInstande() for FileOperationManager using mocked
+ * Provide VolumeManager.getInstance() for FileOperationManager using mocked
  * volume manager instance.
- * @type {!Promise<(FakeVolumeManager|{getVolumeInfo: function()}?)>}
+ * @return {Promise}
  */
 volumeManagerFactory.getInstance = function() {
   return Promise.resolve(volumeManager);
@@ -263,12 +266,13 @@
  * Initializes the test environment.
  */
 function setUp() {
+  // Install mock chrome APIs.
+  installMockChrome(mockChrome);
 }
 
 /**
  * Tests the fileOperationUtil.resolvePath function.
- * @param {function(boolean:hasError)} callback Callback to be passed true on
- *     error.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testResolvePath(callback) {
   var fileSystem = createTestFileSystem('testVolume', {
@@ -299,8 +303,7 @@
 }
 
 /**
- * @param {function(boolean)} callback Callback to be passed true on
- *     error.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testFindEntriesRecursively(callback) {
   var fileSystem = createTestFileSystem('testVolume', {
@@ -319,22 +322,24 @@
   });
 
   var foundFiles = [];
-  fileOperationUtil.findEntriesRecursively(
-      fileSystem.root,
-      function(fileEntry) {
-        foundFiles.push(fileEntry);
-      })
-      .then(
-          function() {
-            assertEquals(12, foundFiles.length);
-            callback(false);
+  fileOperationUtil
+      .findEntriesRecursively(
+          fileSystem.root,
+          function(fileEntry) {
+            foundFiles.push(fileEntry);
           })
-      .catch(callback);
+      .then(function() {
+        assertEquals(12, foundFiles.length);
+        callback(false);
+      })
+      .catch(function() {
+        var error = true;
+        callback(error);
+      });
 }
 
 /**
- * @param {function(boolean)} callback Callback to be passed true on
- *     error.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testFindFilesRecursively(callback) {
   var fileSystem = createTestFileSystem('testVolume', {
@@ -353,11 +358,12 @@
   });
 
   var foundFiles = [];
-  fileOperationUtil.findFilesRecursively(
-      fileSystem.root,
-      function(fileEntry) {
-        foundFiles.push(fileEntry);
-      })
+  fileOperationUtil
+      .findFilesRecursively(
+          fileSystem.root,
+          function(fileEntry) {
+            foundFiles.push(fileEntry);
+          })
       .then(
           function() {
             assertEquals(10, foundFiles.length);
@@ -367,12 +373,14 @@
                 });
             callback(false);
           })
-      .catch(callback);
+      .catch(function() {
+        var error = true;
+        callback(error);
+      });
 }
 
 /**
- * @param {function(boolean)} callback Callback to be passed true on
- *     error.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testGatherEntriesRecursively(callback) {
   var fileSystem = createTestFileSystem('testVolume', {
@@ -391,18 +399,19 @@
   });
 
   fileOperationUtil.gatherEntriesRecursively(fileSystem.root)
-      .then(
-          function(gatheredFiles) {
-            assertEquals(12, gatheredFiles.length);
-            callback(false);
-          })
-      .catch(callback);
+      .then(function(gatheredFiles) {
+        assertEquals(12, gatheredFiles.length);
+        callback(false);
+      })
+      .catch(function() {
+        var error = true;
+        callback(error);
+      });
 }
 
 /**
  * Tests the fileOperationUtil.deduplicatePath
- * @param {function(boolean:hasError)} callback Callback to be passed true on
- *     error.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testDeduplicatePath(callback) {
   var fileSystem1 = createTestFileSystem('testVolume', {'/': DIRECTORY_SIZE});
@@ -449,9 +458,8 @@
 }
 
 /**
- * Tests the fileOperationUtil.paste.
- * @param {function(boolean:hasError)} callback Callback to be passed true on
- *     error.
+ * Tests fileOperationManager copy.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testCopy(callback) {
   // Prepare entries and their resolver.
@@ -459,33 +467,36 @@
     '/': DIRECTORY_SIZE,
     '/test.txt': 10,
   });
-  window.webkitResolveLocalFileSystemURL =
-      resolveTestFileSystemURL.bind(null, fileSystem);
+  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+    resolveTestFileSystemURL(fileSystem, url, success, failure);
+  };
 
-  chrome.fileManagerPrivate.startCopy =
-      function(source, destination, newName, callback) {
-        var makeStatus = function(type) {
-          return {
-            type: type,
-            sourceUrl: source.toURL(),
-            destinationUrl: destination.toURL()
-          };
-        };
-        callback(1);
-        var listener = chrome.fileManagerPrivate.onCopyProgress.listener_;
-        listener(1, makeStatus('begin_copy_entry'));
-        listener(1, makeStatus('progress'));
-        var newPath = joinPath('/', newName);
-        fileSystem.entries[newPath] =
-            fileSystem.entries['/test.txt'].clone(newPath);
-        listener(1, makeStatus('end_copy_entry'));
-        listener(1, makeStatus('success'));
+  mockChrome.fileManagerPrivate.startCopy = function(
+      source, destination, newName, callback) {
+    var makeStatus = function(type) {
+      return {
+        type: type,
+        sourceUrl: source.toURL(),
+        destinationUrl: destination.toURL()
       };
+    };
+    callback(1);
+    var listener = mockChrome.fileManagerPrivate.onCopyProgress.listener_;
+    listener(1, makeStatus('begin_copy_entry'));
+    listener(1, makeStatus('progress'));
+    var newPath = joinPath('/', newName);
+    var entry = /** @type {!MockEntry} */
+        (fileSystem.entries['/test.txt']);
+    fileSystem.entries[newPath] =
+        /** @type {!MockEntry} */ (entry.clone(newPath));
+    listener(1, makeStatus('end_copy_entry'));
+    listener(1, makeStatus('success'));
+  };
 
   volumeManager = new FakeVolumeManager();
   fileOperationManager = new FileOperationManagerImpl();
 
-  // Observing manager's events.
+  // Observe the file operation manager's events.
   var eventsPromise = waitForEvents(fileOperationManager);
 
   // Verify the events.
@@ -515,40 +526,40 @@
 
   fileOperationManager.paste(
       [fileSystem.entries['/test.txt']],
-      fileSystem.entries['/'],
-      false);
+      /** @type {!DirectoryEntry} */ (fileSystem.entries['/']), false);
 }
 
 /**
- * Tests the fileOperationUtil.paste for copying files in sequential. When
- * destination volumes are same, copy operations should run in sequential.
+ * Tests copying files when the destination volumes are same: the copy
+ * operations should be run sequentially.
  */
 function testCopyInSequential(callback) {
+  // Prepare entries and their resolver.
   var fileSystem = createTestFileSystem('testVolume', {
     '/': DIRECTORY_SIZE,
     '/dest': DIRECTORY_SIZE,
     '/test.txt': 10
   });
-
-  window.webkitResolveLocalFileSystemURL =
-      resolveTestFileSystemURL.bind(null, fileSystem);
+  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+    resolveTestFileSystemURL(fileSystem, url, success, failure);
+  };
 
   var blockableFakeStartCopy = new BlockableFakeStartCopy(
-      'filesystem:testVolume/dest',
-      fileSystem.entries['/test.txt'],
+      'filesystem:testVolume/dest', fileSystem.entries['/test.txt'],
       [fileSystem]);
-  chrome.fileManagerPrivate.startCopy =
+  mockChrome.fileManagerPrivate.startCopy =
       blockableFakeStartCopy.startCopyFunc.bind(blockableFakeStartCopy);
 
   volumeManager = new FakeVolumeManager();
   fileOperationManager = new FileOperationManagerImpl();
 
+  // Observe the file operation manager's events.
   var eventLogger = new EventLogger(fileOperationManager);
 
-  // Copy test.txt to /dest. This operation will be blocked.
-  fileOperationManager.paste([fileSystem.entries['/test.txt']],
-      fileSystem.entries['/dest'],
-      false);
+  // Copy test.txt to /dest. This operation should be blocked.
+  fileOperationManager.paste(
+      [fileSystem.entries['/test.txt']],
+      /** @type {!DirectoryEntry} */ (fileSystem.entries['/dest']), false);
 
   var firstOperationTaskId;
   reportPromise(waitUntil(function() {
@@ -560,9 +571,9 @@
     firstOperationTaskId = eventLogger.events[0].taskId;
 
     // Copy test.txt to /. This operation should be blocked.
-    fileOperationManager.paste([fileSystem.entries['/test.txt']],
-        fileSystem.entries['/'],
-        false);
+    fileOperationManager.paste(
+        [fileSystem.entries['/test.txt']],
+        /** @type {!DirectoryEntry} */ (fileSystem.entries['/']), false);
 
     return waitUntil(function() {
       return fileOperationManager.getPendingCopyTasksForTesting().length === 1;
@@ -600,10 +611,11 @@
 }
 
 /**
- * Tests the fileOperationUtil.paste for copying files in paralell. When
- * destination volumes are different, copy operations can run in paralell.
+ * Tests copying files when the destination volumes are different: the copy
+ * operations should be run in parallel.
  */
 function testCopyInParallel(callback) {
+  // Prepare entries and their resolver.
   var fileSystemA = createTestFileSystem('volumeA', {
     '/': DIRECTORY_SIZE,
     '/test.txt': 10
@@ -614,26 +626,27 @@
   var fileSystems = [fileSystemA, fileSystemB];
 
   window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
-    return resolveTestFileSystemURL(
-        getFileSystemForURL(fileSystems, url), url, success, failure);
+    var system = getFileSystemForURL(fileSystems, url);
+    resolveTestFileSystemURL(system, url, success, failure);
   };
 
   var blockableFakeStartCopy = new BlockableFakeStartCopy(
       'filesystem:volumeB/',
       fileSystemA.entries['/test.txt'],
       fileSystems);
-  chrome.fileManagerPrivate.startCopy =
+  mockChrome.fileManagerPrivate.startCopy =
       blockableFakeStartCopy.startCopyFunc.bind(blockableFakeStartCopy);
 
   volumeManager = new FakeVolumeManager();
   fileOperationManager = new FileOperationManagerImpl();
 
+  // Observe the file operation manager's events.
   var eventLogger = new EventLogger(fileOperationManager);
 
   // Copy test.txt from volume A to volume B.
-  fileOperationManager.paste([fileSystemA.entries['/test.txt']],
-      fileSystemB.entries['/'],
-      false);
+  fileOperationManager.paste(
+      [fileSystemA.entries['/test.txt']],
+      /** @type {!DirectoryEntry} */ (fileSystemB.entries['/']), false);
 
   var firstOperationTaskId;
   reportPromise(waitUntil(function() {
@@ -645,9 +658,9 @@
 
     // Copy test.txt from volume A to volume A. This should not be blocked by
     // the previous operation.
-    fileOperationManager.paste([fileSystemA.entries['/test.txt']],
-        fileSystemA.entries['/'],
-        false);
+    fileOperationManager.paste(
+        [fileSystemA.entries['/test.txt']],
+        /** @type {!DirectoryEntry} */ (fileSystemA.entries['/']), false);
 
     // Wait until the second operation is completed.
     return waitUntil(function() {
@@ -681,9 +694,11 @@
 }
 
 /**
- * Test case that a copy fails since destination volume is not available.
+ * Tests that copy operations fail when the destination volume is not
+ * available.
  */
 function testCopyFails(callback) {
+  // Prepare entries.
   var fileSystem = createTestFileSystem('testVolume', {
     '/': DIRECTORY_SIZE,
     '/test.txt': 10
@@ -692,18 +707,19 @@
   volumeManager = {
     /* Mocking volume manager. */
     getVolumeInfo: function() {
-      // Return null to simulate that the volume info is not available.
+      // Returns null to indicate that the volume is not available.
       return null;
     }
   };
   fileOperationManager = new FileOperationManagerImpl();
 
+  // Observe the file operation manager's events.
   var eventLogger = new EventLogger(fileOperationManager);
 
-  // Copy test.txt to /.
-  fileOperationManager.paste([fileSystem.entries['/test.txt']],
-      fileSystem.entries['/'],
-      false);
+  // Copy test.txt to /, which should fail.
+  fileOperationManager.paste(
+      [fileSystem.entries['/test.txt']],
+      /** @type {!DirectoryEntry} */ (fileSystem.entries['/']), false);
 
   reportPromise(waitUntil(function() {
     return eventLogger.numberOfErrorEvents === 1;
@@ -723,8 +739,7 @@
 
 /**
  * Tests the fileOperationUtil.paste for move.
- * @param {function(boolean:hasError)} callback Callback to be passed true on
- *     error.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testMove(callback) {
   // Prepare entries and their resolver.
@@ -733,13 +748,14 @@
     '/directory': DIRECTORY_SIZE,
     '/test.txt': 10,
   });
-  window.webkitResolveLocalFileSystemURL =
-      resolveTestFileSystemURL.bind(null, fileSystem);
+  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+    resolveTestFileSystemURL(fileSystem, url, success, failure);
+  };
 
   volumeManager = new FakeVolumeManager();
   fileOperationManager = new FileOperationManagerImpl();
 
-  // Observing manager's events.
+  // Observe the file operation manager's events.
   var eventsPromise = waitForEvents(fileOperationManager);
 
   // Verify the events.
@@ -775,14 +791,12 @@
 
   fileOperationManager.paste(
       [fileSystem.entries['/test.txt']],
-      fileSystem.entries['/directory'],
-      true);
+      /** @type {!DirectoryEntry} */ (fileSystem.entries['/directory']), true);
 }
 
 /**
- * Tests the fileOperationUtil.deleteEntries.
- * @param {function(boolean:hasError)} callback Callback to be passed true on
- *     error.
+ * Tests fileOperationManager.deleteEntries.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testDelete(callback) {
   // Prepare entries and their resolver.
@@ -790,34 +804,36 @@
     '/': DIRECTORY_SIZE,
     '/test.txt': 10,
   });
-  window.webkitResolveLocalFileSystemURL =
-      resolveTestFileSystemURL.bind(null, fileSystem);
+  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+    resolveTestFileSystemURL(fileSystem, url, success, failure);
+  };
 
   // Observing manager's events.
-  reportPromise(waitForEvents(fileOperationManager).then(function(events) {
-    assertEquals('delete', events[0].type);
-    assertEquals('BEGIN', events[0].reason);
-    assertEquals(10, events[0].totalBytes);
-    assertEquals(0, events[0].processedBytes);
+  reportPromise(
+      waitForEvents(fileOperationManager).then(function(events) {
+        assertEquals('delete', events[0].type);
+        assertEquals('BEGIN', events[0].reason);
+        assertEquals(10, events[0].totalBytes);
+        assertEquals(0, events[0].processedBytes);
 
-    var lastEvent = events[events.length - 1];
-    assertEquals('delete', lastEvent.type);
-    assertEquals('SUCCESS', lastEvent.reason);
-    assertEquals(10, lastEvent.totalBytes);
-    assertEquals(10, lastEvent.processedBytes);
+        var lastEvent = events[events.length - 1];
+        assertEquals('delete', lastEvent.type);
+        assertEquals('SUCCESS', lastEvent.reason);
+        assertEquals(10, lastEvent.totalBytes);
+        assertEquals(10, lastEvent.processedBytes);
 
-    assertFalse(events.some(function(event) {
-      return event.type === 'copy-progress';
-    }));
-  }), callback);
+        assertFalse(events.some(function(event) {
+          return event.type === 'copy-progress';
+        }));
+      }),
+      callback);
 
   fileOperationManager.deleteEntries([fileSystem.entries['/test.txt']]);
 }
 
 /**
- * Tests the fileOperationUtil.zipSelection.
- * @param {function(boolean:hasError)} callback Callback to be passed true on
- *     error.
+ * Tests fileOperationManager.zipSelection.
+ * @param {function(boolean)} callback Callback to be passed true on error.
  */
 function testZip(callback) {
   // Prepare entries and their resolver.
@@ -825,12 +841,15 @@
     '/': DIRECTORY_SIZE,
     '/test.txt': 10,
   });
-  window.webkitResolveLocalFileSystemURL =
-      resolveTestFileSystemURL.bind(null, fileSystem);
-  chrome.fileManagerPrivate.zipSelection = function(
+  window.webkitResolveLocalFileSystemURL = function(url, success, failure) {
+    resolveTestFileSystemURL(fileSystem, url, success, failure);
+  };
+
+  mockChrome.fileManagerPrivate.zipSelection = function(
       sources, parent, newName, success, error) {
     var newPath = joinPath('/', newName);
-    var newEntry = new MockFileEntry(fileSystem, newPath, {size: 10});
+    var newEntry = new MockFileEntry(
+        fileSystem, newPath, /** @type {!Metadata} */ ({size: 10}));
     fileSystem.entries[newPath] = newEntry;
     success(newEntry);
   };
@@ -862,5 +881,6 @@
   }), callback);
 
   fileOperationManager.zipSelection(
-      [fileSystem.entries['/test.txt']], fileSystem.entries['/']);
+      [fileSystem.entries['/test.txt']],
+      /** @type {!DirectoryEntry} */ (fileSystem.entries['/']));
 }
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
index 7726ddd..e669548 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
@@ -11,7 +11,6 @@
   <script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
   <script src="../../../../../ui/webui/resources/js/cr/ui/array_data_model.js"></script>
   <script src="../../../../../ui/webui/resources/js/load_time_data.js"></script>
-  <script src="../../../../../third_party/analytics/google-analytics-bundle.js"></script>
 
   <script src="../../common/js/async_util.js"></script>
   <script src="../../common/js/metrics_base.js"></script>
diff --git a/ui/file_manager/file_manager/common/js/BUILD.gn b/ui/file_manager/file_manager/common/js/BUILD.gn
index 9fcc526..6b93293 100644
--- a/ui/file_manager/file_manager/common/js/BUILD.gn
+++ b/ui/file_manager/file_manager/common/js/BUILD.gn
@@ -87,7 +87,6 @@
     "//ui/file_manager/base/js:volume_manager_types",
   ]
   externs_list = [
-    "//third_party/analytics/externs.js",
     "../../../externs/background_window.js",
     "../../../externs/background/file_browser_background.js",
   ]
diff --git a/ui/file_manager/file_manager/common/js/metrics_unittest.html b/ui/file_manager/file_manager/common/js/metrics_unittest.html
index 8e0ab645..7e1816d 100644
--- a/ui/file_manager/file_manager/common/js/metrics_unittest.html
+++ b/ui/file_manager/file_manager/common/js/metrics_unittest.html
@@ -11,7 +11,6 @@
   <script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
   <script src="../../../../../ui/webui/resources/js/cr/ui/array_data_model.js"></script>
   <script src="../../../../../ui/webui/resources/js/load_time_data.js"></script>
-  <script src="../../../../../third_party/analytics/google-analytics-bundle.js"></script>
   <script src="../../../../file_manager/base/js/test_error_reporting.js"></script>
   <script src="util.js"></script>
   <script src="metrics_base.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model.js b/ui/file_manager/file_manager/foreground/js/actions_model.js
index 1a6bc3e9..9aa4979f 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model.js
@@ -120,7 +120,9 @@
   const canShareItem = metadata[0].canShare !== false;
   return this.volumeManager_.getDriveConnectionState().type !==
       VolumeManagerCommon.DriveConnectionType.OFFLINE &&
-      !util.isTeamDriveRoot(this.entry_) && canShareItem;
+      (loadTimeData.getBoolean('DRIVE_FS_ENABLED') ||
+       !util.isTeamDriveRoot(this.entry_)) &&
+      canShareItem;
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
index 43337a5f7..c7920e64 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
@@ -6,8 +6,6 @@
 
 <html>
 <body>
-  <script src="../../../../../third_party/analytics/google-analytics-bundle.js"></script>
-
   <script src="../../../../../ui/webui/resources/js/cr.js"></script>
   <script src="../../../../../ui/webui/resources/js/assert.js"></script>
   <script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index fbcf6f7..c3cac15 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -73,7 +73,6 @@
     "../../../../externs/paper_elements.js",
     "../../../../externs/platform.js",
     "../../../../externs/search_item.js",
-    "//third_party/analytics/externs.js",
   ]
 }
 
@@ -185,9 +184,7 @@
     "../metadata:metadata_model",
     "//ui/webui/resources/js/cr/ui:grid",
   ]
-  externs_list = [
-    "../../../../externs/background/import_history.js",
-  ]
+  externs_list = [ "../../../../externs/background/import_history.js" ]
 }
 
 js_library("file_list_selection_model") {
@@ -266,9 +263,7 @@
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js/cr/ui:table",
   ]
-  externs_list = [
-    "../../../../externs/background/import_history.js",
-  ]
+  externs_list = [ "../../../../externs/background/import_history.js" ]
 }
 
 js_unittest("file_table_unittest") {
diff --git a/ui/file_manager/file_manager/test/BUILD.gn b/ui/file_manager/file_manager/test/BUILD.gn
index 13fdd928..6f5cffa3 100644
--- a/ui/file_manager/file_manager/test/BUILD.gn
+++ b/ui/file_manager/file_manager/test/BUILD.gn
@@ -63,7 +63,6 @@
     "//ui/file_manager/externs/volume_info.js",
     "//ui/file_manager/externs/volume_info_list.js",
     "//ui/file_manager/externs/volume_manager.js",
-    "//third_party/analytics/externs.js",
   ]
 }
 
diff --git a/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js b/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js
index f30664f4..872cab6 100644
--- a/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js
+++ b/ui/file_manager/integration_tests/file_manager/share_and_manage_dialog.js
@@ -24,18 +24,20 @@
     // Navigate to the specified team drive if one is specified.
     function(results) {
       appId = results.windowId;
-      if (!teamDrive) {
+      if (teamDrive === undefined) {
         this.next();
         return;
       }
       remoteCall
           .navigateWithDirectoryTree(
-              appId, `/team_drives/${teamDrive}`, 'Team Drives', 'drive')
+              appId,
+              teamDrive === '' ? '/team_drives' : `/team_drives/${teamDrive}`,
+              'Team Drives', 'drive')
           .then(this.next);
     },
     // Wait for the file list to update if we navigated.
     function() {
-      if (!teamDrive) {
+      if (teamDrive === undefined) {
         this.next();
         return;
       }
@@ -359,3 +361,12 @@
   const URL = 'https://folder_alternate_link/Team%20Drive%20A';
   manageWithDriveExpectBrowserURL('Team Drive A', URL, '');
 };
+
+/**
+ * Tests sharing a team drive.
+ */
+testcase.shareTeamDrive = function() {
+  const URL =
+      'https://folder_alternate_link/Team%20Drive%20A?userstoinvite=%22%22';
+  shareWithOthersExpectBrowserURL('Team Drive A', URL, '');
+};
diff --git a/ui/gfx/client_native_pixmap_factory.h b/ui/gfx/client_native_pixmap_factory.h
index e17e984b..d7e3f65 100644
--- a/ui/gfx/client_native_pixmap_factory.h
+++ b/ui/gfx/client_native_pixmap_factory.h
@@ -25,10 +25,6 @@
  public:
   virtual ~ClientNativePixmapFactory() {}
 
-  // Returns true if format/usage configuration is supported.
-  virtual bool IsConfigurationSupported(gfx::BufferFormat format,
-                                        gfx::BufferUsage usage) const = 0;
-
   // Import the native pixmap from |handle| to be used in non-GPU processes.
   // This function takes ownership of any file descriptors in |handle|.
   virtual std::unique_ptr<ClientNativePixmap> ImportFromHandle(
diff --git a/ui/gfx/linux/client_native_pixmap_dmabuf.cc b/ui/gfx/linux/client_native_pixmap_dmabuf.cc
index 6611dcfef..0a8ecf0 100644
--- a/ui/gfx/linux/client_native_pixmap_dmabuf.cc
+++ b/ui/gfx/linux/client_native_pixmap_dmabuf.cc
@@ -16,6 +16,7 @@
 #include "base/process/memory.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
 #include <linux/dma-buf.h>
@@ -59,6 +60,68 @@
 }  // namespace
 
 // static
+bool ClientNativePixmapDmaBuf::IsConfigurationSupported(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) {
+  switch (usage) {
+    case gfx::BufferUsage::GPU_READ:
+      return format == gfx::BufferFormat::BGR_565 ||
+             format == gfx::BufferFormat::RGBA_8888 ||
+             format == gfx::BufferFormat::RGBX_8888 ||
+             format == gfx::BufferFormat::BGRA_8888 ||
+             format == gfx::BufferFormat::BGRX_8888 ||
+             format == gfx::BufferFormat::YVU_420;
+    case gfx::BufferUsage::SCANOUT:
+      return format == gfx::BufferFormat::BGRX_8888 ||
+             format == gfx::BufferFormat::RGBX_8888 ||
+             format == gfx::BufferFormat::RGBA_8888 ||
+             format == gfx::BufferFormat::BGRA_8888;
+    case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE:
+      return
+#if defined(ARCH_CPU_X86_FAMILY)
+          // Currently only Intel driver (i.e. minigbm and Mesa) supports R_8
+          // RG_88, NV12 and XB30. https://crbug.com/356871
+          format == gfx::BufferFormat::R_8 ||
+          format == gfx::BufferFormat::RG_88 ||
+          format == gfx::BufferFormat::YUV_420_BIPLANAR ||
+          format == gfx::BufferFormat::RGBX_1010102 ||
+#endif
+
+          format == gfx::BufferFormat::BGRX_8888 ||
+          format == gfx::BufferFormat::BGRA_8888 ||
+          format == gfx::BufferFormat::RGBX_8888 ||
+          format == gfx::BufferFormat::RGBA_8888;
+    case gfx::BufferUsage::SCANOUT_VDA_WRITE:
+      return false;
+
+    case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
+    case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT:
+      return
+#if defined(ARCH_CPU_X86_FAMILY)
+          // Currently only Intel driver (i.e. minigbm and
+          // Mesa) supports R_8 RG_88 and NV12.
+          // https://crbug.com/356871
+          format == gfx::BufferFormat::R_8 ||
+          format == gfx::BufferFormat::RG_88 ||
+          format == gfx::BufferFormat::YUV_420_BIPLANAR ||
+#endif
+          format == gfx::BufferFormat::BGRA_8888;
+    case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE:
+      // Each platform only supports one camera buffer type. We list the
+      // supported buffer formats on all platforms here. When allocating a
+      // camera buffer the caller is responsible for making sure a buffer is
+      // successfully allocated. For example, allocating YUV420_BIPLANAR
+      // for SCANOUT_CAMERA_READ_WRITE may only work on Intel boards.
+      return format == gfx::BufferFormat::YUV_420_BIPLANAR;
+    case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE:
+      // R_8 is used as the underlying pixel format for BLOB buffers.
+      return format == gfx::BufferFormat::R_8;
+  }
+  NOTREACHED();
+  return false;
+}
+
+// static
 std::unique_ptr<gfx::ClientNativePixmap>
 ClientNativePixmapDmaBuf::ImportFromDmabuf(
     const gfx::NativePixmapHandle& handle,
diff --git a/ui/gfx/linux/client_native_pixmap_dmabuf.h b/ui/gfx/linux/client_native_pixmap_dmabuf.h
index b8f5630e..f5da2b3 100644
--- a/ui/gfx/linux/client_native_pixmap_dmabuf.h
+++ b/ui/gfx/linux/client_native_pixmap_dmabuf.h
@@ -11,14 +11,19 @@
 
 #include "base/files/scoped_file.h"
 #include "base/macros.h"
+#include "ui/gfx/buffer_types.h"
 #include "ui/gfx/client_native_pixmap.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gfx_export.h"
 #include "ui/gfx/native_pixmap_handle.h"
 
 namespace gfx {
 
 class ClientNativePixmapDmaBuf : public gfx::ClientNativePixmap {
  public:
+  static GFX_EXPORT bool IsConfigurationSupported(gfx::BufferFormat format,
+                                                  gfx::BufferUsage usage);
+
   static std::unique_ptr<gfx::ClientNativePixmap> ImportFromDmabuf(
       const gfx::NativePixmapHandle& handle,
       const gfx::Size& size);
diff --git a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc
index 70c287b..6b0aaa84 100644
--- a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc
+++ b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc
@@ -45,80 +45,9 @@
 
 class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory {
  public:
-  explicit ClientNativePixmapFactoryDmabuf(
-      bool supports_native_pixmap_import_from_dmabuf)
-      : supports_native_pixmap_import_from_dmabuf_(
-            supports_native_pixmap_import_from_dmabuf) {}
+  explicit ClientNativePixmapFactoryDmabuf() {}
   ~ClientNativePixmapFactoryDmabuf() override {}
 
-  // ClientNativePixmapFactory:
-  bool IsConfigurationSupported(gfx::BufferFormat format,
-                                gfx::BufferUsage usage) const override {
-    switch (usage) {
-      case gfx::BufferUsage::GPU_READ:
-        return format == gfx::BufferFormat::BGR_565 ||
-               format == gfx::BufferFormat::RGBA_8888 ||
-               format == gfx::BufferFormat::RGBX_8888 ||
-               format == gfx::BufferFormat::BGRA_8888 ||
-               format == gfx::BufferFormat::BGRX_8888 ||
-               format == gfx::BufferFormat::YVU_420;
-      case gfx::BufferUsage::SCANOUT:
-        return format == gfx::BufferFormat::BGRX_8888 ||
-               format == gfx::BufferFormat::RGBX_8888 ||
-               format == gfx::BufferFormat::RGBA_8888 ||
-               format == gfx::BufferFormat::BGRA_8888;
-      case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE:
-        return
-#if defined(ARCH_CPU_X86_FAMILY)
-            // Currently only Intel driver (i.e. minigbm and Mesa) supports R_8
-            // RG_88, NV12 and XB30. https://crbug.com/356871
-            format == gfx::BufferFormat::R_8 ||
-            format == gfx::BufferFormat::RG_88 ||
-            format == gfx::BufferFormat::YUV_420_BIPLANAR ||
-            format == gfx::BufferFormat::RGBX_1010102 ||
-#endif
-
-            format == gfx::BufferFormat::BGRX_8888 ||
-            format == gfx::BufferFormat::BGRA_8888 ||
-            format == gfx::BufferFormat::RGBX_8888 ||
-            format == gfx::BufferFormat::RGBA_8888;
-      case gfx::BufferUsage::SCANOUT_VDA_WRITE:
-        return false;
-      case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
-      case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: {
-        if (!supports_native_pixmap_import_from_dmabuf_)
-          return false;
-        return
-#if defined(ARCH_CPU_X86_FAMILY)
-            // Currently only Intel driver (i.e. minigbm and
-            // Mesa) supports R_8 RG_88 and NV12.
-            // https://crbug.com/356871
-            format == gfx::BufferFormat::R_8 ||
-            format == gfx::BufferFormat::RG_88 ||
-            format == gfx::BufferFormat::YUV_420_BIPLANAR ||
-#endif
-            format == gfx::BufferFormat::BGRA_8888;
-      }
-      case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: {
-        if (!supports_native_pixmap_import_from_dmabuf_)
-          return false;
-        // Each platform only supports one camera buffer type. We list the
-        // supported buffer formats on all platforms here. When allocating a
-        // camera buffer the caller is responsible for making sure a buffer is
-        // successfully allocated. For example, allocating YUV420_BIPLANAR
-        // for SCANOUT_CAMERA_READ_WRITE may only work on Intel boards.
-        return format == gfx::BufferFormat::YUV_420_BIPLANAR;
-      }
-      case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE: {
-        if (!supports_native_pixmap_import_from_dmabuf_)
-          return false;
-        // R_8 is used as the underlying pixel format for BLOB buffers.
-        return format == gfx::BufferFormat::R_8;
-      }
-    }
-    NOTREACHED();
-    return false;
-  }
   std::unique_ptr<ClientNativePixmap> ImportFromHandle(
       const gfx::NativePixmapHandle& handle,
       const gfx::Size& size,
@@ -130,11 +59,7 @@
       case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT:
       case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE:
       case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE:
-        if (supports_native_pixmap_import_from_dmabuf_)
-          return ClientNativePixmapDmaBuf::ImportFromDmabuf(handle, size);
-        NOTREACHED()
-            << "Native GpuMemoryBuffers are not supported on this platform";
-        return nullptr;
+        return ClientNativePixmapDmaBuf::ImportFromDmabuf(handle, size);
       case gfx::BufferUsage::GPU_READ:
       case gfx::BufferUsage::SCANOUT:
       case gfx::BufferUsage::SCANOUT_VDA_WRITE:
@@ -148,30 +73,11 @@
   }
 
  private:
-  // Says if ClientNativePixmapDmaBuf can be used to import handle from dmabuf.
-  const bool supports_native_pixmap_import_from_dmabuf_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapFactoryDmabuf);
 };
 
-ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf(
-    bool supports_native_pixmap_import_from_dmabuf) {
-// |supports_native_pixmap_import_from_dmabuf| can be enabled on all linux but
-// it is not a requirement to support glCreateImageChromium+Dmabuf since it uses
-// gfx::BufferUsage::SCANOUT and the pixmap does not need to be mappable on the
-// client side.
-//
-// At the moment, only Ozone/Wayland platform running on Linux is able to import
-// handle from dmabuf in addition to the ChromeOS. This is set in the ozone
-// level in the ClientNativePixmapFactoryWayland class.
-//
-// This is not ideal. The ozone platform should probably set this.
-// TODO(rjkroege): do something better here.
-#if defined(OS_CHROMEOS)
-  supports_native_pixmap_import_from_dmabuf = true;
-#endif
-  return new ClientNativePixmapFactoryDmabuf(
-      supports_native_pixmap_import_from_dmabuf);
+ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf() {
+  return new ClientNativePixmapFactoryDmabuf();
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h
index 730b28d..7f802a6 100644
--- a/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h
+++ b/ui/gfx/linux/client_native_pixmap_factory_dmabuf.h
@@ -10,8 +10,7 @@
 
 namespace gfx {
 
-GFX_EXPORT ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf(
-    bool supports_import_from_dmabuf = false);
+GFX_EXPORT ClientNativePixmapFactory* CreateClientNativePixmapFactoryDmabuf();
 
 }  // namespace gfx
 
diff --git a/ui/ozone/common/stub_client_native_pixmap_factory.cc b/ui/ozone/common/stub_client_native_pixmap_factory.cc
index 93e5963..f5b33302 100644
--- a/ui/ozone/common/stub_client_native_pixmap_factory.cc
+++ b/ui/ozone/common/stub_client_native_pixmap_factory.cc
@@ -15,10 +15,6 @@
   ~StubClientNativePixmapFactory() override {}
 
   // ClientNativePixmapFactory:
-  bool IsConfigurationSupported(gfx::BufferFormat format,
-                                gfx::BufferUsage usage) const override {
-    return false;
-  }
   std::unique_ptr<gfx::ClientNativePixmap> ImportFromHandle(
       const gfx::NativePixmapHandle& handle,
       const gfx::Size& size,
diff --git a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc
index 4aa44c7..84809836 100644
--- a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc
+++ b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc
@@ -36,12 +36,6 @@
 class ClientNativePixmapFactoryCast : public gfx::ClientNativePixmapFactory {
  public:
   // ClientNativePixmapFactoryCast implementation:
-  bool IsConfigurationSupported(gfx::BufferFormat format,
-                                gfx::BufferUsage usage) const override {
-    return format == gfx::BufferFormat::BGRA_8888 &&
-           usage == gfx::BufferUsage::SCANOUT;
-  }
-
   std::unique_ptr<gfx::ClientNativePixmap> ImportFromHandle(
       const gfx::NativePixmapHandle& handle,
       const gfx::Size& size,
diff --git a/ui/ozone/platform/cast/ozone_platform_cast.cc b/ui/ozone/platform/cast/ozone_platform_cast.cc
index 217b28c..594d0e63 100644
--- a/ui/ozone/platform/cast/ozone_platform_cast.cc
+++ b/ui/ozone/platform/cast/ozone_platform_cast.cc
@@ -102,6 +102,11 @@
     // On Cast platform the display is initialized by low-level non-Ozone code.
     return nullptr;
   }
+  bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
+                                     gfx::BufferUsage usage) const override {
+    return format == gfx::BufferFormat::BGRA_8888 &&
+           usage == gfx::BufferUsage::SCANOUT;
+  }
 
   void InitializeUI(const InitParams& params) override {
     device_manager_ = CreateDeviceManager();
diff --git a/ui/ozone/platform/drm/ozone_platform_gbm.cc b/ui/ozone/platform/drm/ozone_platform_gbm.cc
index b7f7999..a2e555d 100644
--- a/ui/ozone/platform/drm/ozone_platform_gbm.cc
+++ b/ui/ozone/platform/drm/ozone_platform_gbm.cc
@@ -23,6 +23,7 @@
 #include "ui/events/ozone/device/device_manager.h"
 #include "ui/events/ozone/evdev/event_factory_evdev.h"
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/gfx/linux/client_native_pixmap_dmabuf.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
@@ -171,6 +172,12 @@
     return std::make_unique<DrmNativeDisplayDelegate>(display_manager_.get());
   }
 
+  bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
+                                     gfx::BufferUsage usage) const override {
+    return gfx::ClientNativePixmapDmaBuf::IsConfigurationSupported(format,
+                                                                   usage);
+  }
+
   void InitializeUI(const InitParams& args) override {
     // Ozone drm can operate in four modes configured at
     // runtime. Three process modes:
@@ -184,12 +191,13 @@
     //
     // and 2 connection modes
     //   a. Viz is launched via content::GpuProcessHost and it notifies the
-    //   ozone host when Viz becomes available. b. The ozone host uses a service
-    //   manager to launch and connect to Viz.
+    //   ozone host when Viz becomes available. b. The ozone host uses a
+    //   service manager to launch and connect to Viz.
     //
     // Combinations 1a, 2b, and 3a, and 3b are supported and expected to work.
     // Combination 1a will hopefully be deprecated and replaced with 3a.
-    // Combination 2b adds undesirable code-debt and the intent is to remove it.
+    // Combination 2b adds undesirable code-debt and the intent is to remove
+    // it.
 
     single_process_ = args.single_process;
     using_mojo_ = args.using_mojo || args.connector != nullptr;
@@ -198,6 +206,7 @@
     device_manager_ = CreateDeviceManager();
     window_manager_.reset(new DrmWindowHostManager());
     cursor_.reset(new DrmCursor(window_manager_.get()));
+
 #if BUILDFLAG(USE_XKBCOMMON)
     KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
         std::make_unique<XkbKeyboardLayoutEngine>(xkb_evdev_code_converter_));
diff --git a/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
index 2ad7f68..d4f4b838 100644
--- a/ui/ozone/platform/scenic/ozone_platform_scenic.cc
+++ b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
@@ -34,12 +34,11 @@
 
 namespace {
 
-const OzonePlatform::PlatformProperties kScenicPlatformProperties(
-    /*needs_view_owner_request=*/true,
+constexpr OzonePlatform::PlatformProperties kScenicPlatformProperties{
+    /*needs_view_token=*/true,
     /*custom_frame_pref_default=*/false,
     /*use_system_title_bar=*/false,
-    /*requires_mojo=*/false,
-    std::vector<gfx::BufferFormat>());
+    /*requires_mojo=*/false};
 
 class ScenicPlatformEventSource : public ui::PlatformEventSource {
  public:
diff --git a/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc b/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc
index a57bfb8..7f839c8 100644
--- a/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc
+++ b/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc
@@ -4,50 +4,15 @@
 
 #include "ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.h"
 
-#include "ui/gfx/linux/client_native_pixmap_dmabuf.h"
 #include "ui/gfx/linux/client_native_pixmap_factory_dmabuf.h"
 #include "ui/ozone/common/stub_client_native_pixmap_factory.h"
 #include "ui/ozone/public/ozone_platform.h"
 
 namespace ui {
 
-// Implements ClientNativePixmapFactory to provide a more accurate buffer format
-// support when Wayland dmabuf is used.
-class ClientNativePixmapFactoryWayland : public gfx::ClientNativePixmapFactory {
- public:
-  ClientNativePixmapFactoryWayland() {
-    dmabuf_factory_.reset(gfx::CreateClientNativePixmapFactoryDmabuf(
-        true /* supports_native_pixmap_import_from_dmabuf */));
-  }
-  ~ClientNativePixmapFactoryWayland() override {}
-
-  // ClientNativePixmapFactory overrides:
-  bool IsConfigurationSupported(gfx::BufferFormat format,
-                                gfx::BufferUsage usage) const override {
-    OzonePlatform::PlatformProperties properties =
-        OzonePlatform::GetInstance()->GetPlatformProperties();
-    for (auto buffer_format : properties.supported_buffer_formats) {
-      if (buffer_format == format)
-        return dmabuf_factory_->IsConfigurationSupported(format, usage);
-    }
-    return false;
-  }
-
-  std::unique_ptr<gfx::ClientNativePixmap> ImportFromHandle(
-      const gfx::NativePixmapHandle& handle,
-      const gfx::Size& size,
-      gfx::BufferUsage usage) override {
-    return dmabuf_factory_->ImportFromHandle(handle, size, usage);
-  }
-
- private:
-  std::unique_ptr<ClientNativePixmapFactory> dmabuf_factory_;
-  DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapFactoryWayland);
-};
-
 gfx::ClientNativePixmapFactory* CreateClientNativePixmapFactoryWayland() {
 #if defined(WAYLAND_GBM)
-  return new ClientNativePixmapFactoryWayland();
+  return gfx::CreateClientNativePixmapFactoryDmabuf();
 #else
   return CreateStubClientNativePixmapFactory();
 #endif
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 642a276..0bba90d 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -10,6 +10,7 @@
 #include "ui/display/manager/fake_display_delegate.h"
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
 #include "ui/events/system_input_injector.h"
+#include "ui/gfx/linux/client_native_pixmap_dmabuf.h"
 #include "ui/ozone/common/stub_overlay_manager.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
@@ -41,21 +42,25 @@
 
 namespace {
 
-class OzonePlatformWayland : public OzonePlatform {
- public:
-  OzonePlatformWayland() {
+constexpr OzonePlatform::PlatformProperties kWaylandPlatformProperties = {
+    /*needs_view_token=*/false,
+
     // Supporting server-side decorations requires a support of xdg-decorations.
     // But this protocol has been accepted into the upstream recently, and it
     // will take time before it is taken by compositors. For now, always use
     // custom frames and disallow switching to server-side frames.
     // https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988
-    properties_.custom_frame_pref_default = true;
-    properties_.use_system_title_bar = false;
+    /*custom_frame_pref_default=*/true,
+    /*use_system_title_bar=*/false,
+
     // Ozone/Wayland relies on the mojo communication when running in
     // !single_process.
     // TODO(msisov, rjkroege): Remove after http://crbug.com/806092.
-    properties_.requires_mojo = true;
-  }
+    /*requires_mojo=*/true};
+
+class OzonePlatformWayland : public OzonePlatform {
+ public:
+  OzonePlatformWayland() {}
   ~OzonePlatformWayland() override {}
 
   // OzonePlatform
@@ -113,6 +118,18 @@
     return connection_->wayland_output_manager()->CreateWaylandScreen();
   }
 
+  bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
+                                     gfx::BufferUsage usage) const override {
+    if (std::find(supported_buffer_formats_.begin(),
+                  supported_buffer_formats_.end(),
+                  format) == supported_buffer_formats_.end()) {
+      return false;
+    }
+
+    return gfx::ClientNativePixmapDmaBuf::IsConfigurationSupported(format,
+                                                                   usage);
+  }
+
   void InitializeUI(const InitParams& args) override {
 #if BUILDFLAG(USE_XKBCOMMON)
     KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
@@ -135,6 +152,7 @@
     overlay_manager_.reset(new StubOverlayManager);
     input_controller_ = CreateStubInputController();
     gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
+    supported_buffer_formats_ = connection_->GetSupportedBufferFormats();
   }
 
   void InitializeGPU(const InitParams& args) override {
@@ -163,11 +181,7 @@
   }
 
   const PlatformProperties& GetPlatformProperties() override {
-    if (connection_ && properties_.supported_buffer_formats.empty()) {
-      properties_.supported_buffer_formats =
-          connection_->GetSupportedBufferFormats();
-    }
-    return properties_;
+    return kWaylandPlatformProperties;
   }
 
   void AddInterfaces(service_manager::BinderRegistry* registry) override {
@@ -199,7 +213,7 @@
   std::unique_ptr<WaylandConnectionProxy> proxy_;
   std::unique_ptr<WaylandConnectionConnector> connector_;
 
-  PlatformProperties properties_;
+  std::vector<gfx::BufferFormat> supported_buffer_formats_;
 
   DISALLOW_COPY_AND_ASSIGN(OzonePlatformWayland);
 };
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory.cc b/ui/ozone/platform/wayland/wayland_surface_factory.cc
index a5b1dec0..83fb59d 100644
--- a/ui/ozone/platform/wayland/wayland_surface_factory.cc
+++ b/ui/ozone/platform/wayland/wayland_surface_factory.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/shared_memory.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/linux/client_native_pixmap_dmabuf.h"
 #include "ui/gfx/vsync_provider.h"
 #include "ui/ozone/common/egl_util.h"
 #include "ui/ozone/common/gl_ozone_egl.h"
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index 8cba6bd..d9c5310 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -35,25 +35,6 @@
 
 }  // namespace
 
-OzonePlatform::PlatformProperties::PlatformProperties() = default;
-
-OzonePlatform::PlatformProperties::PlatformProperties(
-    bool needs_view_token,
-    bool custom_frame_default,
-    bool can_use_system_title_bar,
-    bool requires_mojo_for_ipc,
-    std::vector<gfx::BufferFormat> buffer_formats)
-    : needs_view_token(needs_view_token),
-      custom_frame_pref_default(custom_frame_default),
-      use_system_title_bar(can_use_system_title_bar),
-      requires_mojo(requires_mojo_for_ipc),
-      supported_buffer_formats(buffer_formats) {}
-
-OzonePlatform::PlatformProperties::~PlatformProperties() = default;
-
-OzonePlatform::PlatformProperties::PlatformProperties(
-    const PlatformProperties& other) = default;
-
 OzonePlatform::OzonePlatform() {
   GetOzoneInstanceLock().AssertAcquired();
   DCHECK(!g_instance) << "There should only be a single OzonePlatform.";
@@ -141,6 +122,13 @@
   return nullptr;
 }
 
+bool OzonePlatform::IsNativePixmapConfigSupported(
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage) const {
+  // Platform that support NativePixmap must override this method.
+  return false;
+}
+
 const OzonePlatform::PlatformProperties&
 OzonePlatform::GetPlatformProperties() {
   static const base::NoDestructor<OzonePlatform::PlatformProperties> properties;
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index c6dbf1f..ceb28724 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -87,15 +87,6 @@
 
   // Struct used to indicate platform properties.
   struct PlatformProperties {
-    PlatformProperties();
-    PlatformProperties(bool needs_request,
-                       bool custom_frame_default,
-                       bool can_use_system_title_bar,
-                       bool requires_mojo_for_ipc,
-                       std::vector<gfx::BufferFormat> buffer_formats);
-    ~PlatformProperties();
-    PlatformProperties(const PlatformProperties& other);
-
     // Fuchsia only: set to true when the platforms requires |view_token| field
     // in PlatformWindowInitProperties when creating a window.
     bool needs_view_token = false;
@@ -111,9 +102,6 @@
     // Determines if the platform requires mojo communication for the IPC.
     // Currently used only by the Ozone/Wayland platform.
     bool requires_mojo = false;
-
-    // Wayland only: carries buffer formats supported by a Wayland server.
-    std::vector<gfx::BufferFormat> supported_buffer_formats;
   };
 
   using StartupCallback = base::OnceCallback<void(OzonePlatform*)>;
@@ -161,6 +149,10 @@
   CreateNativeDisplayDelegate() = 0;
   virtual std::unique_ptr<PlatformScreen> CreateScreen();
 
+  // Returns true if the specified buffer format is supported.
+  virtual bool IsNativePixmapConfigSupported(gfx::BufferFormat format,
+                                             gfx::BufferUsage usage) const;
+
   // Returns a struct that contains configuration and requirements for the
   // current platform implementation.
   virtual const PlatformProperties& GetPlatformProperties();
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc
index aa1f1e1..e0db84bb 100644
--- a/ui/views/animation/ink_drop_host_view.cc
+++ b/ui/views/animation/ink_drop_host_view.cc
@@ -230,6 +230,12 @@
 }
 
 void InkDropHostView::OnMouseEvent(ui::MouseEvent* event) {
+  auto weak_ptr = weak_factory_.GetWeakPtr();
+  View::OnMouseEvent(event);
+  if (!weak_ptr) {
+    // Calling View::OnMouseEvent() might destroy |this|.
+    return;
+  }
   switch (event->type()) {
     case ui::ET_MOUSE_ENTERED:
       GetInkDrop()->SetHovered(true);
@@ -243,7 +249,6 @@
     default:
       break;
   }
-  View::OnMouseEvent(event);
 }
 
 std::unique_ptr<InkDropImpl> InkDropHostView::CreateDefaultInkDropImpl() {
diff --git a/ui/views/animation/ink_drop_host_view.h b/ui/views/animation/ink_drop_host_view.h
index 572573c..8f8b8ea 100644
--- a/ui/views/animation/ink_drop_host_view.h
+++ b/ui/views/animation/ink_drop_host_view.h
@@ -205,6 +205,8 @@
 
   std::unique_ptr<views::InkDropMask> ink_drop_mask_;
 
+  base::WeakPtrFactory<InkDropHostView> weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(InkDropHostView);
 };
 
diff --git a/ui/views/layout/layout_manager.cc b/ui/views/layout/layout_manager.cc
index eb91c18..877a9f31 100644
--- a/ui/views/layout/layout_manager.cc
+++ b/ui/views/layout/layout_manager.cc
@@ -14,6 +14,20 @@
 void LayoutManager::Installed(View* host) {
 }
 
+void LayoutManager::InvalidateLayout() {}
+
+gfx::Size LayoutManager::GetMinimumSize(const View* host) const {
+  // Fall back to using preferred size if no minimum size calculation is
+  // available (e.g. legacy layout managers).
+  //
+  // Ideally we'd just call GetPreferredSize() on ourselves here, but because
+  // some legacy views with layout managers override GetPreferredSize(), we need
+  // to call GetPreferredSize() on the host view instead. The default
+  // views::View behavior will be to call GetPreferredSize() on this layout
+  // manager, so the fallback behavior in all other cases is as expected.
+  return host->GetPreferredSize();
+}
+
 int LayoutManager::GetPreferredHeightForWidth(const View* host,
                                               int width) const {
   return GetPreferredSize(host).height();
diff --git a/ui/views/layout/layout_manager.h b/ui/views/layout/layout_manager.h
index 5e4a1e4..ee1bfda 100644
--- a/ui/views/layout/layout_manager.h
+++ b/ui/views/layout/layout_manager.h
@@ -34,16 +34,27 @@
   // Notification that this LayoutManager has been installed on |host|.
   virtual void Installed(View* host);
 
+  // For layout managers that can cache layout data, it's useful to let the
+  // layout manager know that its current layout might not be valid.
+  // TODO(dfried): consider if we should include some default behavior (like a
+  // rolling layout counter).
+  virtual void InvalidateLayout();
+
   // Called by View::Layout() to position and size the children of |host|.
   // Generally this queries |host| for its size and positions and sizes the
   // children in a LayoutManager specific way.
   virtual void Layout(View* host) = 0;
 
-  // Return the preferred size, which is typically the size needed to give each
+  // Returns the preferred size, which is typically the size needed to give each
   // child of |host| its preferred size. Generally this is calculated using the
   // View::CalculatePreferredSize() on each of the children of |host|.
   virtual gfx::Size GetPreferredSize(const View* host) const = 0;
 
+  // Returns the minimum size, which defaults to the preferred size. Layout
+  // managers with the ability to collapse or hide child views may override this
+  // behavior.
+  virtual gfx::Size GetMinimumSize(const View* host) const;
+
   // Return the preferred height for a particular width. Generally this is
   // calculated using View::GetHeightForWidth() or
   // View::CalculatePreferredSize() on each of the children of |host|. Override
diff --git a/ui/views/view.cc b/ui/views/view.cc
index bc6bf9d7..da506368 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -423,6 +423,9 @@
 }
 
 gfx::Size View::GetMinimumSize() const {
+  if (layout_manager_)
+    return layout_manager_->GetMinimumSize(this);
+
   return GetPreferredSize();
 }
 
@@ -431,7 +434,7 @@
 }
 
 int View::GetHeightForWidth(int w) const {
-  if (layout_manager_.get())
+  if (layout_manager_)
     return layout_manager_->GetPreferredHeightForWidth(this, w);
   return GetPreferredSize().height();
 }
@@ -586,7 +589,7 @@
   needs_layout_ = false;
 
   // If we have a layout manager, let it handle the layout for us.
-  if (layout_manager_.get())
+  if (layout_manager_)
     layout_manager_->Layout(this);
 
   // Make sure to propagate the Layout() call to any children that haven't
@@ -609,6 +612,9 @@
   // Always invalidate up. This is needed to handle the case of us already being
   // valid, but not our parent.
   needs_layout_ = true;
+  if (layout_manager_)
+    layout_manager_->InvalidateLayout();
+
   if (parent_)
     parent_->InvalidateLayout();
 }
@@ -1496,7 +1502,7 @@
 // Size and disposition --------------------------------------------------------
 
 gfx::Size View::CalculatePreferredSize() const {
-  if (layout_manager_.get())
+  if (layout_manager_)
     return layout_manager_->GetPreferredSize(this);
   return gfx::Size();
 }
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 9ba7e7a..1332884 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -578,7 +578,7 @@
   // problems with event routing (i.e. which Hook takes precedence) and
   // destruction ordering.
   DCHECK(!keyboard_hook_);
-  keyboard_hook_ = ui::KeyboardHook::Create(
+  keyboard_hook_ = ui::KeyboardHook::CreateModifierKeyboardHook(
       std::move(dom_codes), GetAcceleratedWidget(),
       base::BindRepeating(&DesktopWindowTreeHostWin::HandleKeyEvent,
                           base::Unretained(this)));
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index 9dd1a0dc..5e46a4f6 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -1318,7 +1318,7 @@
   // problems with event routing (i.e. which Hook takes precedence) and
   // destruction ordering.
   DCHECK(!keyboard_hook_);
-  keyboard_hook_ = ui::KeyboardHook::Create(
+  keyboard_hook_ = ui::KeyboardHook::CreateModifierKeyboardHook(
       std::move(dom_codes), GetAcceleratedWidget(),
       base::BindRepeating(&DesktopWindowTreeHostX11::DispatchKeyEvent,
                           base::Unretained(this)));
diff --git a/ui/webui/resources/PRESUBMIT.py b/ui/webui/resources/PRESUBMIT.py
index d1933521..5252e6b 100644
--- a/ui/webui/resources/PRESUBMIT.py
+++ b/ui/webui/resources/PRESUBMIT.py
@@ -61,8 +61,7 @@
     cwd = input_api.PresubmitLocalPath()
     sys.path += [input_api.os_path.join(cwd, '..', '..', '..', 'tools')]
     from web_dev_style import presubmit_support
-    BLACKLIST = ['ui/webui/resources/js/analytics.js',
-                 'ui/webui/resources/js/jstemplate_compiled.js']
+    BLACKLIST = ['ui/webui/resources/js/jstemplate_compiled.js']
     file_filter = lambda f: f.LocalPath() not in BLACKLIST
     results += presubmit_support.CheckStyle(input_api, output_api, file_filter)
   finally:
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
index 918bb67..12eaf15b 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
@@ -237,12 +237,11 @@
    * Processes the message received from the quick unlock api and hides/shows
    * the problem based on the message.
    * @private
-   * @param {string} newPin
    * @param {chrome.quickUnlockPrivate.CredentialCheck} message The message
    *     received from checkCredential.
    */
-  processPinProblems_: function(newPin, message) {
-    if (newPin && !message.errors.length && !message.warnings.length) {
+  processPinProblems_: function(message) {
+    if (!message.errors.length && !message.warnings.length) {
       this.hideProblem_();
       this.enableSubmit = true;
       this.pinHasPassedMinimumLength_ = true;
@@ -281,9 +280,6 @@
           break;
       }
     }
-
-    if (!newPin)
-      this.enableSubmit = false;
   },
 
   /**
@@ -292,13 +288,13 @@
   onPinChange_: function(e) {
     const newPin = /** @type {{pin: string}} */ (e.detail).pin;
     if (!this.isConfirmStep) {
-      if (this.quickUnlockPrivate) {
+      if (newPin) {
         this.quickUnlockPrivate.checkCredential(
             chrome.quickUnlockPrivate.QuickUnlockMode.PIN, newPin,
-            this.processPinProblems_.bind(this, newPin));
-      }
-      if (!newPin)
+            this.processPinProblems_.bind(this));
+      } else {
         this.enableSubmit = false;
+      }
       return;
     }
 
diff --git a/ui/webui/resources/js/analytics.js b/ui/webui/resources/js/analytics.js
deleted file mode 100644
index b0893a6..0000000
--- a/ui/webui/resources/js/analytics.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file serves as a proxy to bring the included js file from /third_party
-// into its correct location under the resources directory tree, whence it is
-// delivered via a chrome://resources URL.  See ../webui_resources.grd.
-
-// Note: this <include> is not behind a single-line comment because the first
-// line of the file is source code (so the first line would be skipped) instead
-// of a licence header.
-// clang-format off
-<include src="../../../../third_party/analytics/google-analytics-bundle.js">
diff --git a/ui/webui/resources/webui_resources.grd b/ui/webui/resources/webui_resources.grd
index a2b2e2d..46c8b37 100644
--- a/ui/webui/resources/webui_resources.grd
+++ b/ui/webui/resources/webui_resources.grd
@@ -15,7 +15,6 @@
     <includes>
       <include name="IDR_WEBUI_I18N_TEMPLATE_JS" file="js/i18n_template.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_WEBUI_JSTEMPLATE_JS" file="js/jstemplate_compiled.js" flattenhtml="true" type="BINDATA" />
-      <include name="IDR_WEBUI_ANALYTICS_JS" file="js/analytics.js" flattenhtml="true" type="BINDATA" compress="gzip" />
       <!-- Roboto Font. Roboto-Regular and Roboto-Light is already available on
            Android, and Roboto-Medium is not used on Android. All 6 weights of
            Roboto are available on Chrome OS.-->
diff --git a/url/origin.cc b/url/origin.cc
index 1c78bc9..f6403f5 100644
--- a/url/origin.cc
+++ b/url/origin.cc
@@ -42,7 +42,8 @@
     // It's SchemeHostPort's responsibility to filter out unrecognized schemes;
     // sanity check that this is happening.
     DCHECK(tuple.IsInvalid() || url.IsStandard() ||
-           base::ContainsValue(GetLocalSchemes(), url.scheme_piece()));
+           base::ContainsValue(GetLocalSchemes(), url.scheme_piece()) ||
+           AllowNonStandardSchemesForAndroidWebView());
   }
 
   if (tuple.IsInvalid())
diff --git a/url/origin_unittest.cc b/url/origin_unittest.cc
index 0b57da0..5f889c09 100644
--- a/url/origin_unittest.cc
+++ b/url/origin_unittest.cc
@@ -654,4 +654,18 @@
   EXPECT_STREQ("https://foo.com", origin1_debug_alias);
 }
 
+TEST_F(OriginTest, NonStandardScheme) {
+  Origin origin = Origin::Create(GURL("cow://"));
+  EXPECT_TRUE(origin.opaque());
+}
+TEST_F(OriginTest, NonStandardSchemeWithAndroidWebViewHack) {
+  EnableNonStandardSchemesForAndroidWebView();
+  Origin origin = Origin::Create(GURL("cow://"));
+  EXPECT_FALSE(origin.opaque());
+  EXPECT_EQ("cow", origin.scheme());
+  EXPECT_EQ("", origin.host());
+  EXPECT_EQ(0, origin.port());
+  Shutdown();
+}
+
 }  // namespace url
diff --git a/url/scheme_host_port.cc b/url/scheme_host_port.cc
index 0d681f13..e588aece 100644
--- a/url/scheme_host_port.cc
+++ b/url/scheme_host_port.cc
@@ -52,6 +52,10 @@
                   const base::StringPiece& host,
                   uint16_t port,
                   SchemeHostPort::ConstructPolicy policy) {
+  // Empty schemes are never valid.
+  if (scheme.empty())
+    return false;
+
   SchemeType scheme_type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
   bool is_standard = GetStandardSchemeType(
       scheme.data(),
@@ -67,7 +71,10 @@
     if (base::ContainsValue(GetLocalSchemes(), scheme) && host.empty() &&
         port == 0)
       return true;
-    return false;
+
+    // Otherwise, allow non-standard schemes only if the Android WebView
+    // workaround is enabled.
+    return AllowNonStandardSchemesForAndroidWebView();
   }
 
   switch (scheme_type) {
@@ -135,7 +142,8 @@
   scheme_ = std::move(scheme);
   host_ = std::move(host);
   port_ = port;
-  DCHECK(!IsInvalid());
+  DCHECK(!IsInvalid()) << "Scheme: " << scheme_ << " Host: " << host_
+                       << " Port: " << port;
 }
 
 SchemeHostPort::SchemeHostPort(base::StringPiece scheme,
diff --git a/url/url_util.cc b/url/url_util.cc
index a515231..29cebf96 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -20,6 +20,8 @@
 
 namespace {
 
+bool g_allow_non_standard_schemes = false;
+
 // Pass this enum through for methods which would like to know if whitespace
 // removal is necessary.
 enum WhitespaceRemovalPolicy {
@@ -530,6 +532,7 @@
 
 void Shutdown() {
   initialized = false;
+  g_allow_non_standard_schemes = false;
   delete standard_schemes;
   standard_schemes = nullptr;
   delete referrer_schemes;
@@ -550,6 +553,14 @@
   empty_document_schemes = nullptr;
 }
 
+void EnableNonStandardSchemesForAndroidWebView() {
+  g_allow_non_standard_schemes = true;
+}
+
+bool AllowNonStandardSchemesForAndroidWebView() {
+  return g_allow_non_standard_schemes;
+}
+
 void AddStandardScheme(const char* new_scheme, SchemeType type) {
   Initialize();
   DoAddSchemeWithType(new_scheme, type, standard_schemes);
diff --git a/url/url_util.h b/url/url_util.h
index d4b0d773..ee456a0 100644
--- a/url/url_util.h
+++ b/url/url_util.h
@@ -39,6 +39,17 @@
 
 // Schemes ---------------------------------------------------------------------
 
+// Changes the behavior of SchemeHostPort / Origin to allow non-standard schemes
+// to be specified, instead of canonicalizing them to an invalid SchemeHostPort
+// or opaque Origin, respectively. This is used for Android WebView backwards
+// compatibility, which allows the use of custom schemes: content hosted in
+// Android WebView assumes that one URL with a non-standard scheme will be
+// same-origin to another URL with the same non-standard scheme.
+URL_EXPORT void EnableNonStandardSchemesForAndroidWebView();
+
+// Whether or not SchemeHostPort and Origin allow non-standard schemes.
+URL_EXPORT bool AllowNonStandardSchemesForAndroidWebView();
+
 // A pair for representing a standard scheme name and the SchemeType for it.
 struct URL_EXPORT SchemeWithType {
   const char* scheme;