diff --git a/DEPS b/DEPS
index 5153173..31513e6f 100644
--- a/DEPS
+++ b/DEPS
@@ -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': '5f01324fb627a502823d9e359d243c3430920258',
+  'angle_revision': '471358f39a6a53b97575f0074f466892babd8efc',
   # 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.
@@ -172,7 +172,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'eee0f6ad0df532b4ba3eee2bf04a5a155817586c',
+  'catapult_revision': 'b88aa2d8ba8e67124bac06ab296251420767d636',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -192,31 +192,31 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
   # and whatever else without interference from each other.
-  'android_sdk_build-tools_version': 'version:27.0.3-cr0',
+  'android_sdk_build-tools_version': '125a2abaa0393cd68a2414ec79e514d5d0a80b58',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_emulator_version
   # and whatever else without interference from each other.
-  'android_sdk_emulator_version': 'version:27.1.12-cr0',
+  'android_sdk_emulator_version': '731059df93885359487fd9d6085bc7804a23f9c8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_extras_version
   # and whatever else without interference from each other.
-  'android_sdk_extras_version': 'version:47.0.0-cr0',
+  'android_sdk_extras_version': '2f33032ef348e2ff37d90d53563f4cb030a879bf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_platform-tools_version
   # and whatever else without interference from each other.
-  'android_sdk_platform-tools_version': 'version:27.0.1-cr0',
+  'android_sdk_platform-tools_version': '7f31093ac2fc930abadb3bf65fc0a663a22cd6dc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_platforms_version
   # and whatever else without interference from each other.
-  'android_sdk_platforms_version': 'version:android-27-cr0',
+  'android_sdk_platforms_version': '6a79b931523181e147ddfc7db318bf0b2e34edb5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_sources_version
   # and whatever else without interference from each other.
-  'android_sdk_sources_version': 'version:android-27-cr1',
+  'android_sdk_sources_version': '8d8bd74d8f062449b8c44a663cc38a33ddf96752',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_tools_version
   # and whatever else without interference from each other.
-  'android_sdk_tools_version': 'version:26.1.1-cr9',
+  'android_sdk_tools_version': '1a659d51804abb9461cd19aeffc2102e47a15a25',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -236,7 +236,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '813bfbd061128113dd7b4c7c80321b536597c362',
+  'dawn_revision': 'df72914a60d570678b60ccc2e23b2ff13396f218',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -263,7 +263,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/cts_archive',
-              'version': 'version:1.1',
+              'version': '3JR1LoM5xpNK_jcnwciobRW9H8TM3SUxuSGQJBjt6AUC',
           },
       ],
       'condition': 'checkout_android',
@@ -288,7 +288,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/safe_browsing/dmg',
-        'version': 'version:20180816.2',
+        'version': 'a543ae3f0b3e67dd5a1c75f63317231a1d242912',
       },
     ],
     'condition': 'checkout_mac',
@@ -325,7 +325,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/firebase_ios',
-          'version': 'version:5.8.0',
+          'version': 'b44315eefb889f856af131d487cc61b46a78777b',
         },
       ],
       'condition': 'checkout_ios',
@@ -442,7 +442,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/accessibility-test-framework',
-              'version': 'version:2.1-cr0',
+              'version': 'b5ec1e56e58e56bc1a0c77d43111c37f9b512c8a',
           },
       ],
       'condition': 'checkout_android',
@@ -463,7 +463,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_support_test_runner',
-              'version': 'version:0.5-cr0',
+              'version': '96d4bf848cd210fdcbca6bcc8c1b4b39cbd93141',
           },
       ],
       'condition': 'checkout_android',
@@ -474,7 +474,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_system_sdk',
-              'version': 'version:28-dp3-cr0',
+              'version': '0c0a94a9326c045c8aabb7fc418ea9c849b782f2',
           },
       ],
       'condition': 'checkout_android',
@@ -490,7 +490,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_build_tools/aapt2',
-              'version': 'version:3.3.0-beta01-5013011-cr0',
+              'version': 'XPNW95mgY7ws_5lNsyjlq7DowuughMNsRIGuGCT0basC',
           },
       ],
       'condition': 'checkout_android',
@@ -512,7 +512,7 @@
       'packages': [
           {
        'package': 'chromium/third_party/android_tools_bundletool',
-       'version': 'version:0.7.1-cr0',
+       'version': 'bSpsD5lu4IO9FkDBSyjPNU2yibLq89K25354Hx8Ak-QC',
    },
       ],
       'condition': 'checkout_android',
@@ -579,7 +579,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/apk-patch-size-estimator',
-              'version': 'version:0.2-cr0',
+              'version': 'b603e99dca9b90d6a99519c232cd811878283b08',
           },
       ],
       'condition': 'checkout_android',
@@ -595,7 +595,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/bazel',
-              'version': 'version:0.10.0',
+              'version': '1794576f65a721eb0af320a0701e48d31f1b2415',
           },
       ],
       'condition': 'checkout_android',
@@ -617,7 +617,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/bouncycastle',
-              'version': 'version:1.46-cr0',
+              'version': 'c078e87552ba26e776566fdaf0f22cd8712743d0',
           },
       ],
       'condition': 'checkout_android',
@@ -631,7 +631,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/byte_buddy',
-              'version': 'version:1.8.8-cr0',
+              'version': 'c9b53316603fc2d997c899c7ca1707f809b918cd',
           },
       ],
       'condition': 'checkout_android',
@@ -642,7 +642,7 @@
     Var('chromium_git') + '/catapult.git' + '@' + Var('catapult_revision'),
 
   'src/third_party/cct_dynamic_module/src': {
-      'url': Var('chromium_git') + '/dynamicmodule' + '@' + 'b5a34248be7ff38ef4d7836123f3c796dfb18225',
+      'url': Var('chromium_git') + '/dynamicmodule' + '@' + '4c0a460459bc34177d3a71d6b49e253476431ec9',
       'condition': 'checkout_android',
   },
 
@@ -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' + '@' + '81cbcd45e1a28db58d86bb556e1c40a289f35ef9',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '97b903782cf522254f3a531cc05bcba0da721028',
       'condition': 'checkout_linux',
   },
 
@@ -698,7 +698,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/espresso',
-              'version': 'version:2.2.1-cr0',
+              'version': 'c92dcfc4e894555a0b3c309f2b7939640eb1fee4',
           },
       ],
       'condition': 'checkout_android',
@@ -749,7 +749,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/google-truth',
-              'version': 'version:0.40',
+              'version': '4d6fe892fc3150ab40ef1d619baf0038859eb6d2',
           },
       ],
       'condition': 'checkout_android',
@@ -774,7 +774,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/gson',
-              'version': 'version:2.8.0-cr0',
+              'version': '681931c9778045903a0ed59856ce2dd8dd7bf7ca',
           },
       ],
       'condition': 'checkout_android',
@@ -785,7 +785,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/guava',
-              'version': 'version:23.0-cr0',
+              'version': 'a6fba501f3a0de88b9be1daa2052632de5b96a46',
           },
       ],
       'condition': 'checkout_android',
@@ -806,7 +806,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/hamcrest',
-              'version': 'version:1.3-cr0',
+              'version': '37eccfc658fe79695d6abb6dd497463c4372032f',
           },
       ],
       'condition': 'checkout_android',
@@ -823,7 +823,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/icu4j',
-              'version': 'version:53.1-cr0',
+              'version': 'e87e5bed2b4935913ee26a3ebd0b723ee2344354',
           },
       ],
       'condition': 'checkout_android',
@@ -834,7 +834,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/intellij',
-              'version': 'version:12.0-cr0',
+              'version': '77c2721b024b36ee073402c08e6d8428c0295336',
           },
       ],
       'condition': 'checkout_android',
@@ -974,7 +974,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/objenesis',
-              'version': 'version:2.4-cr0',
+              'version': '9e367f55e5a65781ee77bfcbaa88fb82b30e75c0',
           },
       ],
       'condition': 'checkout_android',
@@ -991,7 +991,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/ow2_asm',
-              'version': 'version:5.0.1-cr0',
+              'version': '0dcaea8bd839b3f2eb8415c327b40e8e398a373e',
           },
       ],
       'condition': 'checkout_android',
@@ -1125,7 +1125,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/sqlite4java',
-              'version': 'version:0.282-cr0',
+              'version': '889660698187baa7c8b0d79f7bf58563125fbd66',
           },
       ],
       'condition': 'checkout_android',
@@ -1182,7 +1182,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/xstream',
-              'version': 'version:1.4.8-cr0',
+              'version': '4278b1b78b86ab7a1a29e64d5aec9a47a9aab0fe',
           },
       ],
       'condition': 'checkout_android',
@@ -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@73b3bde2659f52e891abf622a706feed5dfce112',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9dbf638d6f752c059861c913e202fbdd8fbd2f9a',
     'condition': 'checkout_src_internal',
   },
 
@@ -1210,7 +1210,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core_verification',
-              'version': 'version:1.3.7-cr0',
+              'version': 'CMX_sCQTYt-Pj3-xFaCMPNGK3TjVMfToP2glkNm1a4AC',
           },
       ],
       'condition': 'checkout_android',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 5302d57c..535f0b9 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 import("//android_webview/system_webview_apk_tmpl.gni")
-import("//android_webview/webview_repack_locales.gni")
 import("//android_webview/variables.gni")
+import("//android_webview/webview_repack_locales.gni")
 import("//build/config/android/chrome_version.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
@@ -852,6 +852,7 @@
     "java/src/org/chromium/android_webview/AwDevToolsServer.java",
     "java/src/org/chromium/android_webview/AwFeatureList.java",
     "java/src/org/chromium/android_webview/AwFormDatabase.java",
+    "java/src/org/chromium/android_webview/AwFunctor.java",
     "java/src/org/chromium/android_webview/AwGeolocationPermissions.java",
     "java/src/org/chromium/android_webview/AwGLFunctor.java",
     "java/src/org/chromium/android_webview/AwHttpAuthHandler.java",
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 1b30da5a..021e475d 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -112,6 +112,8 @@
 
 namespace android_webview {
 
+class CompositorFrameConsumer;
+
 namespace {
 
 bool g_should_download_favicons = false;
@@ -378,7 +380,7 @@
     base::MemoryPressureListener::NotifyMemoryPressure(
         base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
   }
-  SetAwGLFunctor(nullptr);
+  browser_view_renderer_.SetCurrentCompositorFrameConsumer(nullptr);
   AwContentsLifecycleNotifier::OnWebViewDestroyed();
 }
 
@@ -393,15 +395,12 @@
   return web_contents_->GetJavaWebContents();
 }
 
-void AwContents::SetAwGLFunctor(AwGLFunctor* functor) {
+void AwContents::SetCompositorFrameConsumer(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    jlong compositor_frame_consumer) {
   browser_view_renderer_.SetCurrentCompositorFrameConsumer(
-      functor ? functor->GetCompositorFrameConsumer() : nullptr);
-}
-
-void AwContents::SetAwGLFunctor(JNIEnv* env,
-                                const base::android::JavaParamRef<jobject>& obj,
-                                jlong gl_functor) {
-  SetAwGLFunctor(reinterpret_cast<AwGLFunctor*>(gl_functor));
+      reinterpret_cast<CompositorFrameConsumer*>(compositor_frame_consumer));
 }
 
 ScopedJavaLocalRef<jobject> AwContents::GetRenderProcess(
diff --git a/android_webview/browser/aw_contents.h b/android_webview/browser/aw_contents.h
index c623fc5..1d7e579 100644
--- a/android_webview/browser/aw_contents.h
+++ b/android_webview/browser/aw_contents.h
@@ -39,7 +39,6 @@
 namespace android_webview {
 
 class AwContentsClientBridge;
-class AwGLFunctor;
 class AwPdfExporter;
 class AwWebContentsDelegate;
 class PermissionRequestHandler;
@@ -103,9 +102,10 @@
   base::android::ScopedJavaLocalRef<jobject> GetWebContents(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
-  void SetAwGLFunctor(JNIEnv* env,
-                      const base::android::JavaParamRef<jobject>& obj,
-                      jlong gl_functor);
+  void SetCompositorFrameConsumer(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      jlong compositor_frame_consumer);
 
   base::android::ScopedJavaLocalRef<jobject> GetRenderProcess(
       JNIEnv* env,
@@ -374,8 +374,6 @@
 
   void SetDipScaleInternal(float dip_scale);
 
-  void SetAwGLFunctor(AwGLFunctor* functor);
-
   JavaObjectWeakGlobalRef java_ref_;
   BrowserViewRenderer browser_view_renderer_;  // Must outlive |web_contents_|.
   std::unique_ptr<content::WebContents> web_contents_;
diff --git a/android_webview/browser/aw_gl_functor.cc b/android_webview/browser/aw_gl_functor.cc
index ac39690..a306d495 100644
--- a/android_webview/browser/aw_gl_functor.cc
+++ b/android_webview/browser/aw_gl_functor.cc
@@ -85,6 +85,13 @@
   return reinterpret_cast<intptr_t>(&render_thread_manager_);
 }
 
+jlong AwGLFunctor::GetCompositorFrameConsumer(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return reinterpret_cast<intptr_t>(GetCompositorFrameConsumer());
+}
+
 static jint JNI_AwGLFunctor_GetNativeInstanceCount(
     JNIEnv* env,
     const JavaParamRef<jclass>&) {
diff --git a/android_webview/browser/aw_gl_functor.h b/android_webview/browser/aw_gl_functor.h
index 699474b..12816fd0 100644
--- a/android_webview/browser/aw_gl_functor.h
+++ b/android_webview/browser/aw_gl_functor.h
@@ -25,14 +25,17 @@
                               const base::android::JavaParamRef<jobject>& obj);
   jlong GetAwDrawGLViewContext(JNIEnv* env,
                                const base::android::JavaParamRef<jobject>& obj);
+  jlong GetCompositorFrameConsumer(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
   jlong GetAwDrawGLFunction(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj);
 
+ private:
   CompositorFrameConsumer* GetCompositorFrameConsumer() {
     return &render_thread_manager_;
   }
 
- private:
   JavaObjectWeakGlobalRef java_ref_;
   RenderThreadManager render_thread_manager_;
 };
diff --git a/android_webview/browser/state_serializer.cc b/android_webview/browser/state_serializer.cc
index 9a4289b3f..49caeb3e 100644
--- a/android_webview/browser/state_serializer.cc
+++ b/android_webview/browser/state_serializer.cc
@@ -35,8 +35,7 @@
 
 }  // namespace
 
-void WriteToPickle(const content::WebContents& web_contents,
-                   base::Pickle* pickle) {
+void WriteToPickle(content::WebContents& web_contents, base::Pickle* pickle) {
   DCHECK(pickle);
 
   internal::WriteHeaderToPickle(pickle);
diff --git a/android_webview/browser/state_serializer.h b/android_webview/browser/state_serializer.h
index a07bbd2..7a3f1a0 100644
--- a/android_webview/browser/state_serializer.h
+++ b/android_webview/browser/state_serializer.h
@@ -29,8 +29,7 @@
 // success.
 
 // Note that |pickle| may be changed even if function returns false.
-void WriteToPickle(const content::WebContents& web_contents,
-                   base::Pickle* pickle);
+void WriteToPickle(content::WebContents& web_contents, base::Pickle* pickle);
 
 // |web_contents| will not be modified if function returns false.
 bool RestoreFromPickle(base::PickleIterator* iterator,
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 dcf13c8..01b40e2 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -343,7 +343,7 @@
     private long mNativeAwContents;
     private final AwBrowserContext mBrowserContext;
     private ViewGroup mContainerView;
-    private AwGLFunctor mDrawFunctor;
+    private AwFunctor mDrawFunctor;
     private final Context mContext;
     private final int mAppTargetSdkVersion;
     private AwViewAndroidDelegate mViewAndroidDelegate;
@@ -778,7 +778,7 @@
                 if (isDestroyedOrNoOperation(NO_WARN)) return;
                 if (level >= TRIM_MEMORY_MODERATE) {
                     if (mDrawFunctor != null) {
-                        mDrawFunctor.deleteHardwareRenderer();
+                        mDrawFunctor.trimMemory();
                     }
                 }
                 nativeTrimMemory(mNativeAwContents, level, visible);
@@ -1145,18 +1145,18 @@
         }
     }
 
-    private void setFunctor(AwGLFunctor functor) {
+    private void setFunctor(AwFunctor functor) {
         if (mDrawFunctor == functor) return;
-        AwGLFunctor oldFunctor = mDrawFunctor;
+        AwFunctor oldFunctor = mDrawFunctor;
         mDrawFunctor = functor;
         updateNativeAwGLFunctor();
 
-        if (oldFunctor != null) oldFunctor.releasedByContents();
+        if (oldFunctor != null) oldFunctor.destroy();
     }
 
     private void updateNativeAwGLFunctor() {
-        nativeSetAwGLFunctor(
-                mNativeAwContents, mDrawFunctor != null ? mDrawFunctor.getNativeAwGLFunctor() : 0);
+        nativeSetCompositorFrameConsumer(mNativeAwContents,
+                mDrawFunctor != null ? mDrawFunctor.getNativeCompositorFrameConsumer() : 0);
     }
 
     /* Common initialization routine for adopting a native AwContents instance into this
@@ -3376,7 +3376,7 @@
         // Only valid within software onDraw().
         private final Rect mClipBoundsTemporary = new Rect();
 
-        @SuppressLint("DrawAllocation") // For new AwGLFunctor.
+        @SuppressLint("DrawAllocation") // For new AwFunctor.
         @Override
         public void onDraw(Canvas canvas) {
             if (isDestroyedOrNoOperation(NO_WARN)) {
@@ -3426,7 +3426,7 @@
             }
             if (did_draw && canvas.isHardwareAccelerated()
                     && !ForceAuxiliaryBitmapRendering.sResult) {
-                did_draw = mDrawFunctor.requestDrawGL(canvas);
+                did_draw = mDrawFunctor.requestDraw(canvas);
             }
             if (did_draw) {
                 int scrollXDiff = mContainerView.getScrollX() - scrollX;
@@ -3773,7 +3773,8 @@
             InterceptNavigationDelegate navigationInterceptionDelegate,
             AutofillProvider autofillProvider);
     private native WebContents nativeGetWebContents(long nativeAwContents);
-    private native void nativeSetAwGLFunctor(long nativeAwContents, long nativeAwGLFunctor);
+    private native void nativeSetCompositorFrameConsumer(
+            long nativeAwContents, long nativeCompositorFrameConsumer);
 
     private native void nativeDocumentHasImages(long nativeAwContents, Message message);
     private native void nativeGenerateMHTML(
diff --git a/android_webview/java/src/org/chromium/android_webview/AwFunctor.java b/android_webview/java/src/org/chromium/android_webview/AwFunctor.java
new file mode 100644
index 0000000..16aebf0
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/AwFunctor.java
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview;
+
+import android.graphics.Canvas;
+
+/**
+ * Interface for functor implementation. This allows client to avoid differentiating between GL and
+ * Vulkan implementations.
+ */
+public interface AwFunctor {
+    /** Insert draw functor into recording canvas */
+    boolean requestDraw(Canvas canvas);
+
+    /** Return the raw native pointer to CompositorFrameConsumer */
+    long getNativeCompositorFrameConsumer();
+
+    /** Free memory */
+    void trimMemory();
+
+    /** Destroy on UI thread. Client should stop using CompositorFrameConsumer before this */
+    void destroy();
+}
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 2893030..ec1bd80 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwGLFunctor.java
@@ -19,7 +19,7 @@
  * the render node hierarchy.
  */
 @JNINamespace("android_webview")
-public class AwGLFunctor {
+public class AwGLFunctor implements AwFunctor {
     private final long mNativeAwGLFunctor;
     private final AwContents.NativeDrawGLFunctor mNativeDrawGLFunctor;
     private final ViewGroup mContainerView;
@@ -41,7 +41,8 @@
         addReference();
     }
 
-    public void releasedByContents() {
+    @Override
+    public void destroy() {
         assert mRefCount > 0;
         removeReference();
     }
@@ -50,12 +51,14 @@
         return nativeGetAwDrawGLFunction();
     }
 
-    public long getNativeAwGLFunctor() {
+    @Override
+    public long getNativeCompositorFrameConsumer() {
         assert mRefCount > 0;
-        return mNativeAwGLFunctor;
+        return nativeGetCompositorFrameConsumer(mNativeAwGLFunctor);
     }
 
-    public boolean requestDrawGL(Canvas canvas) {
+    @Override
+    public boolean requestDraw(Canvas canvas) {
         assert mRefCount > 0;
         boolean success = mNativeDrawGLFunctor.requestDrawGL(canvas, mFunctorReleasedCallback);
         if (success && mFunctorReleasedCallback != null) {
@@ -91,7 +94,8 @@
         mContainerView.invalidate();
     }
 
-    public void deleteHardwareRenderer() {
+    @Override
+    public void trimMemory() {
         assert mRefCount > 0;
         nativeDeleteHardwareRenderer(mNativeAwGLFunctor);
     }
@@ -107,6 +111,7 @@
 
     private native void nativeDeleteHardwareRenderer(long nativeAwGLFunctor);
     private native long nativeGetAwDrawGLViewContext(long nativeAwGLFunctor);
+    private native long nativeGetCompositorFrameConsumer(long nativeAwGLFunctor);
 
     private static native long nativeGetAwDrawGLFunction();
     private static native void nativeDestroy(long nativeAwGLFunctor);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index cc3ebc9..bf3ad51c 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -262,6 +262,8 @@
     "cancel_mode.h",
     "cast_config_controller.cc",
     "cast_config_controller.h",
+    "contained_shell/contained_shell_controller.cc",
+    "contained_shell/contained_shell_controller.h",
     "dbus/ash_dbus_services.cc",
     "dbus/ash_dbus_services.h",
     "dbus/display_service_provider.cc",
@@ -1636,6 +1638,8 @@
     "assistant/util/deep_link_util_unittest.cc",
     "autoclick/autoclick_drag_event_rewriter_unittest.cc",
     "autoclick/autoclick_unittest.cc",
+    "contained_shell/mock_contained_shell_client.cc",
+    "contained_shell/mock_contained_shell_client.h",
     "cursor_unittest.cc",
     "detachable_base/detachable_base_handler_unittest.cc",
     "detachable_base/detachable_base_notification_controller_unittest.cc",
@@ -1948,6 +1952,7 @@
     "//net:net",
     "//services/catalog:lib",
     "//services/media_session/public/cpp/test:test_support",
+    "//services/media_session/public/mojom",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/ws:test_support",
     "//services/ws/public/cpp/input_devices:test_support",
diff --git a/ash/contained_shell/OWNERS b/ash/contained_shell/OWNERS
new file mode 100644
index 0000000..d95f00a
--- /dev/null
+++ b/ash/contained_shell/OWNERS
@@ -0,0 +1,3 @@
+qnnguyen@chromium.org
+michaelpg@chromium.org
+jdufault@chromium.org
diff --git a/ash/contained_shell/contained_shell_controller.cc b/ash/contained_shell/contained_shell_controller.cc
new file mode 100644
index 0000000..a8aa3cf
--- /dev/null
+++ b/ash/contained_shell/contained_shell_controller.cc
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/contained_shell/contained_shell_controller.h"
+
+#include <utility>
+
+namespace ash {
+
+ContainedShellController::ContainedShellController() = default;
+
+ContainedShellController::~ContainedShellController() = default;
+
+void ContainedShellController::BindRequest(
+    mojom::ContainedShellControllerRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void ContainedShellController::LaunchContainedShell() {
+  // TODO(crbug/902571): Implement launch by dispatching to a
+  // ContainedShellClient method.
+  NOTIMPLEMENTED();
+}
+
+void ContainedShellController::SetClient(
+    mojom::ContainedShellClientPtr client) {
+  contained_shell_client_ = std::move(client);
+}
+
+}  // namespace ash
diff --git a/ash/contained_shell/contained_shell_controller.h b/ash/contained_shell/contained_shell_controller.h
new file mode 100644
index 0000000..4d379230
--- /dev/null
+++ b/ash/contained_shell/contained_shell_controller.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_CONTAINED_SHELL_CONTAINED_SHELL_CONTROLLER_H_
+#define ASH_CONTAINED_SHELL_CONTAINED_SHELL_CONTROLLER_H_
+
+#include "ash/ash_export.h"
+#include "ash/public/interfaces/contained_shell.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace ash {
+
+// ContainedShellController allows a consumer of ash to provide a
+// ContainedShellClient, to which we dispatch requests.
+// TODO(910226): remove this code once the ContainedShell demo is complete and
+// no longer needed.
+class ASH_EXPORT ContainedShellController
+    : public mojom::ContainedShellController {
+ public:
+  ContainedShellController();
+  ~ContainedShellController() override;
+
+  // Binds the mojom::ContainedShellController interface to this object.
+  void BindRequest(mojom::ContainedShellControllerRequest request);
+
+  // Starts the ContainedShell feature by sending LaunchContainedShell
+  // request to ContainedShellClient.
+  void LaunchContainedShell();
+
+  // mojom::ContainedShellController:
+  void SetClient(mojom::ContainedShellClientPtr client) override;
+
+ private:
+  mojom::ContainedShellClientPtr contained_shell_client_;
+  mojo::BindingSet<mojom::ContainedShellController> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContainedShellController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_CONTAINED_SHELL_CONTAINED_SHELL_CONTROLLER_H
diff --git a/ash/contained_shell/mock_contained_shell_client.cc b/ash/contained_shell/mock_contained_shell_client.cc
new file mode 100644
index 0000000..6a2bd52e
--- /dev/null
+++ b/ash/contained_shell/mock_contained_shell_client.cc
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/contained_shell/mock_contained_shell_client.h"
+
+#include "ash/contained_shell/contained_shell_controller.h"
+#include "ash/shell.h"
+
+namespace ash {
+
+MockContainedShellClient::MockContainedShellClient() = default;
+
+MockContainedShellClient::~MockContainedShellClient() = default;
+
+mojom::ContainedShellClientPtr
+MockContainedShellClient::CreateInterfacePtrAndBind() {
+  mojom::ContainedShellClientPtr ptr;
+  binding_.Bind(mojo::MakeRequest(&ptr));
+  return ptr;
+}
+
+std::unique_ptr<MockContainedShellClient> BindMockContainedShellClient() {
+  auto client = std::make_unique<MockContainedShellClient>();
+  Shell::Get()->contained_shell_controller()->SetClient(
+      client->CreateInterfacePtrAndBind());
+  return client;
+}
+
+}  // namespace ash
diff --git a/ash/contained_shell/mock_contained_shell_client.h b/ash/contained_shell/mock_contained_shell_client.h
new file mode 100644
index 0000000..5ef731b
--- /dev/null
+++ b/ash/contained_shell/mock_contained_shell_client.h
@@ -0,0 +1,36 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_CONTAINED_SHELL_MOCK_CONTAINED_SHELL_CLIENT_H_
+#define ASH_CONTAINED_SHELL_MOCK_CONTAINED_SHELL_CLIENT_H_
+
+#include <memory>
+
+#include "ash/public/interfaces/contained_shell.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ash {
+
+class MockContainedShellClient : public mojom::ContainedShellClient {
+ public:
+  MockContainedShellClient();
+  ~MockContainedShellClient() override;
+
+  mojom::ContainedShellClientPtr CreateInterfacePtrAndBind();
+
+ private:
+  mojo::Binding<mojom::ContainedShellClient> binding_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(MockContainedShellClient);
+};
+
+// Helper function to bind a ContainedShellClient so that it receives mojo
+// calls.
+std::unique_ptr<MockContainedShellClient> BindMockContainedShellClient();
+
+}  // namespace ash
+
+#endif  // ASH_CONTAINED_SHELL_MOCK_CONTAINED_SHELL_CLIENT_H
diff --git a/ash/manifest.json b/ash/manifest.json
index 4e0cbe0..0ae64eb1 100644
--- a/ash/manifest.json
+++ b/ash/manifest.json
@@ -22,6 +22,7 @@
           "ash.mojom.AssistantSetupController",
           "ash.mojom.AssistantVolumeControl",
           "ash.mojom.CastConfig",
+          "ash.mojom.ContainedShellController",
           "ash.mojom.CrosDisplayConfigController",
           "ash.mojom.DockedMagnifierController",
           "ash.mojom.EventRewriterController",
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index 28fa45c9..fd3d03e 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -14,6 +14,7 @@
 #include "ash/assistant/assistant_screen_context_controller.h"
 #include "ash/assistant/assistant_setup_controller.h"
 #include "ash/cast_config_controller.h"
+#include "ash/contained_shell/contained_shell_controller.h"
 #include "ash/display/ash_display_controller.h"
 #include "ash/display/cros_display_config.h"
 #include "ash/display/display_output_protection.h"
@@ -122,6 +123,11 @@
   Shell::Get()->cast_config()->BindRequest(std::move(request));
 }
 
+void BindContainedShellControllerRequestOnMainThread(
+    mojom::ContainedShellControllerRequest request) {
+  Shell::Get()->contained_shell_controller()->BindRequest(std::move(request));
+}
+
 void BindDisplayOutputProtectionRequestOnMainThread(
     mojom::DisplayOutputProtectionRequest request) {
   Shell::Get()->display_output_protection()->BindRequest(std::move(request));
@@ -281,6 +287,11 @@
       main_thread_task_runner);
   registry->AddInterface(base::BindRepeating(&BindCastConfigOnMainThread),
                          main_thread_task_runner);
+  if (base::FeatureList::IsEnabled(features::kContainedShell)) {
+    registry->AddInterface(
+        base::BindRepeating(&BindContainedShellControllerRequestOnMainThread),
+        main_thread_task_runner);
+  }
   registry->AddInterface(
       base::BindRepeating(&BindDisplayOutputProtectionRequestOnMainThread),
       main_thread_task_runner);
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 9cf3eff..3588974 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -58,6 +58,9 @@
 const base::Feature kUnlockWithExternalBinary{
     "UnlockWithExternalBinary", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kContainedShell{"ContainedShell",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kViewsLogin{"ViewsLogin", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kUseBluetoothSystemInAsh{"UseBluetoothSystemInAsh",
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index c3572cef..52e5f084 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -76,6 +76,9 @@
 // Enables running an external binary which provides lock screen authentication.
 ASH_PUBLIC_EXPORT extern const base::Feature kUnlockWithExternalBinary;
 
+// Enables the ContainedShell feature.
+ASH_PUBLIC_EXPORT extern const base::Feature kContainedShell;
+
 // Enables views login.
 ASH_PUBLIC_EXPORT extern const base::Feature kViewsLogin;
 
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index d44aa88..f5984197 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -26,6 +26,7 @@
     "assistant_volume_control.mojom",
     "cast_config.mojom",
     "constants.mojom",
+    "contained_shell.mojom",
     "cros_display_config.mojom",
     "display_output_protection.mojom",
     "docked_magnifier_controller.mojom",
diff --git a/ash/public/interfaces/contained_shell.mojom b/ash/public/interfaces/contained_shell.mojom
new file mode 100644
index 0000000..516ab2d7
--- /dev/null
+++ b/ash/public/interfaces/contained_shell.mojom
@@ -0,0 +1,18 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ash.mojom;
+
+// Performs browser-side functionality for the ContainedShell feature,
+// e.g. launching a WebView to host the ContainedShell.
+interface ContainedShellClient {
+};
+
+// Allows Ash and its consumers to interact with the ContainedShell
+// feature, e.g. by requesting to launch the ContainedShell WebView.
+// These requests are forwarded to the ContainedShellClient.
+interface ContainedShellController {
+  // Provides a client for dispatching requests.
+  SetClient(ContainedShellClient client);
+};
diff --git a/ash/shell.cc b/ash/shell.cc
index 74bed486..85d6497 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -23,6 +23,7 @@
 #include "ash/autoclick/autoclick_controller.h"
 #include "ash/cast_config_controller.h"
 #include "ash/components/tap_visualizer/public/mojom/constants.mojom.h"
+#include "ash/contained_shell/contained_shell_controller.h"
 #include "ash/dbus/ash_dbus_services.h"
 #include "ash/detachable_base/detachable_base_handler.h"
 #include "ash/detachable_base/detachable_base_notification_controller.h"
@@ -656,6 +657,7 @@
           std::make_unique<system::BrightnessControllerChromeos>()),
       cast_config_(std::make_unique<CastConfigController>()),
       connector_(connector),
+      contained_shell_controller_(std::make_unique<ContainedShellController>()),
       first_run_helper_(std::make_unique<FirstRunHelper>()),
       focus_cycler_(std::make_unique<FocusCycler>()),
       ime_controller_(std::make_unique<ImeController>()),
@@ -1427,6 +1429,11 @@
   // Disable drag-and-drop during OOBE and GAIA login screens by only enabling
   // the controller when the session is active. https://crbug.com/464118
   drag_drop_controller_->set_enabled(is_session_active);
+
+  if (base::FeatureList::IsEnabled(features::kContainedShell) &&
+      is_session_active) {
+    contained_shell_controller_->LaunchContainedShell();
+  }
 }
 
 void Shell::OnLoginStatusChanged(LoginStatus login_status) {
diff --git a/ash/shell.h b/ash/shell.h
index f9854c3..ffe673c 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -103,6 +103,7 @@
 class BrightnessControlDelegate;
 class CastConfigController;
 class DisplayOutputProtection;
+class ContainedShellController;
 class CrosDisplayConfig;
 class DetachableBaseHandler;
 class DetachableBaseNotificationController;
@@ -357,6 +358,9 @@
   }
   CastConfigController* cast_config() { return cast_config_.get(); }
   service_manager::Connector* connector() { return connector_; }
+  ContainedShellController* contained_shell_controller() {
+    return contained_shell_controller_.get();
+  }
   CrosDisplayConfig* cros_display_config() {
     return cros_display_config_.get();
   }
@@ -720,6 +724,7 @@
   std::unique_ptr<CastConfigController> cast_config_;
   std::unique_ptr<CrosDisplayConfig> cros_display_config_;
   service_manager::Connector* const connector_;
+  std::unique_ptr<ContainedShellController> contained_shell_controller_;
   std::unique_ptr<DetachableBaseHandler> detachable_base_handler_;
   std::unique_ptr<DetachableBaseNotificationController>
       detachable_base_notification_controller_;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 902e44c..9f5395e 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2077,6 +2077,7 @@
 
   if (is_android) {
     deps += [ "//testing/android/native_test:native_test_native_code" ]
+    shard_timeout = 600
   }
 }
 
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index 761d3cb8..dcc9f8d1 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -300,16 +300,6 @@
 #endif
 }
 
-// static
-MessageLoopCurrentForUI MessageLoopForUI::current() {
-  return MessageLoopCurrentForUI::Get();
-}
-
-// static
-bool MessageLoopForUI::IsCurrent() {
-  return MessageLoopCurrentForUI::IsSet();
-}
-
 #if defined(OS_IOS)
 void MessageLoopForUI::Attach() {
   backend_->AttachToMessagePump();
@@ -338,17 +328,4 @@
 
 #endif  // !defined(OS_NACL)
 
-//------------------------------------------------------------------------------
-// MessageLoopForIO
-
-// static
-MessageLoopCurrentForIO MessageLoopForIO::current() {
-  return MessageLoopCurrentForIO::Get();
-}
-
-// static
-bool MessageLoopForIO::IsCurrent() {
-  return MessageLoopCurrentForIO::IsSet();
-}
-
 }  // namespace base
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 04d45ae..1adfd4cd 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -403,10 +403,6 @@
  public:
   explicit MessageLoopForUI(Type type = TYPE_UI);
 
-  // TODO(gab): Mass migrate callers to MessageLoopCurrentForUI::Get()/IsSet().
-  static MessageLoopCurrentForUI current();
-  static bool IsCurrent();
-
 #if defined(OS_IOS)
   // On iOS, the main message loop cannot be Run().  Instead call Attach(),
   // which connects this MessageLoop to the UI thread's CFRunLoop and allows
@@ -456,10 +452,6 @@
 class BASE_EXPORT MessageLoopForIO : public MessageLoop {
  public:
   MessageLoopForIO() : MessageLoop(TYPE_IO) {}
-
-  // TODO(gab): Mass migrate callers to MessageLoopCurrentForIO::Get()/IsSet().
-  static MessageLoopCurrentForIO current();
-  static bool IsCurrent();
 };
 
 // Do not add any member variables to MessageLoopForIO!  This is important b/c
diff --git a/base/power_monitor/power_monitor_device_source_win.cc b/base/power_monitor/power_monitor_device_source_win.cc
index e74be50..37a4462 100644
--- a/base/power_monitor/power_monitor_device_source_win.cc
+++ b/base/power_monitor/power_monitor_device_source_win.cc
@@ -65,7 +65,7 @@
 
 PowerMonitorDeviceSource::PowerMessageWindow::PowerMessageWindow()
     : instance_(NULL), message_hwnd_(NULL) {
-  if (!MessageLoopForUI::IsCurrent()) {
+  if (!MessageLoopCurrentForUI::IsSet()) {
     // Creating this window in (e.g.) a renderer inhibits shutdown on Windows.
     // See http://crbug.com/230122. TODO(vandebo): http://crbug.com/236031
     DLOG(ERROR)
diff --git a/base/test/fuzzed_data_provider.h b/base/test/fuzzed_data_provider.h
index f133959..19e829f 100644
--- a/base/test/fuzzed_data_provider.h
+++ b/base/test/fuzzed_data_provider.h
@@ -87,7 +87,7 @@
       abort();
 
     // Use the biggest type possible to hold the range and the result.
-    uint64_t range = max - min;
+    uint64_t range = static_cast<uint64_t>(max) - min;
     uint64_t result = 0;
     size_t offset = 0;
 
@@ -108,7 +108,7 @@
     if (range != std::numeric_limits<decltype(range)>::max())
       result = result % (range + 1);
 
-    return min + static_cast<T>(result);
+    return static_cast<T>(min + result);
   }
 
   // Returns a std::string of length from 0 to |max_length|. When it runs out of
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index a228216..86062857 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -302,7 +302,7 @@
 #if defined(OS_POSIX) && !defined(OS_NACL)
   // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API.
   std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher;
-  if (MessageLoopForIO::IsCurrent()) {
+  if (MessageLoopCurrentForIO::IsSet()) {
     file_descriptor_watcher.reset(
         new FileDescriptorWatcher(message_loop_->task_runner()));
   }
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index 3da9414e..fabe15f6 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -527,6 +527,7 @@
 
 def WriteDepfile(depfile_path, first_gn_output, inputs=None, add_pydeps=True):
   assert depfile_path != first_gn_output  # http://crbug.com/646165
+  assert not isinstance(inputs, basestring)  # Easy mistake to make
   inputs = inputs or []
   if add_pydeps:
     inputs = _ComputePythonDependencies() + inputs
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 701e77e..3b4d641 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -119,6 +119,8 @@
     <ignore regexp="content/public/android/java/res/drawable-xxxhdpi"/>
     <ignore regexp="ui/android/java/res/drawable-xxhdpi"/>
     <ignore regexp="ui/android/java/res/drawable-xxxhdpi"/>
+    <!-- This is intentional to reduce APK size. See: http://crrev/c/1352161 -->
+    <ignore regexp="chrome/android/java/res_autofill_assistant/drawable-*"/>
   </issue>
   <issue id="IconDipSize">
     <ignore regexp="chromecast/internal"/>
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index 6ffce53b..f9caabc 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -32,6 +32,9 @@
   parser.add_argument('--gtest_repeat',
                       help='GTest repeat value to use. This also disables the '
                            'test launcher timeout.')
+  parser.add_argument('--test-launcher-retry-limit',
+                      help='Number of times that test suite will retry failing '
+                           'tests. This is multiplicative with --gtest_repeat.')
   parser.add_argument('--gtest_break_on_failure', action='store_true',
                       default=False,
                       help='Should GTest break on failure; useful with '
@@ -79,6 +82,9 @@
   if args.gtest_repeat:
     child_args.append('--gtest_repeat=' + args.gtest_repeat)
     child_args.append('--test-launcher-timeout=-1')
+  if args.test_launcher_retry_limit:
+    child_args.append(
+        '--test-launcher-retry-limit=' + args.test_launcher_retry_limit)
   if args.gtest_break_on_failure:
     child_args.append('--gtest_break_on_failure')
   if args.child_args:
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
index c6b2aad..7065ccd 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.feed;
 
+import android.support.annotation.NonNull;
+
+import com.google.android.libraries.feed.api.stream.ScrollListener;
 import com.google.android.libraries.feed.host.logging.ActionType;
 import com.google.android.libraries.feed.host.logging.BasicLoggingApi;
 import com.google.android.libraries.feed.host.logging.ContentLoggingData;
@@ -36,14 +39,20 @@
 
     /** Cleans up native half of this bridge. */
     public void destroy() {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeDestroy(mNativeFeedLoggingBridge);
         mNativeFeedLoggingBridge = 0;
     }
 
     @Override
     public void onContentViewed(ContentLoggingData data) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnContentViewed(mNativeFeedLoggingBridge, data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()),
                 TimeUnit.SECONDS.toMillis(data.getTimeContentBecameAvailable()), data.getScore());
@@ -51,24 +60,39 @@
 
     @Override
     public void onContentDismissed(ContentLoggingData data) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnContentDismissed(
                 mNativeFeedLoggingBridge, data.getPositionInStream(), data.getRepresentationUri());
     }
 
     @Override
-    public void onContentSwiped(ContentLoggingData data) {}
+    public void onContentSwiped(ContentLoggingData data) {
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
+        nativeOnContentSwiped(mNativeFeedLoggingBridge);
+    }
 
     @Override
     public void onContentClicked(ContentLoggingData data) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnContentClicked(mNativeFeedLoggingBridge, data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()), data.getScore());
     }
 
     @Override
     public void onClientAction(ContentLoggingData data, @ActionType int actionType) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         recordUserAction(actionType);
         nativeOnClientAction(
                 mNativeFeedLoggingBridge, feedActionToWindowOpenDisposition(actionType));
@@ -76,43 +100,67 @@
 
     @Override
     public void onContentContextMenuOpened(ContentLoggingData data) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnContentContextMenuOpened(mNativeFeedLoggingBridge, data.getPositionInStream(),
                 TimeUnit.SECONDS.toMillis(data.getPublishedTimeSeconds()), data.getScore());
     }
 
     @Override
     public void onMoreButtonViewed(int position) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnMoreButtonViewed(mNativeFeedLoggingBridge, position);
     }
 
     @Override
     public void onMoreButtonClicked(int position) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnMoreButtonClicked(mNativeFeedLoggingBridge, position);
     }
 
     @Override
     public void onOpenedWithContent(int timeToPopulateMs, int contentCount) {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnOpenedWithContent(mNativeFeedLoggingBridge, timeToPopulateMs, contentCount);
     }
 
     @Override
     public void onOpenedWithNoImmediateContent() {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnOpenedWithNoImmediateContent(mNativeFeedLoggingBridge);
     }
 
     @Override
     public void onOpenedWithNoContent() {
-        assert mNativeFeedLoggingBridge != 0;
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
         nativeOnOpenedWithNoContent(mNativeFeedLoggingBridge);
     }
 
     @Override
-    public void onSpinnerShown(int timeShownMs, @SpinnerType int spinnerType) {}
+    public void onSpinnerShown(int timeShownMs, @SpinnerType int spinnerType) {
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
+        nativeOnSpinnerShown(mNativeFeedLoggingBridge, timeShownMs);
+    }
 
     /**
      * Reports how long a user spends on the page.
@@ -168,12 +216,46 @@
         }
     }
 
+    private void reportScrolledAfterOpen() {
+        // Bridge could have been destroyed for policy when this is called.
+        // See https://crbug.com/901414.
+        if (mNativeFeedLoggingBridge == 0) return;
+
+        nativeReportScrolledAfterOpen(mNativeFeedLoggingBridge);
+    }
+
+    /**
+     * One-shot reporter that records the first time the user scrolls in the {@link Stream}.
+     */
+    public static class ScrollEventReporter implements ScrollListener {
+        private final FeedLoggingBridge mLoggingBridge;
+        private boolean mFired;
+
+        public ScrollEventReporter(@NonNull FeedLoggingBridge loggingBridge) {
+            super();
+            mLoggingBridge = loggingBridge;
+        }
+
+        @Override
+        public void onScrollStateChanged(@ScrollState int state) {
+            if (mFired) return;
+            if (state != ScrollState.DRAGGING) return;
+
+            mLoggingBridge.reportScrolledAfterOpen();
+            mFired = true;
+        }
+
+        @Override
+        public void onScrolled(int dx, int dy) {}
+    }
+
     private native long nativeInit(Profile profile);
     private native void nativeDestroy(long nativeFeedLoggingBridge);
     private native void nativeOnContentViewed(long nativeFeedLoggingBridge, int position,
             long publishedTimeMs, long timeContentBecameAvailableMs, float score);
     private native void nativeOnContentDismissed(
             long nativeFeedLoggingBridge, int position, String uri);
+    private native void nativeOnContentSwiped(long nativeFeedLoggingBridge);
     private native void nativeOnContentClicked(
             long nativeFeedLoggingBridge, int position, long publishedTimeMs, float score);
     private native void nativeOnClientAction(
@@ -186,6 +268,8 @@
             long nativeFeedLoggingBridge, int timeToPopulateMs, int contentCount);
     private native void nativeOnOpenedWithNoImmediateContent(long nativeFeedLoggingBridge);
     private native void nativeOnOpenedWithNoContent(long nativeFeedLoggingBridge);
+    private native void nativeOnSpinnerShown(long nativeFeedLoggingBridge, long spinnerShownTimeMs);
     private native void nativeOnContentTargetVisited(
             long nativeFeedLoggingBridge, long visitTimeMs, boolean isOffline, boolean returnToNtp);
+    private native void nativeReportScrolledAfterOpen(long nativeFeedLoggingBridge);
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 692bdc0c..585dfc3 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -282,6 +282,13 @@
         mMediator.onThumbnailCaptured();
     }
 
+    /**
+     * @return The {@link StreamLifecycleManager} that manages the lifecycle of the {@link Stream}.
+     */
+    StreamLifecycleManager getStreamLifecycleManager() {
+        return mStreamLifecycleManager;
+    }
+
     /** @return The {@link Stream} that this class holds. */
     Stream getStream() {
         return mStream;
@@ -343,6 +350,7 @@
         if (mSigninPromoView != null) UiUtils.removeViewFromParent(mSigninPromoView);
         mStream.setHeaderViews(Arrays.asList(new NonDismissibleHeader(mNewTabPageLayout),
                 new NonDismissibleHeader(mSectionHeaderView)));
+        mStream.addScrollListener(new FeedLoggingBridge.ScrollEventReporter(loggingBridge));
         // Explicitly request focus on the scroll container to avoid UrlBar being focused after
         // the scroll container for policy is removed.
         view.requestFocus();
@@ -362,6 +370,9 @@
     void createScrollViewForPolicy() {
         if (mStream != null) {
             mRootView.removeView(mStream.getView());
+            assert mStreamLifecycleManager
+                    != null
+                : "StreamLifecycleManager should not be null when the Stream is not null.";
             mStreamLifecycleManager.destroy();
             mStreamLifecycleManager = null;
             // Do not call mStream.onDestroy(), the mStreamLifecycleManager has done that for us.
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPageMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPageMediator.java
index ec22014..5b1cefff 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPageMediator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPageMediator.java
@@ -152,14 +152,20 @@
         if (stream == null) return;
 
         stream.removeScrollListener(mStreamScrollListener);
-        stream.removeOnContentChangedListener(mStreamContentChangedListener);
-        MemoryPressureListener.removeCallback(mMemoryPressureCallback);
-        if (mSignInPromo != null) mSignInPromo.destroy();
-        mPrefChangeRegistrar.removeObserver(Pref.NTP_ARTICLES_LIST_VISIBLE);
         mStreamScrollListener = null;
+
+        stream.removeOnContentChangedListener(mStreamContentChangedListener);
         mStreamContentChangedListener = null;
+
+        MemoryPressureListener.removeCallback(mMemoryPressureCallback);
         mMemoryPressureCallback = null;
-        mSignInPromo = null;
+
+        if (mSignInPromo != null) {
+            mSignInPromo.destroy();
+            mSignInPromo = null;
+        }
+
+        mPrefChangeRegistrar.removeObserver(Pref.NTP_ARTICLES_LIST_VISIBLE);
     }
 
     /**
@@ -178,6 +184,7 @@
         if (mSignInPromo != null) {
             mSignInPromo.setCanShowPersonalizedSuggestions(suggestionsVisible);
         }
+        if (suggestionsVisible) mCoordinator.getStreamLifecycleManager().activate();
         mStreamContentChanged = true;
     }
 
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index a8dc1433..03b0288 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -29,9 +29,20 @@
 public class FeedProcessScopeFactory {
     private static final String TAG = "FeedProcessScopeFtry";
 
-    /** Flag that tracks whether we've ever been disabled via enterprise policy. Should only be
-     * accessed through isFeedProcessScopeEnabled(). */
+    /**
+     * Flag that tracks whether we've ever been disabled via enterprise policy. Should only be
+     * accessed through isFeedProcessScopeEnabled().
+     */
     private static boolean sEverDisabledForPolicy;
+
+    /**
+     * Tracks whether the article suggestions should be visible to the user during the current
+     * session. If user opts in to the suggestions during the current session, the suggestions
+     * services will be immediately warmed up. If user opts out during the current session,
+     * the suggestions services will not shut down until the next session.
+     */
+    private static boolean sArticlesVisibleDuringSession;
+
     private static PrefChangeRegistrar sPrefChangeRegistrar;
     private static FeedAppLifecycle sFeedAppLifecycle;
     private static FeedProcessScope sFeedProcessScope;
@@ -109,6 +120,8 @@
                 && sFeedAppLifecycle == null && sFeedLoggingBridge == null;
         if (!isFeedProcessEnabled()) return;
 
+        sArticlesVisibleDuringSession =
+                PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE);
         sPrefChangeRegistrar = new PrefChangeRegistrar();
         sPrefChangeRegistrar.addObserver(Pref.NTP_ARTICLES_SECTION_ENABLED,
                 FeedProcessScopeFactory::articlesEnabledPrefChange);
@@ -193,6 +206,21 @@
         destroy();
     }
 
+    /**
+     * @return Whether article suggestions are prepared to be shown based on user preference. If
+     *         article suggestions are set hidden within a session, this will still return true
+     *         until the next restart.
+     */
+    static boolean areArticlesVisibleDuringSession() {
+        // Skip the native call if sArticlesVisibleDuringSession is already true to reduce overhead.
+        if (!sArticlesVisibleDuringSession
+                && PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE)) {
+            sArticlesVisibleDuringSession = true;
+        }
+
+        return sArticlesVisibleDuringSession;
+    }
+
     private static void articlesEnabledPrefChange() {
         // Should only be subscribed while it was enabled. A change should mean articles are now
         // disabled.
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java
index b268868..0fd5515 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java
@@ -136,8 +136,11 @@
     /** @return Whether the {@link Stream} can be shown. */
     private boolean canShow() {
         final int state = ApplicationStatus.getStateForActivity(mActivity);
+        // We don't call Stream#onShow to prevent feed services from being warmed up if the user
+        // has opted out from article suggestions during the previous session.
         return (mStreamState == CREATED || mStreamState == HIDDEN) && !mTab.isHidden()
-                && (state == ActivityState.STARTED || state == ActivityState.RESUMED);
+                && (state == ActivityState.STARTED || state == ActivityState.RESUMED)
+                && FeedProcessScopeFactory.areArticlesVisibleDuringSession();
     }
 
     /** Calls {@link Stream#onShow()}. */
@@ -150,15 +153,17 @@
 
     /** @return Whether the {@link Stream} can be activated. */
     private boolean canActivate() {
-        return mStreamState != ACTIVE && mStreamState != DESTROYED && mTab.isUserInteractable()
-                && ApplicationStatus.getStateForActivity(mActivity) == ActivityState.RESUMED;
+        return (mStreamState == SHOWN || mStreamState == INACTIVE) && mTab.isUserInteractable()
+                && ApplicationStatus.getStateForActivity(mActivity) == ActivityState.RESUMED
+                && FeedProcessScopeFactory.areArticlesVisibleDuringSession();
     }
 
     /** Calls {@link Stream#onActive()}. */
-    private void activate() {
+    void activate() {
+        // Make sure the Stream can be shown and is set shown before setting it to active state.
+        show();
         if (!canActivate()) return;
 
-        show();
         mStreamState = ACTIVE;
         mStream.onActive();
     }
@@ -175,6 +180,7 @@
     private void hide() {
         if (mStreamState == HIDDEN || mStreamState == CREATED || mStreamState == DESTROYED) return;
 
+        // Make sure the Stream is inactive before setting it to hidden state.
         deactivate();
         mStreamState = HIDDEN;
         // Save instance state as the Stream begins to hide. This matches the activity lifecycle
@@ -190,6 +196,7 @@
     void destroy() {
         if (mStreamState == DESTROYED) return;
 
+        // Make sure the Stream is hidden before setting it to destroyed state.
         hide();
         mStreamState = DESTROYED;
         mTab.removeObserver(mTabObserver);
diff --git a/chrome/android/java/res_autofill_assistant/drawable-hdpi/onboarding_background.png b/chrome/android/java/res_autofill_assistant/drawable-hdpi/onboarding_background.png
deleted file mode 100644
index 3ea9224d..0000000
--- a/chrome/android/java/res_autofill_assistant/drawable-hdpi/onboarding_background.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-mdpi/onboarding_background.png b/chrome/android/java/res_autofill_assistant/drawable-mdpi/onboarding_background.png
index 79b5754..9812e98 100644
--- a/chrome/android/java/res_autofill_assistant/drawable-mdpi/onboarding_background.png
+++ b/chrome/android/java/res_autofill_assistant/drawable-mdpi/onboarding_background.png
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-xhdpi/onboarding_background.png b/chrome/android/java/res_autofill_assistant/drawable-xhdpi/onboarding_background.png
deleted file mode 100644
index 8bbacec..0000000
--- a/chrome/android/java/res_autofill_assistant/drawable-xhdpi/onboarding_background.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-xxhdpi/onboarding_background.png b/chrome/android/java/res_autofill_assistant/drawable-xxhdpi/onboarding_background.png
index e691cdb..d9fa3c3 100644
--- a/chrome/android/java/res_autofill_assistant/drawable-xxhdpi/onboarding_background.png
+++ b/chrome/android/java/res_autofill_assistant/drawable-xxhdpi/onboarding_background.png
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/drawable-xxxhdpi/onboarding_background.png b/chrome/android/java/res_autofill_assistant/drawable-xxxhdpi/onboarding_background.png
deleted file mode 100644
index e40b1f7..0000000
--- a/chrome/android/java/res_autofill_assistant/drawable-xxxhdpi/onboarding_background.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res_autofill_assistant/layout/init_screen.xml b/chrome/android/java/res_autofill_assistant/layout/init_screen.xml
index d95aac7..cd93f12 100644
--- a/chrome/android/java/res_autofill_assistant/layout/init_screen.xml
+++ b/chrome/android/java/res_autofill_assistant/layout/init_screen.xml
@@ -38,10 +38,13 @@
         <ImageView
             tools:ignore="contentDescription"
             android:layout_width="250dp"
-            android:layout_height="170dp"
+            android:layout_height="141dp"
             android:scaleType="centerCrop"
             android:src="@drawable/onboarding_background"
-            android:paddingStart="50dp"/>
+            android:paddingStart="25dp"
+            android:paddingEnd="25dp"
+            android:paddingTop="10dp"
+            android:paddingBottom="15dp"/>
 
         <TextView
             android:layout_height="wrap_content"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index 8b9039e..c97fe4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -52,6 +52,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.webapps.GooglePlayWebApkInstallDelegate;
 import org.chromium.chrome.browser.webauth.Fido2ApiHandler;
+import org.chromium.chrome.browser.widget.FeatureHighlightProvider;
 import org.chromium.components.signin.AccountManagerDelegate;
 import org.chromium.components.signin.SystemAccountManagerDelegate;
 import org.chromium.policy.AppRestrictionsProvider;
@@ -326,6 +327,13 @@
     }
 
     /**
+     * @return A new {@link FeatureHighlightProvider}.
+     */
+    public FeatureHighlightProvider createFeatureHighlightProvider() {
+        return new FeatureHighlightProvider();
+    }
+
+    /**
      * Checks the Google Play services availability on the this device.
      *
      * This is a workaround for the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
index d3069106..202ee81c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
@@ -108,6 +108,30 @@
     private static final float GENERATED_ICON_PADDING_RATIO = 1.0f / 12.0f;
     private static final float GENERATED_ICON_FONT_SIZE_RATIO = 1.0f / 3.0f;
 
+    // Constants for figuring out the amount of padding required to transform a web manifest
+    // maskable icon to an Android adaptive icon.
+    //
+    // The web standard for maskable icons specifies a larger safe zone inside the icon
+    // than Android adaptive icons define. Therefore we need to pad the image so that
+    // the maskable icon's safe zone is reduced to the dimensions expected by Android. See
+    // https://github.com/w3c/manifest/issues/555#issuecomment-404097653.
+    //
+    // The *_RATIO variables give the diameter of the safe zone divided by the width of the icon.
+    // Sources:
+    // - https://www.w3.org/TR/appmanifest/#icon-masks
+    // - https://medium.com/google-design/designing-adaptive-icons-515af294c783
+    //
+    // We subtract 1 from the scaling factor to give the amount we need to increase by, then divide
+    // it by two to get the amount of padding that we will add to both sides.
+    private static final float MASKABLE_SAFE_ZONE_RATIO = 4.0f / 5.0f;
+    private static final float ADAPTABLE_SAFE_ZONE_RATIO = 66.0f / 108.0f;
+
+    private static final float MASKABLE_TO_ADAPTABLE_SCALING_FACTOR =
+            MASKABLE_SAFE_ZONE_RATIO / ADAPTABLE_SAFE_ZONE_RATIO;
+
+    private static final float MASKABLE_ICON_PADDING_RATIO =
+            (MASKABLE_TO_ADAPTABLE_SCALING_FACTOR - 1.0f) / 2.0f;
+
     // True when Android O's ShortcutManager.requestPinShortcut() is supported.
     private static boolean sIsRequestPinShortcutSupported;
 
@@ -120,13 +144,15 @@
     public static class Delegate {
         /**
          * Request Android to add a shortcut to the home screen.
-         * @param title  Title of the shortcut.
-         * @param icon   Image that represents the shortcut.
-         * @param intent Intent to fire when the shortcut is activated.
+         * @param title Title of the shortcut.
+         * @param icon Image that represents the shortcut.
+         * @param iconAdaptable Whether to create an Android Adaptable icon.
+         * @param shortcutIntent Intent to fire when the shortcut is activated.
          */
-        public void addShortcutToHomescreen(String title, Bitmap icon, Intent shortcutIntent) {
+        public void addShortcutToHomescreen(
+                String title, Bitmap icon, boolean iconAdaptable, Intent shortcutIntent) {
             if (isRequestPinShortcutSupported()) {
-                addShortcutWithShortcutManager(title, icon, shortcutIntent);
+                addShortcutWithShortcutManager(title, icon, iconAdaptable, shortcutIntent);
                 return;
             }
             Intent intent = createAddToHomeIntent(title, icon, shortcutIntent);
@@ -160,9 +186,9 @@
     @CalledByNative
     private static void addWebapp(final String id, final String url, final String scopeUrl,
             final String userTitle, final String name, final String shortName, final String iconUrl,
-            final Bitmap icon, @WebDisplayMode final int displayMode, final int orientation,
-            final int source, final long themeColor, final long backgroundColor,
-            final String splashScreenUrl, final long callbackPointer) {
+            final Bitmap icon, boolean iconAdaptable, @WebDisplayMode final int displayMode,
+            final int orientation, final int source, final long themeColor,
+            final long backgroundColor, final String splashScreenUrl, final long callbackPointer) {
         new AsyncTask<Intent>() {
             @Override
             protected Intent doInBackground() {
@@ -185,7 +211,7 @@
             }
             @Override
             protected void onPostExecute(final Intent resultIntent) {
-                sDelegate.addShortcutToHomescreen(userTitle, icon, resultIntent);
+                sDelegate.addShortcutToHomescreen(userTitle, icon, iconAdaptable, resultIntent);
 
                 // Store the webapp data so that it is accessible without the intent. Once this
                 // process is complete, call back to native code to start the splash image
@@ -208,14 +234,14 @@
      */
     @SuppressWarnings("unused")
     @CalledByNative
-    private static void addShortcut(
-            String id, String url, String userTitle, Bitmap icon, int source) {
+    private static void addShortcut(String id, String url, String userTitle, Bitmap icon,
+            boolean iconAdaptable, int source) {
         Context context = ContextUtils.getApplicationContext();
         final Intent shortcutIntent = createShortcutIntent(url);
         shortcutIntent.putExtra(EXTRA_ID, id);
         shortcutIntent.putExtra(EXTRA_SOURCE, source);
         shortcutIntent.setPackage(context.getPackageName());
-        sDelegate.addShortcutToHomescreen(userTitle, icon, shortcutIntent);
+        sDelegate.addShortcutToHomescreen(userTitle, icon, iconAdaptable, shortcutIntent);
         if (shouldShowToastWhenAddingShortcut()) {
             showAddedToHomescreenToast(userTitle);
         }
@@ -223,14 +249,17 @@
 
     @TargetApi(Build.VERSION_CODES.O)
     private static void addShortcutWithShortcutManager(
-            String title, Bitmap icon, Intent shortcutIntent) {
+            String title, Bitmap bitmap, boolean isMaskableIcon, Intent shortcutIntent) {
         String id = shortcutIntent.getStringExtra(ShortcutHelper.EXTRA_ID);
         Context context = ContextUtils.getApplicationContext();
 
+        Icon icon = isMaskableIcon ? Icon.createWithAdaptiveBitmap(bitmap)
+                                   : Icon.createWithBitmap(bitmap);
+
         ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, id)
                                             .setShortLabel(title)
                                             .setLongLabel(title)
-                                            .setIcon(Icon.createWithBitmap(icon))
+                                            .setIcon(icon)
                                             .setIntent(shortcutIntent)
                                             .build();
         try {
@@ -410,12 +439,12 @@
      * Adapts a website's icon (e.g. favicon or touch icon) to make it suitable for the home screen.
      * This involves adding padding if the icon is a full sized square.
      *
-     * @param context Context used to create the intent.
      * @param webIcon The website's favicon or touch icon.
+     * @param maskable Whether the icon is suitable for creating an adaptive icon.
      * @return Bitmap Either the touch-icon or the newly created favicon.
      */
     @CalledByNative
-    public static Bitmap createHomeScreenIconFromWebIcon(Bitmap webIcon) {
+    public static Bitmap createHomeScreenIconFromWebIcon(Bitmap webIcon, boolean maskable) {
         // getLauncherLargeIconSize() is just a guess at the launcher icon size, and is often
         // wrong -- the launcher can show icons at any size it pleases. Instead of resizing the
         // icon to the supposed launcher size and then having the launcher resize the icon again,
@@ -426,18 +455,21 @@
         int maxInnerSize = Math.round(am.getLauncherLargeIconSize() * MAX_INNER_SIZE_RATIO);
         int innerSize = Math.min(maxInnerSize, Math.max(webIcon.getWidth(), webIcon.getHeight()));
 
-        int outerSize = innerSize;
         Rect innerBounds = new Rect(0, 0, innerSize, innerSize);
+        int padding = 0;
 
-        // Draw the icon with padding around it if all four corners are not transparent. Otherwise,
-        // don't add padding.
-        if (shouldPadIcon(webIcon)) {
-            int padding = Math.round(ICON_PADDING_RATIO * innerSize);
-            outerSize += 2 * padding;
-            innerBounds.offset(padding, padding);
+        if (maskable) {
+            // See comments for MASKABLE_ICON_PADDING_RATIO.
+            padding = Math.round(MASKABLE_ICON_PADDING_RATIO * innerSize);
+        } else if (shouldPadIcon(webIcon)) {
+            // Draw the icon with padding around it if all four corners are not transparent.
+            padding = Math.round(ICON_PADDING_RATIO * innerSize);
         }
 
-        Bitmap bitmap = null;
+        int outerSize = 2 * padding + innerSize;
+        innerBounds.offset(padding, padding);
+
+        Bitmap bitmap;
         try {
             bitmap = Bitmap.createBitmap(outerSize, outerSize, Bitmap.Config.ARGB_8888);
         } catch (OutOfMemoryError e) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserServicesMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserServicesMetrics.java
index 99fefe6..2cfd35d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserServicesMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserServicesMetrics.java
@@ -9,7 +9,6 @@
 
 import org.chromium.base.metrics.CachedMetrics;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.RecordUserAction;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -46,20 +45,6 @@
     }
 
     /**
-     * Records that a Trusted Web Activity has been opened.
-     */
-    public static void recordTwaOpened() {
-        RecordUserAction.record("BrowserServices.TwaOpened");
-    }
-
-    /**
-     * Records the time that a Trusted Web Activity has been open for.
-     */
-    public static void recordTwaOpenTime(long duration, TimeUnit unit) {
-        RecordHistogram.recordTimesHistogram("BrowserServices.TwaOpenTime", duration, unit);
-    }
-
-    /**
      * Returns a {@link TimingMetric} that records the amount of time spent querying the Android
      * system for ResolveInfos that will deal with a given URL when launching from a background
      * service.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java
index ea5ed9a8..4d787176 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java
@@ -7,13 +7,14 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.v7.app.AppCompatActivity;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.preferences.website.SettingsNavigationSource;
 import org.chromium.chrome.browser.preferences.website.SingleCategoryPreferences;
 import org.chromium.chrome.browser.preferences.website.SiteSettingsCategory;
 
@@ -29,16 +30,18 @@
 public class ClearDataDialogActivity extends AppCompatActivity {
     private static final String EXTRA_DOMAINS = "org.chromium.chrome.extra.domains";
     private static final String EXTRA_ORIGINS = "org.chromium.chrome.extra.origins";
+    private static final String EXTRA_APP_UNINSTALLED = "org.chromium.chrome.extra.app_uninstalled";
 
     /**
      * Creates an intent for launching this activity for the TWA client app that was uninstalled or
      * had its data cleared.
      */
     public static Intent createIntent(Context context, Collection<String> linkedDomains,
-            Collection<String> linkedOrigins) {
+            Collection<String> linkedOrigins, boolean appUninstalled) {
         Intent intent = new Intent(context, ClearDataDialogActivity.class);
         intent.putExtra(EXTRA_DOMAINS, new ArrayList<>(linkedDomains));
         intent.putExtra(EXTRA_ORIGINS, new ArrayList<>(linkedOrigins));
+        intent.putExtra(EXTRA_APP_UNINSTALLED, appUninstalled);
         return intent;
     }
 
@@ -51,15 +54,19 @@
                 .setTitle(R.string.twa_clear_data_dialog_title)
                 .setMessage(R.string.twa_clear_data_dialog_message)
                 .setPositiveButton(R.string.preferences, (ignored1, ignored2) -> {
+                    recordDecision(true);
                     openSettings();
                     finish();
                 })
                 .setNegativeButton(R.string.twa_clear_data_dialog_keep_data,
-                        (ignored1, ignored2) -> finish());
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            builder.setOnDismissListener(ignored -> finish());
-        }
+                        (ignored1, ignored2) -> {
+                    recordDecision(false);
+                    finish();
+                })
+                .setOnCancelListener((ignored) -> {
+                    recordDecision(false);
+                    finish();
+                });
 
         builder.create().show();
     }
@@ -86,7 +93,8 @@
     }
 
     private void openSingleWebsitePrefs(String origin) {
-        startActivity(PreferencesLauncher.createIntentForSingleWebsitePreferences(this, origin));
+        startActivity(PreferencesLauncher.createIntentForSingleWebsitePreferences(
+                this, origin, SettingsNavigationSource.TWA_CLEAR_DATA_DIALOG));
     }
 
     private void openFilteredAllSiteSettings(Collection<String> domains) {
@@ -97,7 +105,15 @@
                 getString(R.string.twa_clear_data_site_selection_title));
         extras.putStringArrayList(
                 SingleCategoryPreferences.EXTRA_SELECTED_DOMAINS, new ArrayList<>(domains));
+        extras.putInt(SettingsNavigationSource.EXTRA_KEY,
+                SettingsNavigationSource.TWA_CLEAR_DATA_DIALOG);
 
         PreferencesLauncher.launchSettingsPage(this, SingleCategoryPreferences.class, extras);
     }
+
+    private void recordDecision(boolean accepted) {
+        final boolean appUninstalled = getIntent().getBooleanExtra(EXTRA_APP_UNINSTALLED, false);
+        ChromeApplication.getComponent().resolveTwaClearDataDialogRecorder()
+                .handleDialogResult(accepted, appUninstalled);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorder.java
new file mode 100644
index 0000000..626c5a4
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorder.java
@@ -0,0 +1,76 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.browserservices;
+
+import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_CLEAR_DATA;
+import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_UNINSTALL;
+
+import org.chromium.base.StrictModeContext;
+import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Record the results of showing a clear data dialog on TWA client uninstall or data clear.
+ */
+public class ClearDataDialogResultRecorder {
+    private final Lazy<ChromePreferenceManager> mPrefsManager;
+    private final ChromeBrowserInitializer mBrowserInitializer;
+    private final TrustedWebActivityUmaRecorder mUmaRecorder;
+
+    @Inject
+    public ClearDataDialogResultRecorder(
+            Lazy<ChromePreferenceManager> manager,
+            ChromeBrowserInitializer browserInitializer,
+            TrustedWebActivityUmaRecorder umaRecorder) {
+        mPrefsManager = manager;
+        mBrowserInitializer = browserInitializer;
+        mUmaRecorder = umaRecorder;
+    }
+
+    /**
+     * Called when the dialog is closed.
+     * @param accepted Whether positive button was clicked.
+     * @param triggeredByUninstall Whether the dialog was triggered by uninstall.
+     */
+    public void handleDialogResult(boolean accepted, boolean triggeredByUninstall) {
+        if (accepted || mBrowserInitializer.hasNativeInitializationCompleted()) {
+            // If accepted, native is going to be loaded for the settings.
+            mBrowserInitializer.runNowOrAfterNativeInitialization(() ->
+                    mUmaRecorder.recordClearDataDialogAction(accepted,
+                            triggeredByUninstall));
+        } else {
+            // Avoid loading native just for the sake of recording. Save the info and record
+            // on next Chrome launch.
+            String key = triggeredByUninstall ? TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_UNINSTALL
+                    : TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_CLEAR_DATA;
+
+            try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+                mPrefsManager.get().writeInt(key, mPrefsManager.get().readInt(key) + 1);
+            }
+        }
+    }
+
+    /**
+     * Make recordings that were deferred in order to not load native.
+     */
+    public void makeDeferredRecordings() {
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+            recordDismissals(TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_UNINSTALL, true);
+            recordDismissals(TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_CLEAR_DATA, false);
+        }
+    }
+
+    private void recordDismissals(String prefKey, boolean triggeredByUninstall) {
+        int times = mPrefsManager.get().readInt(prefKey);
+        for (int i = 0; i < times; i++) {
+            mUmaRecorder.recordClearDataDialogAction(false /*accepted*/, triggeredByUninstall);
+        }
+        mPrefsManager.get().removeKey(prefKey);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java
index 1707605..50e2a34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java
@@ -149,7 +149,8 @@
             // cleaned up.
             Set<String> domains = register.getDomainsForRegisteredUid(uid);
             Set<String> origins = register.getOriginsForRegisteredUid(uid);
-            Intent intent = ClearDataDialogActivity.createIntent(context, domains, origins);
+            Intent intent =
+                    ClearDataDialogActivity.createIntent(context, domains, origins, uninstalled);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
index 3995ea05..1b54e42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.preferences.website.SettingsNavigationSource;
 
 /**
  * Launched by {@link android.support.customtabs.TrustedWebUtils#launchBrowserSiteSettings}.
@@ -39,8 +40,9 @@
     private void verifyOriginAndLaunchSettings() {
         Origin origin = new Origin(getIntent().getData());
         if (isVerifiedOrigin(origin)) {
+            new TrustedWebActivityUmaRecorder().recordOpenedSettingsViaManageSpace();
             startActivity(PreferencesLauncher.createIntentForSingleWebsitePreferences(this,
-                    origin.toString()));
+                    origin.toString(), SettingsNavigationSource.TWA_MANAGE_SPACE_ACTIVITY));
         } else {
             logVerificationFailed();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
index 2fa8f0b..5a4deba4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.browserservices;
 
+import static org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder.DelegatedNotificationSmallIconFallback.FALLBACK_ICON_NOT_PROVIDED;
+import static org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder.DelegatedNotificationSmallIconFallback.NO_FALLBACK;
+
 import android.app.Notification;
 import android.content.ComponentName;
 import android.content.Context;
@@ -23,6 +26,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder.DelegatedNotificationSmallIconFallback;
 import org.chromium.chrome.browser.notifications.NotificationBuilderBase;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 
@@ -34,12 +38,17 @@
  */
 public class TrustedWebActivityClient {
     private final TrustedWebActivityServiceConnectionManager mConnection;
+    private final TrustedWebActivityUmaRecorder mRecorder;
+    private final NotificationUmaTracker mNotificationUmaTracker;
 
     /**
      * Creates a TrustedWebActivityService.
      */
-    public TrustedWebActivityClient(TrustedWebActivityServiceConnectionManager connection) {
+    public TrustedWebActivityClient(TrustedWebActivityServiceConnectionManager connection,
+            TrustedWebActivityUmaRecorder recorder, NotificationUmaTracker notificationUmaTracker) {
         mConnection = connection;
+        mRecorder = recorder;
+        mNotificationUmaTracker = notificationUmaTracker;
     }
 
     /**
@@ -70,27 +79,31 @@
 
             Notification notification = builder.build();
 
-            boolean success =
-                    service.notify(platformTag, platformId, notification, channelDisplayName);
+            service.notify(platformTag, platformId, notification, channelDisplayName);
 
-            if (success) {
-                NotificationUmaTracker.getInstance().onNotificationShown(
-                        NotificationUmaTracker.SystemNotificationType.SITES, notification);
-            }
+            mNotificationUmaTracker.onNotificationShown(
+                    NotificationUmaTracker.SystemNotificationType.TRUSTED_WEB_ACTIVITY_SITES,
+                    notification);
         });
     }
 
     private void fallbackToIconFromServiceIfNecessary(NotificationBuilderBase builder,
             TrustedWebActivityServiceWrapper service) throws RemoteException {
         if (builder.hasSmallIconForContent() && builder.hasStatusBarIconBitmap()) {
+            recordFallback(NO_FALLBACK);
             return;
         }
 
         int id = service.getSmallIconId();
         if (id == TrustedWebActivityService.NO_ID) {
+            recordFallback(FALLBACK_ICON_NOT_PROVIDED);
             return;
         }
 
+        recordFallback(builder.hasSmallIconForContent()
+                ? DelegatedNotificationSmallIconFallback.FALLBACK_FOR_STATUS_BAR
+                : DelegatedNotificationSmallIconFallback.FALLBACK_FOR_STATUS_BAR_AND_CONTENT);
+
         Bitmap bitmap = service.getSmallIconBitmap();
         if (!builder.hasStatusBarIconBitmap()) {
             builder.setStatusBarIconForUntrustedRemoteApp(id, bitmap,
@@ -101,6 +114,10 @@
         }
     }
 
+    private void recordFallback(@DelegatedNotificationSmallIconFallback int fallback) {
+        mRecorder.recordDelegatedNotificationSmallIconFallback(fallback);
+    }
+
     /**
      * Cancels a notification through a Trusted Web Activity client.
      * @param scope The scope of the Service Worker that triggered the notification.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUmaRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUmaRecorder.java
new file mode 100644
index 0000000..036c970
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUmaRecorder.java
@@ -0,0 +1,121 @@
+// 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.browserservices;
+
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.tab.Tab;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+/**
+ * Encapsulates Uma recording actions related to Trusted Web Activities.
+ */
+public class TrustedWebActivityUmaRecorder {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DelegatedNotificationSmallIconFallback.NO_FALLBACK,
+            DelegatedNotificationSmallIconFallback.FALLBACK_ICON_NOT_PROVIDED,
+            DelegatedNotificationSmallIconFallback.FALLBACK_FOR_STATUS_BAR,
+            DelegatedNotificationSmallIconFallback.FALLBACK_FOR_STATUS_BAR_AND_CONTENT})
+    public @interface DelegatedNotificationSmallIconFallback {
+        int NO_FALLBACK = 0;
+        int FALLBACK_ICON_NOT_PROVIDED = 1;
+        int FALLBACK_FOR_STATUS_BAR = 2;
+        int FALLBACK_FOR_STATUS_BAR_AND_CONTENT = 3;
+        int NUM_ENTRIES = 4;
+    }
+
+    @Inject
+    public TrustedWebActivityUmaRecorder() {}
+
+    /**
+     * Records that a Trusted Web Activity has been opened.
+     */
+    public void recordTwaOpened(@Nullable Tab tab) {
+        RecordUserAction.record("BrowserServices.TwaOpened");
+        if (tab != null) {
+            new UkmRecorder.Bridge().recordTwaOpened(tab);
+        }
+    }
+
+    /**
+     * Records the time that a Trusted Web Activity has been in resumed state.
+     */
+    public void recordTwaOpenTime(long durationMs) {
+        recordDuration(durationMs, "BrowserServices.TwaOpenTime");
+    }
+
+    /**
+     * Records the time spent in verified origin until navigating to unverified one or pausing
+     * the Trusted Web Activity.
+     */
+    public void recordTimeInVerifiedOrigin(long durationMs) {
+        recordDuration(durationMs, "TrustedWebActivity.TimeInVerifiedOrigin");
+    }
+
+    /**
+     * Records the time spent in verified origin until navigating to unverified one or pausing
+     * the Trusted Web Activity.
+     */
+    public void recordTimeOutOfVerifiedOrigin(long durationMs) {
+        recordDuration(durationMs, "TrustedWebActivity.TimeOutOfVerifiedOrigin");
+    }
+
+    private void recordDuration(long durationMs, String histogramName) {
+        RecordHistogram.recordTimesHistogram(histogramName, durationMs, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Records the fact that disclosure was shown.
+     */
+    public void recordDisclosureShown() {
+        RecordUserAction.record("TrustedWebActivity.DisclosureShown");
+    }
+
+    /**
+     * Records the fact that disclosure was accepted by user.
+     */
+    public void recordDisclosureAccepted() {
+        RecordUserAction.record("TrustedWebActivity.DisclosureAccepted");
+    }
+
+    /**
+     * Records which action the user took upon seeing a clear data dialog.
+     * @param accepted Whether user proceeded to the settings from the dialog.
+     * @param triggeredByUninstall Whether the dialog was triggered by app uninstall as opposed to
+     * app data getting cleared.
+     */
+    public void recordClearDataDialogAction(boolean accepted, boolean triggeredByUninstall) {
+        String histogramName = triggeredByUninstall
+                ? "TrustedWebActivity.ClearDataDialogOnUninstallAccepted"
+                : "TrustedWebActivity.ClearDataDialogOnClearAppDataAccepted";
+        RecordHistogram.recordBooleanHistogram(histogramName, accepted);
+    }
+
+    /**
+     * Records the fact that site settings were opened via "Manage Space" button in TWA client app's
+     * settings.
+     */
+    public void recordOpenedSettingsViaManageSpace() {
+        RecordUserAction.record("TrustedWebActivity.OpenedSettingsViaManageSpace");
+    }
+
+    /**
+     * Records which fallback (if any) was used for the small icon of a delegated notification.
+     */
+    public void recordDelegatedNotificationSmallIconFallback(
+            @DelegatedNotificationSmallIconFallback int fallback) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "TrustedWebActivity.DelegatedNotificationSmallIconFallback", fallback,
+                DelegatedNotificationSmallIconFallback.NUM_ENTRIES);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/UkmRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/UkmRecorder.java
index e2ded9f..1eff79c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/UkmRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/UkmRecorder.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeCall;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.WebContents;
 
 /**
@@ -14,9 +15,9 @@
  */
 public interface UkmRecorder {
     /**
-     * Records a TWA has been opened.
+     * Records a TWA has been opened in given tab.
      */
-    void recordTwaOpened(WebContents webContents);
+    void recordTwaOpened(Tab tab);
 
     /**
      * The actual recorder.
@@ -24,8 +25,8 @@
     @JNINamespace("browserservices")
     class Bridge implements UkmRecorder {
         @Override
-        public void recordTwaOpened(WebContents webContents) {
-            nativeRecordOpen(webContents);
+        public void recordTwaOpened(Tab tab) {
+            nativeRecordOpen(tab.getWebContents());
         }
 
         @NativeCall("Bridge")
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java
index db3f629..98142806 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureController.java
@@ -11,6 +11,7 @@
 import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel.DISCLOSURE_STATE_SHOWN;
 import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VERIFICATION_FAILURE;
 
+import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
@@ -28,16 +29,19 @@
     private final ChromePreferenceManager mPreferenceManager;
     private final TrustedWebActivityModel mModel;
     private final TrustedWebActivityVerifier mVerifier;
+    private final TrustedWebActivityUmaRecorder mRecorder;
 
     @Inject
     TrustedWebActivityDisclosureController(
             ChromePreferenceManager preferenceManager,
             TrustedWebActivityModel model,
             ActivityLifecycleDispatcher lifecycleDispatcher,
-            TrustedWebActivityVerifier verifier) {
+            TrustedWebActivityVerifier verifier,
+            TrustedWebActivityUmaRecorder recorder) {
         mVerifier = verifier;
         mPreferenceManager = preferenceManager;
         mModel = model;
+        mRecorder = recorder;
         model.set(DISCLOSURE_EVENTS_CALLBACK, this);
         verifier.addVerificationObserver(this::onVerificationStatusChanged);
         lifecycleDispatcher.register(this);
@@ -53,6 +57,7 @@
 
     @Override
     public void onDisclosureAccepted() {
+        mRecorder.recordDisclosureAccepted();
         mPreferenceManager.setUserAcceptedTwaDisclosureForPackage(mVerifier.getClientPackageName());
         mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_DISMISSED_BY_USER);
     }
@@ -60,6 +65,7 @@
     /** Shows the disclosure if it is not already showing and hasn't been accepted. */
     private void showIfNeeded() {
         if (!isShowing() && !wasDismissed()) {
+            mRecorder.recordDisclosureShown();
             mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_SHOWN);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java
index c6bead01..9b4c45e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorder.java
@@ -4,15 +4,18 @@
 
 package org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller;
 
+import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VERIFICATION_PENDING;
+import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VERIFICATION_SUCCESS;
+
 import android.os.SystemClock;
 
-import org.chromium.chrome.browser.browserservices.BrowserServicesMetrics;
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
+import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 
-import java.util.concurrent.TimeUnit;
-
 import javax.inject.Inject;
 
 /**
@@ -20,11 +23,27 @@
  */
 @ActivityScope
 public class TrustedWebActivityOpenTimeRecorder implements PauseResumeWithNativeObserver {
+    private final TrustedWebActivityVerifier mVerifier;
+    private final TrustedWebActivityUmaRecorder mRecorder;
+    private final ActivityTabProvider mTabProvider;
+
     private long mOnResumeTimestampMs;
+    private long mLastStateChangeTimestampMs;
+
+    private boolean mInVerifiedOrigin;
+    private boolean mTwaOpenedRecorded;
 
     @Inject
-    TrustedWebActivityOpenTimeRecorder(ActivityLifecycleDispatcher lifecycleDispatcher) {
+    TrustedWebActivityOpenTimeRecorder(
+            ActivityLifecycleDispatcher lifecycleDispatcher,
+            TrustedWebActivityVerifier verifier,
+            TrustedWebActivityUmaRecorder recorder,
+            ActivityTabProvider provider) {
+        mVerifier = verifier;
+        mRecorder = recorder;
+        mTabProvider = provider;
         lifecycleDispatcher.register(this);
+        verifier.addVerificationObserver(this::onVerificationStateChanged);
     }
 
     @Override
@@ -35,8 +54,40 @@
     @Override
     public void onPauseWithNative() {
         assert mOnResumeTimestampMs != 0;
-        BrowserServicesMetrics.recordTwaOpenTime(
-                SystemClock.elapsedRealtime() - mOnResumeTimestampMs, TimeUnit.MILLISECONDS);
+        mRecorder.recordTwaOpenTime(SystemClock.elapsedRealtime() - mOnResumeTimestampMs);
+        recordTimeCurrentState();
         mOnResumeTimestampMs = 0;
     }
+
+    private void onVerificationStateChanged() {
+        VerificationState state = mVerifier.getState();
+        if (state == null || state.status == VERIFICATION_PENDING) {
+            return;
+        }
+        boolean inVerifiedOrigin = state.status == VERIFICATION_SUCCESS;
+        if (inVerifiedOrigin == mInVerifiedOrigin) {
+            return;
+        }
+        recordTimeCurrentState();
+        mInVerifiedOrigin = inVerifiedOrigin;
+        mLastStateChangeTimestampMs = SystemClock.elapsedRealtime();
+
+        if (mInVerifiedOrigin && !mTwaOpenedRecorded) {
+            mRecorder.recordTwaOpened(mTabProvider.getActivityTab());
+            mTwaOpenedRecorded = true;
+        }
+    }
+
+    private void recordTimeCurrentState() {
+        if (mLastStateChangeTimestampMs == 0) {
+            return;
+        }
+        long timeInCurrentState = SystemClock.elapsedRealtime()
+                - Math.max(mLastStateChangeTimestampMs, mOnResumeTimestampMs);
+        if (mInVerifiedOrigin) {
+            mRecorder.recordTimeInVerifiedOrigin(timeInCurrentState);
+        } else {
+            mRecorder.recordTimeOutOfVerifiedOrigin(timeInCurrentState);
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
index 582d670..cf06271 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
@@ -11,7 +11,6 @@
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.browserservices.BrowserServicesMetrics;
 import org.chromium.chrome.browser.browserservices.Origin;
 import org.chromium.chrome.browser.browserservices.OriginVerifier;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
@@ -162,7 +161,6 @@
         new OriginVerifier((packageName2, origin2, verified, online) -> {
             if (!origin.equals(new Origin(tab.getUrl()))) return;
 
-            BrowserServicesMetrics.recordTwaOpened();
             handleVerificationResult(verified, origin);
         }, mClientPackageName, RELATIONSHIP).start(origin);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java
index 706d6aa6..49b7654d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/PersistentNotificationView.java
@@ -38,6 +38,7 @@
 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.preferences.website.SettingsNavigationSource;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -164,7 +165,7 @@
 
         private PendingIntent makeManageDataIntent() {
             Intent settingsIntent = PreferencesLauncher.createIntentForSingleWebsitePreferences(
-                    mAppContext, mOrigin.toString());
+                    mAppContext, mOrigin.toString(), SettingsNavigationSource.OTHER);
             return PendingIntent.getActivity(mAppContext, 0, settingsIntent, FLAG_UPDATE_CURRENT);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java
index e2a6d1c..fe65f00 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.dependency_injection;
 
 import org.chromium.chrome.browser.AppHooksModule;
+import org.chromium.chrome.browser.browserservices.ClearDataDialogResultRecorder;
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule;
 import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitor;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
@@ -32,6 +33,7 @@
 
     CustomTabsConnection resolveCustomTabsConnection();
     ChromePreferenceManager resolvePreferenceManager();
+    ClearDataDialogResultRecorder resolveTwaClearDataDialogRecorder();
 
     // Temporary getters for DI migration process. All of these getters
     // should eventually be replaced with constructor injection.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
index 102a7b04..fc16f638 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
@@ -12,6 +12,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitor;
 import org.chromium.chrome.browser.contextual_suggestions.EnabledStateMonitorImpl;
+import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.profiles.Profile;
 
@@ -51,4 +52,9 @@
     public Context provideContext() {
         return ContextUtils.getApplicationContext();
     }
+
+    @Provides
+    public ChromeBrowserInitializer provideChromeBrowserInitializer() {
+        return ChromeBrowserInitializer.getInstance();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index 532c41b1..cc136f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -429,6 +429,10 @@
             MediaViewerUtils.updateMediaLauncherActivityEnabled(
                     ContextUtils.getApplicationContext());
         });
+
+        deferredStartupHandler.addDeferredTask(
+                ChromeApplication.getComponent().resolveTwaClearDataDialogRecorder()
+                        ::makeDeferredRecordings);
     }
 
     private void initChannelsAsync() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
index 850da54..9d7f617 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
@@ -30,6 +30,7 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient;
+import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
 import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
@@ -132,7 +133,9 @@
             mNotificationManager = new NotificationManagerProxyImpl(context);
         }
         mTwaClient = new TrustedWebActivityClient(
-                new TrustedWebActivityServiceConnectionManager(context));
+                new TrustedWebActivityServiceConnectionManager(context),
+                new TrustedWebActivityUmaRecorder(),
+                NotificationUmaTracker.getInstance());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
index 2df9608e..a6ded43 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
@@ -41,7 +41,8 @@
             SystemNotificationType.MEDIA, SystemNotificationType.SITES, SystemNotificationType.SYNC,
             SystemNotificationType.WEBAPK, SystemNotificationType.BROWSER_ACTIONS,
             SystemNotificationType.WEBAPP_ACTIONS,
-            SystemNotificationType.OFFLINE_CONTENT_SUGGESTION})
+            SystemNotificationType.OFFLINE_CONTENT_SUGGESTION,
+            SystemNotificationType.TRUSTED_WEB_ACTIVITY_SITES})
     @Retention(RetentionPolicy.SOURCE)
     public @interface SystemNotificationType {
         int DOWNLOAD_FILES = 0;
@@ -60,8 +61,9 @@
         int BROWSER_ACTIONS = 10;
         int WEBAPP_ACTIONS = 11;
         int OFFLINE_CONTENT_SUGGESTION = 12;
+        int TRUSTED_WEB_ACTIVITY_SITES = 13;
 
-        int NUM_ENTRIES = 13;
+        int NUM_ENTRIES = 14;
     }
 
     private static final String LAST_SHOWN_NOTIFICATION_TYPE_KEY =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index ec6c1cc..0d6d7744 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -232,6 +232,15 @@
     public static final String LATEST_UNSUPPORTED_VERSION = "android_os_unsupported_chrome_version";
 
     /**
+     * Keys for deferred recording of the outcomes of showing the clear data dialog after
+     * Trusted Web Activity client apps are uninstalled or have their data cleared.
+     */
+    public static final String TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_UNINSTALL =
+            "twa_dialog_number_of_dismissals_on_uninstall";
+    public static final String TWA_DIALOG_NUMBER_OF_DIMSISSALS_ON_CLEAR_DATA =
+            "twa_dialog_number_of_dismissals_on_clear_data";
+
+    /**
      * Deprecated keys for Chrome Home.
      */
     private static final String CHROME_HOME_USER_ENABLED_KEY = "chrome_home_user_enabled";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java
index 32f0911..bd2263d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PreferencesLauncher.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.browser.AppHooks;
 import org.chromium.chrome.browser.preferences.autofill.AutofillPaymentMethodsFragment;
 import org.chromium.chrome.browser.preferences.autofill.AutofillProfilesFragment;
+import org.chromium.chrome.browser.preferences.website.SettingsNavigationSource;
 import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.content_public.browser.WebContents;
@@ -94,10 +95,11 @@
     /**
      * Creates an intent to launch single website preferences for the specified {@param url}.
      */
-    public static Intent createIntentForSingleWebsitePreferences(Context context, String url) {
-        return createIntentForSettingsPage(
-                context, SingleWebsitePreferences.class.getName(),
-                SingleWebsitePreferences.createFragmentArgsForSite(url));
+    public static Intent createIntentForSingleWebsitePreferences(
+            Context context, String url, @SettingsNavigationSource int navigationSource) {
+        Bundle args = SingleWebsitePreferences.createFragmentArgsForSite(url);
+        args.putInt(SettingsNavigationSource.EXTRA_KEY, navigationSource);
+        return createIntentForSettingsPage(context, SingleWebsitePreferences.class.getName(), args);
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SettingsNavigationSource.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SettingsNavigationSource.java
new file mode 100644
index 0000000..b4fe981
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SettingsNavigationSource.java
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences.website;
+
+import android.support.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Sources of navigation into settings screens, used for UMA tracking purposes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+        SettingsNavigationSource.OTHER,
+        SettingsNavigationSource.TWA_CLEAR_DATA_DIALOG,
+        SettingsNavigationSource.TWA_MANAGE_SPACE_ACTIVITY,
+})
+public @interface SettingsNavigationSource {
+    int OTHER = 0;
+    int TWA_CLEAR_DATA_DIALOG = 1;
+    int TWA_MANAGE_SPACE_ACTIVITY = 2;
+    int NUM_ENTRIES = 3;
+
+    // The key of the intent extra that is used for passing around the source.
+    String EXTRA_KEY = "org.chromium.chrome.preferences.navigation_source";
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
index 1b68318f..9f0b26f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
@@ -382,6 +382,9 @@
             } else {
                 website.putSiteAddressIntoExtras(SingleWebsitePreferences.EXTRA_SITE_ADDRESS);
             }
+            int navigationSource = getArguments().getInt(
+                    SettingsNavigationSource.EXTRA_KEY, SettingsNavigationSource.OTHER);
+            website.getExtras().putInt(SettingsNavigationSource.EXTRA_KEY, navigationSource);
         }
 
         return super.onPreferenceTreeClick(screen, preference);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
index d6e2be8b..eaf74787 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
@@ -25,6 +25,7 @@
 import android.widget.ListView;
 
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ContentSettingsType;
 import org.chromium.chrome.browser.browserservices.Origin;
@@ -799,6 +800,11 @@
 
         mSiteDataCleaner.clearData(mSite, mDataClearedCallback);
 
+        int navigationSource = getArguments().getInt(
+                SettingsNavigationSource.EXTRA_KEY, SettingsNavigationSource.OTHER);
+        RecordHistogram.recordEnumeratedHistogram("SingleWebsitePreferences.NavigatedFromToReset",
+                navigationSource, SettingsNavigationSource.NUM_ENTRIES);
+
         if (finishActivityImmediately) {
             getActivity().finish();
         }
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 b339876..0c41146 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
@@ -96,12 +96,13 @@
 
         mMenuButton = toolbarRoot.findViewById(R.id.menu_button_wrapper);
 
-        final View iphAnchor = toolbarRoot.findViewById(R.id.bottom_toolbar_container);
+        final View iphAnchor = toolbarRoot.findViewById(R.id.search_accelerator);
         tabProvider.addObserverAndTrigger(new HintlessActivityTabObserver() {
             @Override
             public void onActivityTabChanged(Tab tab) {
                 if (tab == null) return;
-                mMediator.showIPH(iphAnchor, TrackerFactory.getTrackerForProfile(tab.getProfile()));
+                mMediator.showIPH(tab.getActivity(), iphAnchor,
+                        TrackerFactory.getTrackerForProfile(tab.getProfile()));
                 tabProvider.removeObserver(this);
             }
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
index 103528b..c5cb254 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BrowsingModeBottomToolbarMediator.java
@@ -8,7 +8,9 @@
 import android.content.res.Resources;
 import android.view.View;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager.OverlayPanelManagerObserver;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
@@ -21,13 +23,12 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
 import org.chromium.chrome.browser.toolbar.ThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.ThemeColorProvider.ThemeColorObserver;
-import org.chromium.chrome.browser.widget.textbubble.TextBubble;
+import org.chromium.chrome.browser.widget.FeatureHighlightProvider;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.resources.ResourceManager;
-import org.chromium.ui.widget.ViewRectProvider;
 
 /**
  * This class is responsible for reacting to events from the outside world, interacting with other
@@ -41,6 +42,9 @@
     /** The amount of time to show the Duet help bubble for. */
     private static final int DUET_IPH_BUBBLE_SHOW_DURATION_MS = 6000;
 
+    /** The transparency fraction of the IPH bubble. */
+    private static final float DUET_IPH_BUBBLE_ALPHA_FRACTION = 0.9f;
+
     /** The model for the browsing mode bottom toolbar that holds all of its state. */
     private BrowsingModeBottomToolbarModel mModel;
 
@@ -122,19 +126,27 @@
 
     /**
      * Maybe show the IPH bubble for Chrome Duet.
+     * @param activity An activity to attach the IPH to.
      * @param anchor The view to anchor the IPH to.
      * @param tracker A tracker for IPH.
      */
-    void showIPH(View anchor, Tracker tracker) {
-        if (tracker.shouldTriggerHelpUI(FeatureConstants.CHROME_DUET_FEATURE)) {
-            TextBubble bubble =
-                    new TextBubble(anchor.getContext(), anchor, R.string.iph_duet_icons_moved,
-                            R.string.iph_duet_icons_moved, true, new ViewRectProvider(anchor));
-            bubble.setAutoDismissTimeout(DUET_IPH_BUBBLE_SHOW_DURATION_MS);
-            bubble.addOnDismissListener(
-                    () -> tracker.dismissed(FeatureConstants.CHROME_DUET_FEATURE));
-            bubble.show();
-        }
+    void showIPH(ChromeActivity activity, View anchor, Tracker tracker) {
+        if (!tracker.shouldTriggerHelpUI(FeatureConstants.CHROME_DUET_FEATURE)) return;
+        int baseColor =
+                ApiCompatibilityUtils.getColor(anchor.getResources(), R.color.modern_blue_600);
+
+        // Clear out the alpha and use custom transparency.
+        int finalColor =
+                (baseColor & 0x00FFFFFF) | ((int) (DUET_IPH_BUBBLE_ALPHA_FRACTION * 255) << 24);
+
+        FeatureHighlightProvider.getInstance().buildForView(activity, anchor,
+                R.string.iph_duet_start_search, FeatureHighlightProvider.TextAlignment.CENTER,
+                R.style.WhiteTitle1, R.string.iph_duet_icons_moved,
+                FeatureHighlightProvider.TextAlignment.CENTER, R.style.WhiteBody, finalColor,
+                DUET_IPH_BUBBLE_SHOW_DURATION_MS);
+
+        anchor.postDelayed(() -> tracker.dismissed(FeatureConstants.CHROME_DUET_FEATURE),
+                DUET_IPH_BUBBLE_SHOW_DURATION_MS);
     }
 
     boolean isVisible() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenDialog.java
index f160378c..cad435d8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenDialog.java
@@ -4,9 +4,12 @@
 
 package org.chromium.chrome.browser.webapps;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.DialogInterface;
 import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Build;
 import android.support.v7.app.AlertDialog;
 import android.text.Editable;
 import android.text.TextUtils;
@@ -229,13 +232,27 @@
 
     /**
      * Called when the home screen icon is available. Must be called after onUserTitleAvailable().
-     * @param icon Icon to use in the launcher.
+     * @param icon that will be used in the launcher.
      */
     public void onIconAvailable(Bitmap icon) {
+        mIconView.setImageBitmap(icon);
+        setIconAvailable();
+    }
+
+    /**
+     * Called when the home screen icon is available and was generated to be an Android adaptable
+     * icon. Must be called after onUserTitleAvailable().
+     * @param icon that will be used in the launcher.
+     */
+    @TargetApi(Build.VERSION_CODES.O)
+    public void onAdaptableIconAvailable(Bitmap icon) {
+        mIconView.setImageIcon(Icon.createWithAdaptiveBitmap(icon));
+        setIconAvailable();
+    }
+
+    private void setIconAvailable() {
         mProgressBarView.setVisibility(View.GONE);
         mIconView.setVisibility(View.VISIBLE);
-        mIconView.setImageBitmap(icon);
-
         mHasIcon = true;
         updateAddButtonEnabledState();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java
index 549e0fa..e6f07d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenManager.java
@@ -6,6 +6,7 @@
 
 import android.app.Activity;
 import android.graphics.Bitmap;
+import android.os.Build;
 import android.text.TextUtils;
 
 import org.chromium.base.annotations.CalledByNative;
@@ -93,8 +94,13 @@
     }
 
     @CalledByNative
-    private void onIconAvailable(Bitmap icon) {
-        mDialog.onIconAvailable(icon);
+    private void onIconAvailable(Bitmap icon, boolean iconAdaptable) {
+        if (iconAdaptable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            mDialog.onAdaptableIconAvailable(icon);
+        } else {
+            assert !iconAdaptable : "Adaptive icons should not be provided pre-Android O.";
+            mDialog.onIconAvailable(icon);
+        }
     }
 
     private native long nativeInitializeAndStart(WebContents webContents);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/FeatureHighlightProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/FeatureHighlightProvider.java
new file mode 100644
index 0000000..da2327f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/FeatureHighlightProvider.java
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.widget;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntDef;
+import android.support.annotation.StringRes;
+import android.support.annotation.StyleRes;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+
+import org.chromium.chrome.browser.AppHooks;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** A means of showing highlight for a particular feature as a form of in product help. */
+public class FeatureHighlightProvider {
+    /**
+     * These values determine text alignment and need to match the values in the closed-source
+     * library.
+     */
+    @IntDef({TextAlignment.START, TextAlignment.CENTER, TextAlignment.END})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TextAlignment {
+        int START = 0;
+        int CENTER = 1;
+        int END = 2;
+    }
+
+    /** Static handle to the sole highlight provider. */
+    private static FeatureHighlightProvider sInstance;
+
+    /**
+     * @return A handle to the highlight provider.
+     */
+    public static FeatureHighlightProvider getInstance() {
+        if (sInstance == null) sInstance = AppHooks.get().createFeatureHighlightProvider();
+        return sInstance;
+    }
+
+    /**
+     * Build and show a feature highlight bubble for a particular view.
+     * @param activity An activity to attach the highlight to.
+     * @param view The view to focus.
+     * @param headTextId The text shown in the header section of the bubble.
+     * @param headAlignment Alignment of the head text.
+     * @param bodyTextId The text shown in the body section of the bubble.
+     * @param bodyAlignment Alignment of the body text.
+     * @param color The color of the bubble.
+     * @param timeoutMs The amount of time in ms before the bubble disappears.
+     */
+    public void buildForView(AppCompatActivity activity, View view, @StringRes int headTextId,
+            @TextAlignment int headAlignment, @StyleRes int headStyle, @StringRes int bodyTextId,
+            @TextAlignment int bodyAlignment, @StyleRes int bodyStyle, @ColorInt int color,
+            long timeoutMs) {}
+}
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 4bcbbc5..9daf9ce 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3926,8 +3926,11 @@
       </message>
 
       <!-- Chrome Duet -->
+      <message name="IDS_IPH_DUET_START_SEARCH" desc="The in-product help message for the split toolbar experiment that explains that the user can start a new web search using the focused icon.">
+        Tap here to quickly begin a search
+      </message>
       <message name="IDS_IPH_DUET_ICONS_MOVED" desc="The in-product help message for the split toolbar experiment that explains that the user's icons (menu button, tab switcher button, and home button) have moved to the bottom of the screen.">
-        Your icons have moved to the bottom of the screen
+        Easily navigate anywhere in Chrome using the buttons at the bottom of your screen
       </message>
 
       <!-- Photo picker -->
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 3f7d803..e0cf3fc 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -185,6 +185,7 @@
   "java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java",
   "java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java",
   "java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java",
+  "java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorder.java",
   "java/src/org/chromium/chrome/browser/browserservices/ClearDataNotificationPublisher.java",
   "java/src/org/chromium/chrome/browser/browserservices/ClearDataService.java",
   "java/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiver.java",
@@ -196,6 +197,7 @@
   "java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java",
   "java/src/org/chromium/chrome/browser/browserservices/Relationship.java",
   "java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java",
+  "java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityUmaRecorder.java",
   "java/src/org/chromium/chrome/browser/browserservices/UkmRecorder.java",
   "java/src/org/chromium/chrome/browser/browserservices/VerificationResultStore.java",
   "java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityCoordinator.java",
@@ -1365,6 +1367,7 @@
   "java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java",
   "java/src/org/chromium/chrome/browser/preferences/website/NotificationCategory.java",
   "java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java",
+  "java/src/org/chromium/chrome/browser/preferences/website/SettingsNavigationSource.java",
   "java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/website/SiteDataCleaner.java",
@@ -1725,6 +1728,7 @@
   "java/src/org/chromium/chrome/browser/widget/FadingEdgeScrollView.java",
   "java/src/org/chromium/chrome/browser/widget/FadingShadow.java",
   "java/src/org/chromium/chrome/browser/widget/FadingShadowView.java",
+  "java/src/org/chromium/chrome/browser/widget/FeatureHighlightProvider.java",
   "java/src/org/chromium/chrome/browser/widget/FullscreenControlContainer.java",
   "java/src/org/chromium/chrome/browser/widget/ListMenuButton.java",
   "java/src/org/chromium/chrome/browser/widget/LoadingView.java",
@@ -2316,6 +2320,7 @@
   "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java",
   "junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java",
   "junit/src/org/chromium/chrome/browser/browseractions/BrowserActionsIntentTest.java",
+  "junit/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorderTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ClearDataNotificationPublisherTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ClearDataServiceTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ClientAppBroadcastReceiverTest.java",
@@ -2325,6 +2330,7 @@
   "junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/ClientAppDataRecorderTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java",
+  "junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java",
   "junit/src/org/chromium/chrome/browser/cached_image_fetcher/CachedImageFetcherImplTest.java",
   "junit/src/org/chromium/chrome/browser/cached_image_fetcher/InMemoryCachedImageFetcherTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimatorTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index df1c767..dc85b54 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -189,7 +189,8 @@
         AppBannerManager.setIsSupported(true);
         ShortcutHelper.setDelegateForTests(new ShortcutHelper.Delegate() {
             @Override
-            public void addShortcutToHomescreen(String title, Bitmap icon, Intent shortcutIntent) {
+            public void addShortcutToHomescreen(
+                    String title, Bitmap icon, boolean iconAdaptive, Intent shortcutIntent) {
                 // Ignore to prevent adding homescreen shortcuts.
             }
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index 6283c259..4a5748b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -26,9 +26,11 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.StandardNotificationBuilder;
 
 import java.util.concurrent.TimeoutException;
@@ -118,10 +120,12 @@
 
     @Before
     public void setUp() throws TimeoutException, RemoteException, InterruptedException {
+        RecordHistogram.setDisabledForTests(true);
         mTargetContext = InstrumentationRegistry.getTargetContext();
         mBuilder = new StandardNotificationBuilder(mTargetContext);
         mClient = new TrustedWebActivityClient(new TrustedWebActivityServiceConnectionManager(
-                ContextUtils.getApplicationContext()));
+                ContextUtils.getApplicationContext()), new TrustedWebActivityUmaRecorder(),
+                NotificationUmaTracker.getInstance());
 
         // TestTrustedWebActivityService is in the test support apk.
         TrustedWebActivityClient.registerClient(mTargetContext, ORIGIN, TEST_SUPPORT_PACKAGE);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedContentStorageConformanceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedContentStorageConformanceTest.java
index f313b01..b26d75c6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedContentStorageConformanceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedContentStorageConformanceTest.java
@@ -37,7 +37,7 @@
 @SmallTest
 @RunWith(ChromeJUnit4ClassRunner.class)
 public final class FeedContentStorageConformanceTest extends ContentStorageConformanceTest {
-    private static final long TIMEOUT = scaleTimeout(1000);
+    private static final long TIMEOUT = scaleTimeout(3000);
 
     class ContentStorageWrapper extends FeedContentStorage {
         public ContentStorageWrapper(Profile p) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
index a7e99ac5..0456028 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
@@ -20,7 +20,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Browser;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.intent.rule.IntentsTestRule;
 import android.support.test.filters.SmallTest;
 import android.support.v7.widget.RecyclerView;
@@ -675,7 +674,7 @@
 
         // Sign in to account. Note that if supervised user is set before sign in, the supervised
         // user setting will be reset.
-        SigninTestUtil.setUpAuthForTest(InstrumentationRegistry.getInstrumentation());
+        SigninTestUtil.setUpAuthForTest();
         final Account account = SigninTestUtil.addTestAccount();
         ThreadUtils.runOnUiThreadBlocking(() -> {
             SigninManager.get().onFirstRunCheckDone();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasicTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasicTest.java
index 37311ba..2be5667c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasicTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesBasicTest.java
@@ -54,7 +54,7 @@
 
     @Before
     public void setUp() throws InterruptedException {
-        SigninTestUtil.setUpAuthForTest(InstrumentationRegistry.getInstrumentation());
+        SigninTestUtil.setUpAuthForTest();
         mActivityTestRule.startMainActivityOnBlankPage();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
index 1db9f72..dd826ef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferencesTest.java
@@ -83,7 +83,7 @@
 
     @Before
     public void setUp() throws Exception {
-        SigninTestUtil.setUpAuthForTest(InstrumentationRegistry.getInstrumentation());
+        SigninTestUtil.setUpAuthForTest();
 
         mActivityTestRule.startMainActivityOnBlankPage();
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
index 3e54939..e843315 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninTest.java
@@ -215,7 +215,7 @@
     @Before
     public void setUp() throws Exception {
         // Mock out the account manager on the device.
-        SigninTestUtil.setUpAuthForTest(InstrumentationRegistry.getInstrumentation());
+        SigninTestUtil.setUpAuthForTest();
 
         mActivityTestRule.startMainActivityOnBlankPage();
         mContext = InstrumentationRegistry.getTargetContext();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
index 174a229..44908cf9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTestRule.java
@@ -271,7 +271,7 @@
         ApplicationTestUtils.clearAppData(InstrumentationRegistry.getTargetContext());
 
         // This must be called before super.setUp() in order for test authentication to work.
-        SigninTestUtil.setUpAuthForTest(InstrumentationRegistry.getInstrumentation());
+        SigninTestUtil.setUpAuthForTest();
     }
 
     private void ruleTearDown() throws Exception {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java
index 34c1b9d..04cd2e5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/DocumentModeAssassinTest.java
@@ -458,7 +458,7 @@
             throws Exception {
         // Load up the metadata file via a TabPersistentStore to make sure that it contains all of
         // the migrated tab information.
-        SigninTestUtil.setUpAuthForTest(InstrumentationRegistry.getInstrumentation());
+        SigninTestUtil.setUpAuthForTest();
         mTestRule.loadNativeLibraryAndInitBrowserProcess();
         TabPersistentStore.setBaseStateDirectoryForTests(mTabbedModeDirectory.getBaseDirectory());
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/test/ChromeBrowserTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ChromeBrowserTestRule.java
index 8dcb8e4..9b8f6361 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/test/ChromeBrowserTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ChromeBrowserTestRule.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.test;
 
-import android.app.Instrumentation;
 import android.support.test.InstrumentationRegistry;
 
 import org.junit.runner.Description;
@@ -19,9 +18,9 @@
  * initializing the AccountManagerFacade.
  */
 public class ChromeBrowserTestRule extends NativeLibraryTestRule {
-    private void setUp(Instrumentation instrumentation) {
+    private void setUp() {
         ApplicationData.clearAppData(InstrumentationRegistry.getTargetContext());
-        SigninTestUtil.setUpAuthForTest(instrumentation);
+        SigninTestUtil.setUpAuthForTest();
         loadNativeLibraryAndInitBrowserProcess();
     }
 
@@ -35,7 +34,7 @@
                  * UI thread).  After loading the library, this will initialize the browser process
                  * if necessary.
                  */
-                setUp(InstrumentationRegistry.getInstrumentation());
+                setUp();
                 try {
                     base.evaluate();
                 } finally {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
index 526dde0..29e0ece 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
@@ -1,5 +1,15 @@
 # XR Instrumentation Tests
 
+## TL;DR For Most Local Repros
+
+1. Get a rooted Pixel device of some sort.
+2. Make sure "VR Services" is up to date in the Playstore.
+3. Run `ninja -C out/Debug chrome_public_test_vr_apk
+        && out/Debug/bin/run_chrome_public_test_vr_apk
+        --num-retries=0
+        --shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json
+        --test-filter=<failing test case>`
+
 ## Introduction
 
 This directory contains all the Java-side infrastructure for running
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java
index df318b9..098f01f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/AddToHomescreenManagerTest.java
@@ -7,6 +7,7 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.os.Build;
 import android.support.test.filters.SmallTest;
 import android.text.TextUtils;
 
@@ -18,6 +19,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.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -82,14 +84,23 @@
             + "<title>" + META_APP_NAME_PAGE_TITLE + "</title>"
             + "</head><body>Webapp capable</body></html>");
 
+    private static final String NON_MASKABLE_MANIFEST_TEST_PAGE_PATH =
+            "/chrome/test/data/banners/manifest_test_page.html";
+    private static final String MASKABLE_MANIFEST_TEST_PAGE_PATH =
+            "/chrome/test/data/banners/manifest_test_page.html?manifest=manifest_maskable.json";
+    private static final String MANIFEST_TEST_PAGE_TITLE = "Web app banner test page";
+
     private static class TestShortcutHelperDelegate extends ShortcutHelper.Delegate {
         public String mRequestedShortcutTitle;
         public Intent mRequestedShortcutIntent;
+        public boolean mRequestedShortcutAdaptable;
 
         @Override
-        public void addShortcutToHomescreen(String title, Bitmap icon, Intent shortcutIntent) {
+        public void addShortcutToHomescreen(
+                String title, Bitmap icon, boolean iconAdaptable, Intent shortcutIntent) {
             mRequestedShortcutTitle = title;
             mRequestedShortcutIntent = shortcutIntent;
+            mRequestedShortcutAdaptable = iconAdaptable;
         }
 
         @Override
@@ -100,6 +111,7 @@
         public void clearRequestedShortcutData() {
             mRequestedShortcutTitle = null;
             mRequestedShortcutIntent = null;
+            mRequestedShortcutAdaptable = false;
         }
     }
 
@@ -200,6 +212,31 @@
     @Test
     @SmallTest
     @Feature("{Webapp}")
+    // The test manifest fulfills the requirements of a WebAPK so disable WebAPKs to force plain old
+    // add to home screen.
+    @CommandLineFlags.Add({"disable-features=ImprovedA2HS"})
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.O)
+    public void testAddAdaptableShortcut() throws Exception {
+        // Test the baseline of no adaptive icon.
+        loadUrl(mTestServerRule.getServer().getURL(NON_MASKABLE_MANIFEST_TEST_PAGE_PATH),
+                MANIFEST_TEST_PAGE_TITLE);
+        addShortcutToTab(mTab, "", true);
+
+        Assert.assertFalse(mShortcutHelperDelegate.mRequestedShortcutAdaptable);
+
+        mShortcutHelperDelegate.clearRequestedShortcutData();
+
+        // Test the adaptive icon.
+        loadUrl(mTestServerRule.getServer().getURL(MASKABLE_MANIFEST_TEST_PAGE_PATH),
+                MANIFEST_TEST_PAGE_TITLE);
+        addShortcutToTab(mTab, "", true);
+
+        Assert.assertTrue(mShortcutHelperDelegate.mRequestedShortcutAdaptable);
+    }
+
+    @Test
+    @SmallTest
+    @Feature("{Webapp}")
     public void testAddBookmarkShortcut() throws Exception {
         loadUrl(NORMAL_HTML, NORMAL_TITLE);
         addShortcutToTab(mTab, "", true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorderTest.java
new file mode 100644
index 0000000..8a88fbb
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorderTest.java
@@ -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.
+
+package org.chromium.chrome.browser.browserservices;
+
+import static org.mockito.AdditionalAnswers.answerVoid;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+
+/**
+ * Tests for {@link ClearDataDialogResultRecorder}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ClearDataDialogResultRecorderTest {
+    private final ChromePreferenceManager mPrefsManager = ChromePreferenceManager.getInstance();
+    @Mock ChromeBrowserInitializer mBrowserInitializer;
+    @Mock TrustedWebActivityUmaRecorder mUmaRecorder;
+    @Captor ArgumentCaptor<Runnable> mTaskOnNativeInitCaptor;
+
+    private ClearDataDialogResultRecorder mRecorder;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        restartApp();
+    }
+
+    @Test
+    public void records_WhenAccepted_AfterNativeInit() {
+        mRecorder.handleDialogResult(true, true);
+        finishNativeInit();
+        verify(mUmaRecorder).recordClearDataDialogAction(true, true);
+    }
+
+    @Test
+    public void records_WhenAccepted_IfNativeAlreadyInited() {
+        finishNativeInit();
+        mRecorder.handleDialogResult(true, true);
+        verify(mUmaRecorder).recordClearDataDialogAction(true, true);
+    }
+
+    @Test
+    public void records_WhenDismissed_IfNativeAlreadyInited() {
+        finishNativeInit();
+        mRecorder.handleDialogResult(false, true);
+        verify(mUmaRecorder).recordClearDataDialogAction(false, true);
+    }
+
+    @Test
+    public void defersRecording_WhenDismissed_IfNativeNotAlreadyInited() {
+        mRecorder.handleDialogResult(false, true);
+        verify(mUmaRecorder, never()).recordClearDataDialogAction(anyBoolean(), anyBoolean());
+    }
+
+    @Test
+    public void makesDeferredRecordingOfDismissals() {
+        mRecorder.handleDialogResult(false, true);
+        restartApp();
+        mRecorder.handleDialogResult(false, true);
+        restartApp();
+        mRecorder.handleDialogResult(false, false);
+        restartApp();
+
+        mRecorder.makeDeferredRecordings();
+        verify(mUmaRecorder, times(2)).recordClearDataDialogAction(false, true);
+        verify(mUmaRecorder).recordClearDataDialogAction(false, false);
+    }
+
+    @Test
+    public void doesntMakeDeferredRecordingTwice() {
+        mRecorder.handleDialogResult(false, true);
+        restartApp();
+        mRecorder.makeDeferredRecordings();
+        restartApp();
+
+        clearInvocations(mUmaRecorder);
+        mRecorder.makeDeferredRecordings();
+        verify(mUmaRecorder, never()).recordClearDataDialogAction(anyBoolean(), anyBoolean());
+    }
+
+    private void restartApp() {
+        when(mBrowserInitializer.hasNativeInitializationCompleted()).thenReturn(false);
+        doNothing().when(mBrowserInitializer).runNowOrAfterNativeInitialization(
+                mTaskOnNativeInitCaptor.capture());
+        mRecorder = new ClearDataDialogResultRecorder(() -> mPrefsManager, mBrowserInitializer,
+                mUmaRecorder);
+    }
+
+    private void finishNativeInit() {
+        for (Runnable task : mTaskOnNativeInitCaptor.getAllValues()) {
+            task.run();
+        }
+        when(mBrowserInitializer.hasNativeInitializationCompleted()).thenReturn(true);
+        doAnswer(answerVoid(Runnable::run)).when(mBrowserInitializer)
+                .runNowOrAfterNativeInitialization(any());
+    }
+
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index 2ca5b7c..22f7ec0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -29,6 +29,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.notifications.NotificationBuilderBase;
+import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 
 /**
  * Unit tests for {@link TrustedWebActivityClient}.
@@ -46,6 +47,10 @@
     private TrustedWebActivityServiceWrapper mService;
     @Mock
     private NotificationBuilderBase mNotificationBuilder;
+    @Mock
+    private TrustedWebActivityUmaRecorder mRecorder;
+    @Mock
+    private NotificationUmaTracker mNotificationUmaTracker;
 
     @Mock
     private Bitmap mServiceSmallIconBitmap;
@@ -67,7 +72,7 @@
         when(mService.getSmallIconBitmap()).thenReturn(mServiceSmallIconBitmap);
         when(mService.getComponentName()).thenReturn(new ComponentName(CLIENT_PACKAGE_NAME, ""));
 
-        mClient = new TrustedWebActivityClient(mConnection);
+        mClient = new TrustedWebActivityClient(mConnection, mRecorder, mNotificationUmaTracker);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java
index 62497ef..90a29b6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityDisclosureControllerTest.java
@@ -29,6 +29,7 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.browserservices.Origin;
+import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
 import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
@@ -46,6 +47,8 @@
     @Mock public ChromePreferenceManager mPreferences;
     @Mock public ActivityLifecycleDispatcher mLifecycleDispatcher;
     @Mock public TrustedWebActivityVerifier mVerifier;
+    @Mock public TrustedWebActivityUmaRecorder mRecorder;
+
     @Captor public ArgumentCaptor<Runnable> mVerificationObserverCaptor;
 
     public TrustedWebActivityModel mModel = new TrustedWebActivityModel();
@@ -61,7 +64,7 @@
         doReturn(false).when(mPreferences).hasUserAcceptedTwaDisclosureForPackage(anyString());
 
         mDisclosureController = new TrustedWebActivityDisclosureController(
-                mPreferences, mModel, mLifecycleDispatcher, mVerifier);
+                mPreferences, mModel, mLifecycleDispatcher, mVerifier, mRecorder);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java
new file mode 100644
index 0000000..9dd02fb
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityOpenTimeRecorderTest.java
@@ -0,0 +1,220 @@
+// 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.browserservices.trustedwebactivityui.controller;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VERIFICATION_FAILURE;
+import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VERIFICATION_PENDING;
+import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VERIFICATION_SUCCESS;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.browserservices.Origin;
+import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
+import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationState;
+import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationStatus;
+import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link TrustedWebActivityOpenTimeRecorder}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class TrustedWebActivityOpenTimeRecorderTest {
+
+    @Mock ActivityLifecycleDispatcher mLifecycleDispatcher;
+    @Mock TrustedWebActivityVerifier mVerifier;
+    @Mock TrustedWebActivityUmaRecorder mUmaRecorder;
+    @Mock ActivityTabProvider mTabProvider;
+    @Captor ArgumentCaptor<Runnable> mVerificationObserverCaptor;
+
+    private TrustedWebActivityOpenTimeRecorder mRecorder;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doNothing().when(mVerifier).addVerificationObserver(mVerificationObserverCaptor.capture());
+        mRecorder = new TrustedWebActivityOpenTimeRecorder(
+                mLifecycleDispatcher, mVerifier, mUmaRecorder, mTabProvider);
+    }
+
+    @Test
+    public void recordsTwaOpened() {
+        launchTwa();
+        verify(mUmaRecorder).recordTwaOpened(any());
+    }
+
+    @Test
+    public void doesntRecordTwaOpenedTwice() {
+        launchTwa();
+        leaveVerifiedOrigin();
+
+        clearInvocations(mUmaRecorder);
+        returnToVerifiedOrigin();
+        verify(mUmaRecorder, never()).recordTwaOpened(any());
+    }
+
+    @Test
+    public void recordsTwaOpenTime_OnFirstActivityPause() {
+        launchTwa();
+        advanceTime(3000);
+
+        mRecorder.onPauseWithNative();
+        verify(mUmaRecorder).recordTwaOpenTime(3000);
+    }
+
+    @Test
+    public void recordsTwaOpenTime_OnSecondActivityPause() {
+        launchTwa();
+        advanceTime(3000);
+        mRecorder.onPauseWithNative();
+        advanceTime(2000);
+        mRecorder.onResumeWithNative();
+        advanceTime(4000);
+
+        clearInvocations(mUmaRecorder);
+        mRecorder.onPauseWithNative();
+        verify(mUmaRecorder).recordTwaOpenTime(4000);
+    }
+
+    @Test
+    public void recordsTimeInVerified_WhenLeftVerified() {
+        launchTwa();
+        advanceTime(2000);
+
+        leaveVerifiedOrigin();
+        verify(mUmaRecorder).recordTimeInVerifiedOrigin(2000);
+    }
+
+    @Test
+    public void recordsTimeOutOfVerified_WhenReturnedToVerified() {
+        launchTwa();
+        advanceTime(2000);
+        leaveVerifiedOrigin();
+        advanceTime(3000);
+
+        returnToVerifiedOrigin();
+        verify(mUmaRecorder).recordTimeOutOfVerifiedOrigin(3000);
+    }
+
+    @Test
+    public void recordsTimeInVerified_WhenLeftVerifiedAgain() {
+        launchTwa();
+        advanceTime(2000);
+        leaveVerifiedOrigin();
+        advanceTime(3000);
+        returnToVerifiedOrigin();
+        advanceTime(4000);
+
+        clearInvocations(mUmaRecorder);
+        leaveVerifiedOrigin();
+        verify(mUmaRecorder).recordTimeInVerifiedOrigin(4000);
+    }
+
+    @Test
+    public void recordsTimeOutOfVerified_WhenReturnedToVerifiedAgain() {
+        launchTwa();
+        advanceTime(2000);
+        leaveVerifiedOrigin();
+        advanceTime(3000);
+        returnToVerifiedOrigin();
+        advanceTime(4000);
+        leaveVerifiedOrigin();
+        advanceTime(5000);
+
+        clearInvocations(mUmaRecorder);
+        returnToVerifiedOrigin();
+        verify(mUmaRecorder).recordTimeOutOfVerifiedOrigin(5000);
+    }
+
+    @Test
+    public void recordsTimeInVerified_WhenPausedWhileInVerified() {
+        launchTwa();
+        advanceTime(2000);
+
+        mRecorder.onPauseWithNative();
+        verify(mUmaRecorder).recordTimeInVerifiedOrigin(2000);
+    }
+
+    @Test
+    public void recordsTimeInVerified_AfterResumedInVerified_AndLeftVerified() {
+        launchTwa();
+        advanceTime(2000);
+        mRecorder.onPauseWithNative();
+        advanceTime(3000);
+        mRecorder.onResumeWithNative();
+        advanceTime(4000);
+
+        clearInvocations(mUmaRecorder);
+        leaveVerifiedOrigin();
+        verify(mUmaRecorder).recordTimeInVerifiedOrigin(4000);
+    }
+
+    @Test
+    public void recordsTimeOutOfVerified_WhenPausedWhileOutOfVerified() {
+        launchTwa();
+        advanceTime(2000);
+        leaveVerifiedOrigin();
+        advanceTime(3000);
+
+        mRecorder.onPauseWithNative();
+        verify(mUmaRecorder).recordTimeOutOfVerifiedOrigin(3000);
+    }
+
+    @Test
+    public void doesntRecordAnyTime_WhenVerifiedForFirstTime() {
+        launchTwa();
+        verify(mUmaRecorder, never()).recordTimeInVerifiedOrigin(anyLong());
+        verify(mUmaRecorder, never()).recordTimeOutOfVerifiedOrigin(anyLong());
+        verify(mUmaRecorder, never()).recordTwaOpenTime(anyLong());
+    }
+
+    private void launchTwa() {
+        advanceTime(1000);
+        mRecorder.onResumeWithNative();
+        setVerificationStatus(VERIFICATION_PENDING);
+        setVerificationStatus(VERIFICATION_SUCCESS);
+    }
+
+    private void leaveVerifiedOrigin() {
+        setVerificationStatus(VERIFICATION_FAILURE);
+    }
+
+    private void returnToVerifiedOrigin() {
+        setVerificationStatus(VERIFICATION_SUCCESS);
+    }
+
+    private void setVerificationStatus(@VerificationStatus int status) {
+        VerificationState newState = new VerificationState(mock(Origin.class), status);
+        when(mVerifier.getState()).thenReturn(newState);
+        for (Runnable observer : mVerificationObserverCaptor.getAllValues()) {
+            observer.run();
+        }
+    }
+
+    private void advanceTime(long millis) {
+        Robolectric.getForegroundThreadScheduler().advanceBy(millis, TimeUnit.MILLISECONDS);
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/StreamLifecycleManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/StreamLifecycleManagerTest.java
index a3bfe742..39becd4ad 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/StreamLifecycleManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/StreamLifecycleManagerTest.java
@@ -6,7 +6,10 @@
 
 import static org.mockito.AdditionalMatchers.or;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -20,6 +23,7 @@
 
 import com.google.android.libraries.feed.api.stream.Stream;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,6 +36,8 @@
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
 
@@ -47,17 +53,30 @@
     private Tab mTab;
     @Mock
     private Stream mStream;
+    @Mock
+    private PrefServiceBridge mPrefServiceBridge;
 
     private StreamLifecycleManager mStreamLifecycleManager;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+
+        // Initialize a test instance for PrefServiceBridge.
+        when(mPrefServiceBridge.getBoolean(anyInt())).thenReturn(true);
+        doNothing().when(mPrefServiceBridge).setBoolean(anyInt(), anyBoolean());
+        PrefServiceBridge.setInstanceForTesting(mPrefServiceBridge);
+
         ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.CREATED);
         mStreamLifecycleManager = new StreamLifecycleManager(mStream, mActivity, mTab);
         verify(mStream, times(1)).onCreate(or(any(String.class), isNull()));
     }
 
+    @After
+    public void tearDown() {
+        PrefServiceBridge.setInstanceForTesting(null);
+    }
+
     @Test
     @SmallTest
     public void testShow() {
@@ -84,6 +103,33 @@
 
     @Test
     @SmallTest
+    public void testShow_ArticlesNotVisible() {
+        // Verify that onShow is not called when articles are set hidden by the user.
+        when(mPrefServiceBridge.getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE)).thenReturn(false);
+        ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
+        when(mTab.isHidden()).thenReturn(false);
+        when(mTab.isUserInteractable()).thenReturn(true);
+        mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab, FROM_NEW);
+        verify(mStream, times(0)).onShow();
+
+        // Verify that onShow is called when articles are set shown by the user.
+        when(mPrefServiceBridge.getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE)).thenReturn(true);
+        mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab, FROM_NEW);
+        verify(mStream, times(1)).onShow();
+
+        // Verify that onHide is called after tab is hidden.
+        mStreamLifecycleManager.getTabObserverForTesting().onHidden(mTab, CHANGED_TABS);
+        verify(mStream, times(1)).onHide();
+
+        // Verify that onShow is called when articles are set hidden by the user within the same
+        // session.
+        when(mPrefServiceBridge.getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE)).thenReturn(false);
+        mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab, FROM_NEW);
+        verify(mStream, times(2)).onShow();
+    }
+
+    @Test
+    @SmallTest
     public void testActivate() {
         // Verify that stream is not active before activity resumed.
         when(mTab.isHidden()).thenReturn(false);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a96bc15..1ee0b0b6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1675,7 +1675,7 @@
       </message>
       <message name="IDS_DOWNLOAD_STATUS_PERCENT_COMPLETE_ACCESSIBLE_ALERT"
                desc="The title of a download notification: the current download status is in progress and time remaining is known. This message is for screen reader users.">
-               Downloading, <ph name="PERCENT_REMAINING">$1<ex>33</ex>% remaining</ph>
+               Downloading, <ph name="PERCENT_REMAINING">$1<ex>33</ex></ph>% remaining
       </message>
       <message name="IDS_DOWNLOAD_STATUS_TIME_REMAINING_ACCESSIBLE_ALERT"
                desc="The title of a download notification: the current download status is in progress and time remaining is known. This message is for screen reader users.">
@@ -1687,11 +1687,11 @@
       </message>
       <message name="IDS_DOWNLOAD_CANCELLED_ACCESSIBLE_ALERT"
                desc="The title of a download notification: the current download status is cancelled. This message is for screen reader users.">
-               Download cancelled: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex>.</ph>
+               Download cancelled: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex></ph>
       </message>
       <message name="IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT"
                desc="The title of a download notification: the current download status is finished successufully. This message is for screen reader users.">
-               Download complete: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex>. Press Shift+F6 to cycle to the downloads bar area.</ph>
+               Download complete: <ph name="FILE_NAME">$1<ex>somedocument.pdf</ex>></ph>. Press Shift+F6 to cycle to the downloads bar area.
       </message>
 
       <!-- Download Notification Labels -->
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 5d6823e..5243974 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -102,6 +102,9 @@
 void UiControllerAndroid::Start(JNIEnv* env,
                                 const JavaParamRef<jobject>& jcaller,
                                 const JavaParamRef<jstring>& initialUrlString) {
+  if (!ui_delegate_)
+    return;
+
   GURL initialUrl =
       GURL(base::android::ConvertJavaStringToUTF8(env, initialUrlString));
   ui_delegate_->Start(initialUrl);
@@ -179,12 +182,18 @@
     const base::android::JavaParamRef<jobject>& obj,
     float distanceX,
     float distanceY) {
+  if (!ui_delegate_)
+    return;
+
   ui_delegate_->ScrollBy(distanceX, distanceY);
 }
 
 void UiControllerAndroid::UpdateTouchableArea(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
+  if (!ui_delegate_)
+    return;
+
   ui_delegate_->UpdateTouchableArea();
 }
 
@@ -192,6 +201,9 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
     const JavaParamRef<jstring>& jscript_path) {
+  if (!ui_delegate_)
+    return;
+
   std::string script_path;
   base::android::ConvertJavaStringToUTF8(env, jscript_path, &script_path);
   ui_delegate_->OnScriptSelected(script_path);
@@ -447,6 +459,9 @@
 }
 
 std::string UiControllerAndroid::GetDebugContext() const {
+  if (!ui_delegate_)
+    return "";
+
   return ui_delegate_->GetDebugContext();
 }
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 59b086e7..013303c 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -132,6 +132,8 @@
   base::android::ScopedJavaGlobalRef<jobject>
       java_autofill_assistant_ui_controller_;
 
+  // UI delegate. It can be nullptr during initialization and after the delegate
+  // has been deleted. Always check for nullptr before using it.
   UiDelegate* ui_delegate_;
   content::BrowserContext* browser_context_;
 
diff --git a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
index 054750f..a6356ae 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
@@ -55,7 +55,7 @@
 }
 
 void ContextualSearchRankerLoggerImpl::SetupRankerPredictor(
-    const content::WebContents& web_contents) {
+    content::WebContents& web_contents) {
   // Create one predictor for the current BrowserContext.
   if (browser_context_) {
     DCHECK(browser_context_ == web_contents.GetBrowserContext());
diff --git a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
index bd952d3..e7b144d 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
@@ -74,7 +74,7 @@
   void LogFeature(const std::string& feature_name, int value);
 
   // Sets up the Ranker Predictor for the given |web_contents|.
-  void SetupRankerPredictor(const content::WebContents& web_contents);
+  void SetupRankerPredictor(content::WebContents& web_contents);
 
   // Logs to UMA when an important feature or outcome is present in the example.
   void logImportantFeaturePresent(const std::string& feature,
diff --git a/chrome/browser/android/feed/feed_logging_bridge.cc b/chrome/browser/android/feed/feed_logging_bridge.cc
index 444d498..c686743 100644
--- a/chrome/browser/android/feed/feed_logging_bridge.cc
+++ b/chrome/browser/android/feed/feed_logging_bridge.cc
@@ -65,6 +65,12 @@
       j_position, GURL(ConvertJavaStringToUTF8(j_env, j_url)));
 }
 
+void FeedLoggingBridge::OnContentSwiped(
+    JNIEnv* j_env,
+    const base::android::JavaRef<jobject>& j_this) {
+  feed_logging_metrics_->OnSuggestionSwiped();
+}
+
 void FeedLoggingBridge::OnContentClicked(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
@@ -120,6 +126,14 @@
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this) {}
 
+void FeedLoggingBridge::OnSpinnerShown(
+    JNIEnv* j_env,
+    const base::android::JavaRef<jobject>& j_this,
+    const jlong j_shownTimeMs) {
+  feed_logging_metrics_->OnSpinnerShown(
+      base::TimeDelta::FromMilliseconds(j_shownTimeMs));
+}
+
 void FeedLoggingBridge::OnContentTargetVisited(
     JNIEnv* j_env,
     const base::android::JavaRef<jobject>& j_this,
@@ -135,4 +149,10 @@
   }
 }
 
+void FeedLoggingBridge::ReportScrolledAfterOpen(
+    JNIEnv* j_env,
+    const base::android::JavaRef<jobject>& j_this) {
+  feed_logging_metrics_->ReportScrolledAfterOpen();
+}
+
 }  // namespace feed
diff --git a/chrome/browser/android/feed/feed_logging_bridge.h b/chrome/browser/android/feed/feed_logging_bridge.h
index 6b10402..27795d0 100644
--- a/chrome/browser/android/feed/feed_logging_bridge.h
+++ b/chrome/browser/android/feed/feed_logging_bridge.h
@@ -35,6 +35,9 @@
                           const jint j_position,
                           const base::android::JavaRef<jstring>& j_url);
 
+  void OnContentSwiped(JNIEnv* j_env,
+                       const base::android::JavaRef<jobject>& j_this);
+
   void OnContentClicked(JNIEnv* j_env,
                         const base::android::JavaRef<jobject>& j_this,
                         const jint j_position,
@@ -71,12 +74,19 @@
   void OnOpenedWithNoContent(JNIEnv* j_env,
                              const base::android::JavaRef<jobject>& j_this);
 
+  void OnSpinnerShown(JNIEnv* j_env,
+                      const base::android::JavaRef<jobject>& j_this,
+                      const jlong j_shownTimeMs);
+
   void OnContentTargetVisited(JNIEnv* j_env,
                               const base::android::JavaRef<jobject>& j_this,
                               const jlong visit_time_ms,
                               const jboolean is_offline,
                               const jboolean return_to_ntp);
 
+  void ReportScrolledAfterOpen(JNIEnv* j_env,
+                               const base::android::JavaRef<jobject>& j_this);
+
  private:
   FeedLoggingMetrics* feed_logging_metrics_;
 
diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
index f0fd40d..749ea5a 100644
--- a/chrome/browser/android/shortcut_helper.cc
+++ b/chrome/browser/android/shortcut_helper.cc
@@ -79,6 +79,7 @@
 void AddWebappWithSkBitmap(const ShortcutInfo& info,
                            const std::string& webapp_id,
                            const SkBitmap& icon_bitmap,
+                           bool is_icon_maskable,
                            const base::Closure& splash_image_callback) {
   // Send the data to the Java side to create the shortcut.
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -113,8 +114,8 @@
 
   Java_ShortcutHelper_addWebapp(
       env, java_webapp_id, java_url, java_scope_url, java_user_title, java_name,
-      java_short_name, java_best_primary_icon_url, java_bitmap, info.display,
-      info.orientation, info.source,
+      java_short_name, java_best_primary_icon_url, java_bitmap,
+      is_icon_maskable, info.display, info.orientation, info.source,
       OptionalSkColorToJavaColor(info.theme_color),
       OptionalSkColorToJavaColor(info.background_color), java_splash_screen_url,
       callback_pointer);
@@ -123,7 +124,8 @@
 // Adds a shortcut which opens in a browser tab to the launcher.
 void AddShortcutWithSkBitmap(const ShortcutInfo& info,
                              const std::string& id,
-                             const SkBitmap& icon_bitmap) {
+                             const SkBitmap& icon_bitmap,
+                             bool is_icon_maskable) {
   JNIEnv* env = base::android::AttachCurrentThread();
   ScopedJavaLocalRef<jstring> java_id =
       base::android::ConvertUTF8ToJavaString(env, id);
@@ -136,7 +138,7 @@
     java_bitmap = gfx::ConvertToJavaBitmap(&icon_bitmap);
 
   Java_ShortcutHelper_addShortcut(env, java_id, java_url, java_user_title,
-                                  java_bitmap, info.source);
+                                  java_bitmap, is_icon_maskable, info.source);
 }
 
 }  // anonymous namespace
@@ -171,19 +173,20 @@
 void ShortcutHelper::AddToLauncherWithSkBitmap(
     content::WebContents* web_contents,
     const ShortcutInfo& info,
-    const SkBitmap& icon_bitmap) {
+    const SkBitmap& icon_bitmap,
+    bool is_icon_maskable) {
   std::string webapp_id = base::GenerateGUID();
   if (info.display == blink::kWebDisplayModeStandalone ||
       info.display == blink::kWebDisplayModeFullscreen ||
       info.display == blink::kWebDisplayModeMinimalUi) {
     AddWebappWithSkBitmap(
-        info, webapp_id, icon_bitmap,
+        info, webapp_id, icon_bitmap, is_icon_maskable,
         base::Bind(&ShortcutHelper::FetchSplashScreenImage, web_contents,
                    info.splash_image_url, info.ideal_splash_image_size_in_px,
                    info.minimum_splash_image_size_in_px, webapp_id));
     return;
   }
-  AddShortcutWithSkBitmap(info, webapp_id, icon_bitmap);
+  AddShortcutWithSkBitmap(info, webapp_id, icon_bitmap, is_icon_maskable);
 }
 
 void ShortcutHelper::ShowWebApkInstallInProgressToast() {
@@ -255,6 +258,7 @@
 // static
 SkBitmap ShortcutHelper::FinalizeLauncherIconInBackground(
     const SkBitmap& bitmap,
+    bool is_icon_maskable,
     const GURL& url,
     bool* is_generated) {
   base::AssertLongCPUWorkAllowed();
@@ -268,8 +272,8 @@
                                                          bitmap.height())) {
       ScopedJavaLocalRef<jobject> java_bitmap =
           gfx::ConvertToJavaBitmap(&bitmap);
-      result =
-          Java_ShortcutHelper_createHomeScreenIconFromWebIcon(env, java_bitmap);
+      result = Java_ShortcutHelper_createHomeScreenIconFromWebIcon(
+          env, java_bitmap, is_icon_maskable);
     }
   }
 
diff --git a/chrome/browser/android/shortcut_helper.h b/chrome/browser/android/shortcut_helper.h
index 857dca23..cbf6626 100644
--- a/chrome/browser/android/shortcut_helper.h
+++ b/chrome/browser/android/shortcut_helper.h
@@ -42,7 +42,8 @@
   // added depends on the properties in |info|.
   static void AddToLauncherWithSkBitmap(content::WebContents* web_contents,
                                         const ShortcutInfo& info,
-                                        const SkBitmap& icon_bitmap);
+                                        const SkBitmap& icon_bitmap,
+                                        bool is_icon_maskable);
 
   // Shows toast notifying user that a WebAPK install is already in progress
   // when user tries to queue a new install for the same WebAPK.
@@ -85,6 +86,7 @@
   // |is_generated| will be set to |true|.
   // Must be called on a background worker thread.
   static SkBitmap FinalizeLauncherIconInBackground(const SkBitmap& icon,
+                                                   bool is_icon_maskable,
                                                    const GURL& url,
                                                    bool* is_generated);
 
diff --git a/chrome/browser/android/webapk/webapk_install_service.cc b/chrome/browser/android/webapk/webapk_install_service.cc
index ad45aaa..dff0770 100644
--- a/chrome/browser/android/webapk/webapk_install_service.cc
+++ b/chrome/browser/android/webapk/webapk_install_service.cc
@@ -95,8 +95,10 @@
     if (!web_contents)
       return;
 
+    // TODO(https://crbug.com/861643): Support maskable icons here.
     ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, shortcut_info,
-                                              primary_icon);
+                                              primary_icon,
+                                              /*is_icon_maskable=*/false);
   }
 }
 
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
index f1d6bdf..7b76ee68 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc
@@ -40,16 +40,14 @@
 // Looks up the original, online, visible URL of |web_contents|. The current
 // visible URL may be a distilled article which is not appropriate for a home
 // screen shortcut.
-GURL GetShortcutUrl(const content::WebContents* web_contents) {
+GURL GetShortcutUrl(content::WebContents* web_contents) {
   return dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
       web_contents->GetVisibleURL());
 }
 
 bool DoesAndroidSupportMaskableIcons() {
-  // TODO(peconn): Enable once Chrome on Android correctly handles maskable
-  // icons (https://crbug.com/904354).
   return base::android::BuildInfo::GetInstance()->sdk_int() >=
-         base::android::SDK_VERSION_OREO && false;
+         base::android::SDK_VERSION_OREO;
 }
 
 InstallableParams ParamsToPerformManifestAndIconFetch() {
@@ -77,10 +75,11 @@
 // - the generated icon
 // - whether |icon| was used in generating the launcher icon
 std::pair<SkBitmap, bool> CreateLauncherIconInBackground(const GURL& start_url,
-                                                         const SkBitmap& icon) {
+                                                         const SkBitmap& icon,
+                                                         bool maskable) {
   bool is_generated = false;
   SkBitmap primary_icon = ShortcutHelper::FinalizeLauncherIconInBackground(
-      icon, start_url, &is_generated);
+      icon, maskable, start_url, &is_generated);
   return std::make_pair(primary_icon, is_generated);
 }
 
@@ -99,7 +98,8 @@
     gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
                           bitmap_result.bitmap_data->size(), &decoded);
   }
-  return CreateLauncherIconInBackground(start_url, decoded);
+  return CreateLauncherIconInBackground(start_url, decoded,
+                                        /* maskable */ false);
 }
 
 void RecordAddToHomescreenDialogDuration(base::TimeDelta duration) {
@@ -238,6 +238,7 @@
   }
 
   raw_primary_icon_ = *data.primary_icon;
+  has_maskable_primary_icon_ = data.has_maskable_primary_icon;
   shortcut_info_.best_primary_icon_url = data.primary_icon_url;
 
   // Save the splash screen URL for the later download.
@@ -346,7 +347,8 @@
       FROM_HERE,
       {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce(&CreateLauncherIconInBackground, shortcut_info_.url, icon),
+      base::BindOnce(&CreateLauncherIconInBackground, shortcut_info_.url, icon,
+                     has_maskable_primary_icon_),
       base::BindOnce(&AddToHomescreenDataFetcher::NotifyObserver,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h
index 1de8039..396e7b0 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h
+++ b/chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h
@@ -66,6 +66,7 @@
   const SkBitmap& badge_icon() const { return badge_icon_; }
   const SkBitmap& primary_icon() const { return primary_icon_; }
   ShortcutInfo& shortcut_info() { return shortcut_info_; }
+  bool has_maskable_primary_icon() const { return has_maskable_primary_icon_; }
 
  private:
   // Called to stop the timeout timer.
@@ -99,6 +100,7 @@
   SkBitmap badge_icon_;
   SkBitmap primary_icon_;
   ShortcutInfo shortcut_info_;
+  bool has_maskable_primary_icon_;
 
   base::CancelableTaskTracker favicon_task_tracker_;
   base::OneShotTimer data_timeout_timer_;
diff --git a/chrome/browser/android/webapps/add_to_homescreen_manager.cc b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
index 9dd1d86..615a5c6 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_manager.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
@@ -77,9 +77,10 @@
     base::string16 user_title =
         base::android::ConvertJavaStringToUTF16(env, j_user_title);
     data_fetcher_->shortcut_info().user_title = user_title;
-    ShortcutHelper::AddToLauncherWithSkBitmap(web_contents,
-                                              data_fetcher_->shortcut_info(),
-                                              data_fetcher_->primary_icon());
+    ShortcutHelper::AddToLauncherWithSkBitmap(
+        web_contents, data_fetcher_->shortcut_info(),
+        data_fetcher_->primary_icon(),
+        data_fetcher_->has_maskable_primary_icon());
   }
 
   // Fire the appinstalled event and do install time logging.
@@ -143,5 +144,6 @@
     java_bitmap = gfx::ConvertToJavaBitmap(&primary_icon);
 
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AddToHomescreenManager_onIconAvailable(env, java_ref_, java_bitmap);
+  Java_AddToHomescreenManager_onIconAvailable(
+      env, java_ref_, java_bitmap, data_fetcher_->has_maskable_primary_icon());
 }
diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h
index 69f0577..35984ff 100644
--- a/chrome/browser/app_controller_mac.h
+++ b/chrome/browser/app_controller_mac.h
@@ -123,6 +123,10 @@
 // window closure from causing the application to quit.
 - (void)stopTryingToTerminateApplication:(NSApplication*)app;
 
+// Run the quit confirmation panel and return whether or not to continue
+// quitting.
+- (BOOL)runConfirmQuitPanel;
+
 // Indicate that the system is powering off or logging out.
 - (void)willPowerOff:(NSNotification*)inNotification;
 
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 5648f890..79a30d3 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -463,12 +463,6 @@
   // them in, but I'm not sure about UX; we'd also want to disable other things
   // though.) http://crbug.com/40861
 
-  // Check if the user really wants to quit by employing the confirm-to-quit
-  // mechanism.
-  if (!browser_shutdown::IsTryingToQuit() &&
-      [self applicationShouldTerminate:app] != NSTerminateNow)
-    return NO;
-
   // Check for active apps. If quitting is prevented, only close browsers and
   // sessions.
   if (!browser_shutdown::IsTryingToQuit() && !isPoweringOff &&
@@ -519,30 +513,26 @@
   }
 }
 
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app {
+- (BOOL)runConfirmQuitPanel {
   // If there are no windows, quit immediately.
   if (BrowserList::GetInstance()->empty() &&
       !AppWindowRegistryUtil::IsAppWindowVisibleInAnyProfile(0)) {
-    return NSTerminateNow;
+    return YES;
   }
 
   // Check if the preference is turned on.
   const PrefService* prefs = g_browser_process->local_state();
   if (!prefs->GetBoolean(prefs::kConfirmToQuitEnabled)) {
     confirm_quit::RecordHistogram(confirm_quit::kNoConfirm);
-    return NSTerminateNow;
+    return YES;
   }
 
-  // If the application is going to terminate as the result of a Cmd+Q
-  // invocation, use the special sauce to prevent accidental quitting.
-  // http://dev.chromium.org/developers/design-documents/confirm-to-quit-experiment
-
-  // This logic is only for keyboard-initiated quits.
-  if ([[app currentEvent] type] != NSKeyDown)
+  // Run only for keyboard-initiated quits.
+  if ([[NSApp currentEvent] type] != NSKeyDown)
     return NSTerminateNow;
 
   return [[ConfirmQuitPanelController sharedController]
-      runModalLoopForApplication:app];
+      runModalLoopForApplication:NSApp];
 }
 
 // Called when the app is shutting down. Clean-up as appropriate.
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index a44b862a..39b0f1f8 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -262,7 +262,7 @@
   if (!active_browser)
     return base::Time();
 
-  const content::WebContents* active_tab =
+  content::WebContents* active_tab =
       active_browser->tab_strip_model()->GetActiveWebContents();
   if (!active_tab)
     return base::Time();
diff --git a/chrome/browser/banners/app_banner_ui_delegate_android.cc b/chrome/browser/banners/app_banner_ui_delegate_android.cc
index 9819c4c..9682c8d 100644
--- a/chrome/browser/banners/app_banner_ui_delegate_android.cc
+++ b/chrome/browser/banners/app_banner_ui_delegate_android.cc
@@ -318,8 +318,10 @@
   AppBannerSettingsHelper::RecordBannerInstallEvent(
       web_contents, shortcut_info_->url.spec(), AppBannerSettingsHelper::WEB);
 
+  // TODO(https://crbug.com/861643): Support maskable icons here.
   ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, *shortcut_info_,
-                                            primary_icon_);
+                                            primary_icon_,
+                                            /*is_icon_maskable=*/false);
 }
 
 void AppBannerUiDelegateAndroid::SendBannerAccepted() {
diff --git a/chrome/browser/browser_process_platform_part_android.cc b/chrome/browser/browser_process_platform_part_android.cc
index b83e7aa0..3680a08 100644
--- a/chrome/browser/browser_process_platform_part_android.cc
+++ b/chrome/browser/browser_process_platform_part_android.cc
@@ -15,7 +15,7 @@
 BrowserProcessPlatformPart::~BrowserProcessPlatformPart() {
 }
 
-void BrowserProcessPlatformPart::AttemptExit() {
+void BrowserProcessPlatformPart::AttemptExit(bool try_to_quit_application) {
   // Tell the Java code to finish() the Activity.
   chrome::TerminateAndroid();
 }
diff --git a/chrome/browser/browser_process_platform_part_android.h b/chrome/browser/browser_process_platform_part_android.h
index 0c2343c..5008e95e 100644
--- a/chrome/browser/browser_process_platform_part_android.h
+++ b/chrome/browser/browser_process_platform_part_android.h
@@ -15,7 +15,7 @@
   ~BrowserProcessPlatformPart() override;
 
   // Overridden from BrowserProcessPlatformPartBase:
-  void AttemptExit() override;
+  void AttemptExit(bool try_to_quit_application) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(BrowserProcessPlatformPart);
diff --git a/chrome/browser/browser_process_platform_part_base.cc b/chrome/browser/browser_process_platform_part_base.cc
index d4a391b..a75a728 100644
--- a/chrome/browser/browser_process_platform_part_base.cc
+++ b/chrome/browser/browser_process_platform_part_base.cc
@@ -23,7 +23,7 @@
 void BrowserProcessPlatformPartBase::StartTearDown() {
 }
 
-void BrowserProcessPlatformPartBase::AttemptExit() {
+void BrowserProcessPlatformPartBase::AttemptExit(bool try_to_quit_application) {
 // chrome::CloseAllBrowsers() doesn't link on OS_ANDROID, but it overrides this
 // method already.
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/browser_process_platform_part_base.h b/chrome/browser/browser_process_platform_part_base.h
index 552de4c..9204bb5 100644
--- a/chrome/browser/browser_process_platform_part_base.h
+++ b/chrome/browser/browser_process_platform_part_base.h
@@ -34,7 +34,7 @@
   virtual void StartTearDown();
 
   // Called from AttemptExitInternal().
-  virtual void AttemptExit();
+  virtual void AttemptExit(bool try_to_quit_application);
 
   // Called at the end of BrowserProcessImpl::PreMainMessageLoopRun().
   virtual void PreMainMessageLoopRun();
diff --git a/chrome/browser/browser_process_platform_part_mac.h b/chrome/browser/browser_process_platform_part_mac.h
index a273a0f..6d326407 100644
--- a/chrome/browser/browser_process_platform_part_mac.h
+++ b/chrome/browser/browser_process_platform_part_mac.h
@@ -19,7 +19,7 @@
 
   // Overridden from BrowserProcessPlatformPartBase:
   void StartTearDown() override;
-  void AttemptExit() override;
+  void AttemptExit(bool try_to_quit_application) override;
   void PreMainMessageLoopRun() override;
 
   AppShimHostManager* app_shim_host_manager();
diff --git a/chrome/browser/browser_process_platform_part_mac.mm b/chrome/browser/browser_process_platform_part_mac.mm
index 7c40a0a..48f09af9 100644
--- a/chrome/browser/browser_process_platform_part_mac.mm
+++ b/chrome/browser/browser_process_platform_part_mac.mm
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/browser_process_platform_part_mac.h"
 
+#include "base/mac/foundation_util.h"
+#import "chrome/browser/app_controller_mac.h"
 #include "chrome/browser/chrome_browser_application_mac.h"
 
 BrowserProcessPlatformPart::BrowserProcessPlatformPart() {
@@ -16,10 +18,26 @@
   app_shim_host_manager_ = NULL;
 }
 
-void BrowserProcessPlatformPart::AttemptExit() {
+void BrowserProcessPlatformPart::AttemptExit(bool try_to_quit_application) {
   // On the Mac, the application continues to run once all windows are closed.
   // Terminate will result in a CloseAllBrowsers() call, and once (and if)
   // that is done, will cause the application to exit cleanly.
+  //
+  // This function is called for two types of attempted exits: URL requests
+  // (chrome://quit or chrome://restart), and a keyboard menu invocations of
+  // command-Q. (Interestingly, selecting the Quit command with the mouse don't
+  // come down this code path at all.) URL requests to exit have
+  // |try_to_quit_application| set to true; keyboard menu invocations have it
+  // set to false.
+
+  if (!try_to_quit_application) {
+    // A keyboard menu invocation.
+    AppController* app_controller =
+        base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
+    if (![app_controller runConfirmQuitPanel])
+      return;
+  }
+
   chrome_browser_application_mac::Terminate();
 }
 
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index c68bed7..31dcf1c6 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -63,6 +63,7 @@
 #include "content/public/test/simple_url_loader_test_helper.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "media/base/media_switches.h"
 #include "media/mojo/services/video_decode_perf_history.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/dns/mock_host_resolver.h"
@@ -79,6 +80,15 @@
 #include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#if defined(OS_MACOSX)
+#include "base/threading/platform_thread.h"
+#endif
+#include "base/memory/scoped_refptr.h"
+#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
+#include "chrome/browser/media/library_cdm_test_helper.h"
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
 using content::BrowserThread;
 using content::BrowsingDataFilterBuilder;
 
@@ -280,14 +290,7 @@
 
 class BrowsingDataRemoverBrowserTest : public InProcessBrowserTest {
  public:
-  BrowsingDataRemoverBrowserTest() {
-    feature_list_.InitWithFeatures(
-        {leveldb::kLevelDBRewriteFeature,
-         // Ensure that kOnionSoupDOMStorage is enabled because the old
-         // SessionStorage implementation causes flaky tests.
-         blink::features::kOnionSoupDOMStorage},
-        {});
-  }
+  BrowsingDataRemoverBrowserTest() {}
 
   // Call to use an Incognito browser rather than the default.
   void UseIncognitoBrowser() {
@@ -351,16 +354,22 @@
   }
 
   void RemoveAndWait(int remove_mask) {
-    RemoveAndWait(remove_mask, base::Time());
+    RemoveAndWait(remove_mask, base::Time(), base::Time::Max());
   }
 
   void RemoveAndWait(int remove_mask, base::Time delete_begin) {
+    RemoveAndWait(remove_mask, delete_begin, base::Time::Max());
+  }
+
+  void RemoveAndWait(int remove_mask,
+                     base::Time delete_begin,
+                     base::Time delete_end) {
     content::BrowsingDataRemover* remover =
         content::BrowserContext::GetBrowsingDataRemover(
             GetBrowser()->profile());
     content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
     remover->RemoveAndReply(
-        delete_begin, base::Time::Max(), remove_mask,
+        delete_begin, delete_end, remove_mask,
         content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
         &completion_observer);
     completion_observer.BlockUntilCompletion();
@@ -446,6 +455,30 @@
     return count;
   }
 
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+  int GetMediaLicenseCount() {
+    base::RunLoop run_loop;
+    int count = -1;
+    content::StoragePartition* partition =
+        content::BrowserContext::GetDefaultStoragePartition(
+            browser()->profile());
+    scoped_refptr<BrowsingDataMediaLicenseHelper> media_license_helper =
+        BrowsingDataMediaLicenseHelper::Create(
+            partition->GetFileSystemContext());
+    media_license_helper->StartFetching(base::BindLambdaForTesting(
+        [&](const std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>&
+                licenses) {
+          count = licenses.size();
+          LOG(INFO) << "Found " << count << " licenses.";
+          for (const auto& license : licenses)
+            LOG(INFO) << license.last_modified_time;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    return count;
+  }
+#endif
+
   inline void ExpectCookieTreeModelCount(int expected) {
     std::unique_ptr<CookiesTreeModel> model = GetCookiesTreeModel();
     EXPECT_EQ(expected, GetCookiesTreeModelCount(model->GetRoot()))
@@ -520,6 +553,25 @@
     return model;
   }
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    InProcessBrowserTest::SetUpCommandLine(command_line);
+
+    std::vector<base::Feature> enabled_features = {
+        leveldb::kLevelDBRewriteFeature,
+        // Ensure that kOnionSoupDOMStorage is enabled because the old
+        // SessionStorage implementation causes flaky tests.
+        blink::features::kOnionSoupDOMStorage};
+
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+    // Testing MediaLicenses requires additional command line parameters as
+    // it uses the External Clear Key CDM.
+    RegisterClearKeyCdm(command_line);
+    enabled_features.push_back(media::kExternalClearKeyForTesting);
+#endif
+
+    feature_list_.InitWithFeatures(enabled_features, {});
+  }
+
   base::test::ScopedFeatureList feature_list_;
   Browser* incognito_browser_ = nullptr;
 };
@@ -1059,6 +1111,130 @@
   TestEmptySiteData("IndexedDb", GetParam());
 }
 
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+// Test Media Licenses by creating one and checking it is counted by the
+// cookie counter. Then delete it and check that the cookie counter is back
+// to zero.
+IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, MediaLicenseDeletion) {
+  const std::string kMediaLicenseType = "MediaLicense";
+  const base::Time delete_begin = GetParam();
+
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(0, GetMediaLicenseCount());
+  GURL url =
+      embedded_test_server()->GetURL("/browsing_data/media_license.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(0, GetMediaLicenseCount());
+  ExpectCookieTreeModelCount(0);
+  EXPECT_FALSE(HasDataForType(kMediaLicenseType));
+
+  SetDataForType(kMediaLicenseType);
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(1, GetMediaLicenseCount());
+  ExpectCookieTreeModelCount(1);
+  EXPECT_TRUE(HasDataForType(kMediaLicenseType));
+
+  // Try to remove the Media Licenses using a time frame up until an hour ago,
+  // which should not remove the recently created Media License.
+  RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
+                delete_begin, kLastHour);
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(1, GetMediaLicenseCount());
+  ExpectCookieTreeModelCount(1);
+  EXPECT_TRUE(HasDataForType(kMediaLicenseType));
+
+  // Now try with a time range that includes the current time, which should
+  // clear the Media License created for this test.
+  RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
+                delete_begin, base::Time::Max());
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(0, GetMediaLicenseCount());
+  ExpectCookieTreeModelCount(0);
+  EXPECT_FALSE(HasDataForType(kMediaLicenseType));
+}
+
+// Create and save a media license (which will be deleted in the following
+// test).
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
+                       PRE_MediaLicenseTimedDeletion) {
+  const std::string kMediaLicenseType = "MediaLicense";
+
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(0, GetMediaLicenseCount());
+
+  GURL url =
+      embedded_test_server()->GetURL("/browsing_data/media_license.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(0, GetMediaLicenseCount());
+  ExpectCookieTreeModelCount(0);
+  EXPECT_FALSE(HasDataForType(kMediaLicenseType));
+
+  SetDataForType(kMediaLicenseType);
+  EXPECT_EQ(0, GetSiteDataCount());
+  EXPECT_EQ(1, GetMediaLicenseCount());
+  ExpectCookieTreeModelCount(1);
+  EXPECT_TRUE(HasDataForType(kMediaLicenseType));
+}
+
+// Create and save a second media license, and then verify that timed deletion
+// selects the correct license to delete.
+IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
+                       MediaLicenseTimedDeletion) {
+  const std::string kMediaLicenseType = "MediaLicense";
+
+  // As the PRE_ test should run first, there should be one media license
+  // still stored. The time of it's creation should be sometime before
+  // this test starts. We can't see the license, since it's stored for a
+  // different origin (but we can delete it).
+  const base::Time start = base::Time::Now();
+  LOG(INFO) << "MediaLicenseTimedDeletion starting @ " << start;
+  EXPECT_EQ(1, GetMediaLicenseCount());
+
+  GURL url =
+      embedded_test_server()->GetURL("/browsing_data/media_license.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+#if defined(OS_MACOSX)
+  // On some Macs the file system uses second granularity. So before
+  // creating the second license, delay for 1 second so that the new
+  // license's time is not the same second as |start|.
+  base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+#endif
+
+  // This test should use a different domain than the PRE_ test, so there
+  // should be no existing media license for it.
+  // Note that checking HasDataForType() may result in an empty file being
+  // created. Deleting licenses checks for any file within the time range
+  // specified in order to delete all the files for the domain, so this may
+  // cause problems (especially with Macs that use second granularity).
+  // http://crbug.com/909829.
+  EXPECT_FALSE(HasDataForType(kMediaLicenseType));
+
+  // Create a media license for this domain.
+  SetDataForType(kMediaLicenseType);
+  EXPECT_EQ(2, GetMediaLicenseCount());
+  EXPECT_TRUE(HasDataForType(kMediaLicenseType));
+
+  // As Clear Browsing Data typically deletes recent data (e.g. last hour,
+  // last day, etc.), try to remove the Media Licenses created since the
+  // the start of this test, which should only delete the just created
+  // media license, and leave the one created by the PRE_ test.
+  RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES, start);
+  EXPECT_EQ(1, GetMediaLicenseCount());
+  EXPECT_FALSE(HasDataForType(kMediaLicenseType));
+
+  // Now try with a time range that includes all time, which should
+  // clear the media license created by the PRE_ test.
+  RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES);
+  EXPECT_EQ(0, GetMediaLicenseCount());
+  ExpectCookieTreeModelCount(0);
+}
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
 const std::vector<std::string> kStorageTypes{
     "Cookie",    "LocalStorage", "FileSystem",    "SessionStorage",
     "IndexedDb", "WebSql",       "ServiceWorker", "CacheStorage",
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 3d71dee..b8e666b7 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1876,7 +1876,7 @@
   // across versions.
   RecordBrowserStartupTime();
 
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   performance_monitor::PerformanceMonitor::GetInstance()->StartGatherCycle();
 
diff --git a/chrome/browser/chromeos/file_system_provider/request_manager.cc b/chrome/browser/chromeos/file_system_provider/request_manager.cc
index fec162f..7a71413 100644
--- a/chrome/browser/chromeos/file_system_provider/request_manager.cc
+++ b/chrome/browser/chromeos/file_system_provider/request_manager.cc
@@ -230,8 +230,7 @@
     const TabStripModel* const tabs = browser->tab_strip_model();
     DCHECK(tabs);
     for (int i = 0; i < tabs->count(); ++i) {
-      const content::WebContents* const web_contents =
-          tabs->GetWebContentsAt(i);
+      content::WebContents* const web_contents = tabs->GetWebContentsAt(i);
       const GURL& url = web_contents->GetURL();
       if (url.SchemeIs(extensions::kExtensionScheme) &&
           url.host_piece() == provider_id_) {
diff --git a/chrome/browser/chromeos/login/lock/screen_locker.cc b/chrome/browser/chromeos/login/lock/screen_locker.cc
index adf25df..3277838 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker.cc
@@ -514,7 +514,7 @@
 // static
 void ScreenLocker::Show() {
   base::RecordAction(UserMetricsAction("ScreenLocker_Show"));
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   // Check whether the currently logged in user is a guest account and if so,
   // refuse to lock the screen (crosbug.com/23764).
@@ -541,7 +541,7 @@
 
 // static
 void ScreenLocker::Hide() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   // For a guest user, screen_locker_ would have never been initialized.
   if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) {
     VLOG(1) << "Refusing to hide lock screen for guest account";
@@ -595,7 +595,7 @@
 
 ScreenLocker::~ScreenLocker() {
   VLOG(1) << "Destroying ScreenLocker " << this;
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   if (authenticator_)
     authenticator_->SetConsumer(nullptr);
diff --git a/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc b/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
index e06877a0..0587585 100644
--- a/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_ui_keyboard_browsertest.cc
@@ -56,7 +56,7 @@
   }
 
   void OnFocusPOD() {
-    ASSERT_TRUE(base::MessageLoopForUI::IsCurrent());
+    ASSERT_TRUE(base::MessageLoopCurrentForUI::IsSet());
     focused_ = true;
     if (runner_.get()) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index 47fb7e4..6fa3043 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -230,7 +230,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     OobeBaseTest::SetUpCommandLine(command_line);
 
-    // Disable sync sinc we don't really need this for these tests and it also
+    // Disable sync since we don't really need this for these tests and it also
     // makes OAuth2Test.MergeSession test flaky http://crbug.com/408867.
     command_line->AppendSwitch(switches::kDisableSync);
   }
@@ -494,7 +494,7 @@
 // PRE_MergeSession is testing merge session for a new profile.
 IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_PRE_PRE_MergeSession) {
   StartNewUserSession(/*wait_for_merge=*/true,
-                      /*is_under_advanced_protectionis_true=*/false);
+                      /*is_under_advanced_protection=*/false);
   // Check for existence of refresh token.
   std::string account_id = PickAccountId(profile(), kTestGaiaId, kTestEmail);
   identity::IdentityManager* identity_manager =
@@ -519,7 +519,7 @@
   LoginAsExistingUser();
   scoped_refptr<CookieReader> cookie_reader(new CookieReader());
   cookie_reader->ReadCookies(profile());
-  // These are still cookie values form the initial session since
+  // These are still cookie values from the initial session since
   // /ListAccounts
   EXPECT_EQ(cookie_reader->GetCookieValue("SID"), kTestSessionSIDCookie);
   EXPECT_EQ(cookie_reader->GetCookieValue("LSID"), kTestSessionLSIDCookie);
@@ -574,7 +574,7 @@
 // Sets up a new user with stored refresh token.
 IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_OverlappingContinueSessionRestore) {
   StartNewUserSession(/*wait_for_merge=*/true,
-                      /*is_under_advanced_protectionis_true=*/false);
+                      /*is_under_advanced_protection=*/false);
 }
 
 // Tests that ContinueSessionRestore could be called multiple times.
@@ -660,7 +660,7 @@
 
 IN_PROC_BROWSER_TEST_F(OAuth2Test, VerifyInAdvancedProtectionAfterOnlineAuth) {
   StartNewUserSession(/*wait_for_merge=*/true,
-                      /*is_under_advanced_protectionis_true=*/true);
+                      /*is_under_advanced_protection=*/true);
 
   // Verify that AccountInfo is properly updated.
   AccountTrackerService* account_tracker =
@@ -672,7 +672,7 @@
 IN_PROC_BROWSER_TEST_F(OAuth2Test,
                        VerifyNotInAdvancedProtectionAfterOnlineAuth) {
   StartNewUserSession(/*wait_for_merge=*/true,
-                      /*is_under_advanced_protectionis_true=*/false);
+                      /*is_under_advanced_protection=*/false);
 
   // Verify that AccountInfo is properly updated.
   AccountTrackerService* account_tracker =
@@ -684,7 +684,7 @@
 // Sets up a new user with stored refresh token.
 IN_PROC_BROWSER_TEST_F(OAuth2Test, PRE_SetInvalidTokenStatus) {
   StartNewUserSession(/*wait_for_merge=*/true,
-                      /*is_under_advanced_protectionis_true=*/false);
+                      /*is_under_advanced_protection=*/false);
 }
 
 // Tests that an auth error marks invalid auth token status despite
@@ -896,7 +896,7 @@
 
 IN_PROC_BROWSER_TEST_F(MergeSessionTest, PageThrottle) {
   StartNewUserSession(/*wait_for_merge=*/false,
-                      /*is_under_advanced_protectionis_true=*/false);
+                      /*is_under_advanced_protection=*/false);
 
   // Try to open a page from google.com.
   Browser* browser = FindOrCreateVisibleBrowser(profile());
@@ -943,7 +943,7 @@
 
 IN_PROC_BROWSER_TEST_F(MergeSessionTest, XHRThrottle) {
   StartNewUserSession(/*wait_for_merge=*/false,
-                      /*is_under_advanced_protectionis_true=*/false);
+                      /*is_under_advanced_protection=*/false);
 
   // Wait until we get send merge session request.
   WaitForMergeSessionToStart();
@@ -972,7 +972,7 @@
                                     fake_google_page_url_.spec().c_str(),
                                     non_google_page_url_.spec().c_str()));
 
-  // Verify that we've sent XHR request form the extension side...
+  // Verify that we've sent XHR request from the extension side...
   JsExpectOnBackgroundPage(ext->id(),
                            "googleRequestSent && !googleResponseReceived");
 
diff --git a/chrome/browser/chromeos/login/signin_partition_manager.cc b/chrome/browser/chromeos/login/signin_partition_manager.cc
index 5eeabba..83662764 100644
--- a/chrome/browser/chromeos/login/signin_partition_manager.cc
+++ b/chrome/browser/chromeos/login/signin_partition_manager.cc
@@ -98,7 +98,7 @@
 SigninPartitionManager::~SigninPartitionManager() {}
 
 void SigninPartitionManager::StartSigninSession(
-    const content::WebContents* embedder_web_contents,
+    content::WebContents* embedder_web_contents,
     StartSigninSessionDoneCallback signin_session_started) {
   // If we already were in a sign-in session, close it first.
   // This clears stale data from the last-used StorageParittion.
diff --git a/chrome/browser/chromeos/login/signin_partition_manager.h b/chrome/browser/chromeos/login/signin_partition_manager.h
index 1a5478e..6bfaa1f 100644
--- a/chrome/browser/chromeos/login/signin_partition_manager.h
+++ b/chrome/browser/chromeos/login/signin_partition_manager.h
@@ -52,7 +52,7 @@
   // |signin_session_started| will be invoked with the partition name of the
   // started signin session on completition.
   void StartSigninSession(
-      const content::WebContents* embedder_web_contents,
+      content::WebContents* embedder_web_contents,
       StartSigninSessionDoneCallback signin_session_started);
 
   // Closes the current StoragePartition. All cached data in the
diff --git a/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc b/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc
index b5b7daa9..83d58dd 100644
--- a/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc
+++ b/chrome/browser/chromeos/login/test/wizard_in_process_browser_test.cc
@@ -44,7 +44,7 @@
 }
 
 void WizardInProcessBrowserTest::TearDownOnMainThread() {
-  ASSERT_TRUE(base::MessageLoopForUI::IsCurrent());
+  ASSERT_TRUE(base::MessageLoopCurrentForUI::IsSet());
 
   if (!host_)
     return;
diff --git a/chrome/browser/download/download_permission_request.cc b/chrome/browser/download/download_permission_request.cc
index f4ebc6de..dda8f22 100644
--- a/chrome/browser/download/download_permission_request.cc
+++ b/chrome/browser/download/download_permission_request.cc
@@ -19,7 +19,7 @@
 DownloadPermissionRequest::DownloadPermissionRequest(
     base::WeakPtr<DownloadRequestLimiter::TabDownloadState> host)
     : host_(host) {
-  const content::WebContents* web_contents = host_->web_contents();
+  content::WebContents* web_contents = host_->web_contents();
   DCHECK(web_contents);
   request_origin_ = web_contents->GetURL().GetOrigin();
 }
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc
index 6f9993b0..1b545c0 100644
--- a/chrome/browser/extensions/activity_log/activity_log.cc
+++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -743,10 +743,9 @@
   return is_active_ && !ActivityLogAPI::IsExtensionWhitelisted(extension_id);
 }
 
-void ActivityLog::OnScriptsExecuted(
-    const content::WebContents* web_contents,
-    const ExecutingScriptsMap& extension_ids,
-    const GURL& on_url) {
+void ActivityLog::OnScriptsExecuted(content::WebContents* web_contents,
+                                    const ExecutingScriptsMap& extension_ids,
+                                    const GURL& on_url) {
   if (!is_active_)
     return;
   ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
diff --git a/chrome/browser/extensions/activity_log/activity_log.h b/chrome/browser/extensions/activity_log/activity_log.h
index b9b05ced..1012ed7 100644
--- a/chrome/browser/extensions/activity_log/activity_log.h
+++ b/chrome/browser/extensions/activity_log/activity_log.h
@@ -64,7 +64,7 @@
   static ActivityLog* GetInstance(content::BrowserContext* context);
 
   // Invoked when a ContentScript is executed.
-  void OnScriptsExecuted(const content::WebContents* web_contents,
+  void OnScriptsExecuted(content::WebContents* web_contents,
                          const ExecutingScriptsMap& extension_ids,
                          const GURL& on_url);
 
diff --git a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
index 656deed4..911813d 100644
--- a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
+++ b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
@@ -137,7 +137,7 @@
 
 bool CanRequestAutomation(const Extension* extension,
                           const AutomationInfo* automation_info,
-                          const content::WebContents* contents) {
+                          content::WebContents* contents) {
   if (automation_info->desktop)
     return true;
 
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 1bba7b5e..9bdef0a5 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -1351,8 +1351,9 @@
       ->FlushProxyConfigMonitorForTesting();
 
   // Verify that the extension can't intercept the network request.
-  ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(
-                                              "/pages_with_script/page.html"));
+  ui_test_utils::NavigateToURL(
+      browser(),
+      GURL("http://does.not.resolve.test/pages_with_script/page.html"));
   EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
   EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
 }
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 6e956fb..9c38e42 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -1256,8 +1256,8 @@
       ->FlushProxyConfigMonitorForTesting();
 
   // Navigate to a page. The URL doesn't matter.
-  ui_test_utils::NavigateToURL(browser(),
-                               embedded_test_server()->GetURL("/title2.html"));
+  ui_test_utils::NavigateToURL(
+      browser(), GURL("http://does.not.resolve.test/title2.html"));
 
   // The extension should not have seen the PAC request.
   EXPECT_EQ(0, GetCountFromBackgroundPage(extension, profile(),
diff --git a/chrome/browser/extensions/user_script_listener_browsertest.cc b/chrome/browser/extensions/user_script_listener_browsertest.cc
index 2d1df834..7fb616c 100644
--- a/chrome/browser/extensions/user_script_listener_browsertest.cc
+++ b/chrome/browser/extensions/user_script_listener_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "build/build_config.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/user_script_listener.h"
 #include "chrome/test/base/testing_profile.h"
@@ -42,17 +41,9 @@
 
 using UserScriptListenerTest = ExtensionBrowserTest;
 
-#if defined(OS_WIN)
-#define MAYBE_NavigationWaitsForContentScriptsToLoad \
-  DISABLED_NavigationWaitsForContentScriptsToLoad
-#else
-#define MAYBE_NavigationWaitsForContentScriptsToLoad \
-  NavigationWaitsForContentScriptsToLoad
-#endif
-
 // Test that navigations block while waiting for content scripts to load.
 IN_PROC_BROWSER_TEST_F(UserScriptListenerTest,
-                       MAYBE_NavigationWaitsForContentScriptsToLoad) {
+                       NavigationWaitsForContentScriptsToLoad) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   TestingProfile profile;
diff --git a/chrome/browser/interstitials/enterprise_util.cc b/chrome/browser/interstitials/enterprise_util.cc
index 0163485f..6597a0a5 100644
--- a/chrome/browser/interstitials/enterprise_util.cc
+++ b/chrome/browser/interstitials/enterprise_util.cc
@@ -29,7 +29,7 @@
       browser_context);
 }
 
-std::string GetUserName(const content::WebContents* web_contents) {
+std::string GetUserName(content::WebContents* web_contents) {
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   auto* identity_manager =
diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc
index d0accd53..cbf53fb 100644
--- a/chrome/browser/lifetime/application_lifetime.cc
+++ b/chrome/browser/lifetime/application_lifetime.cc
@@ -149,7 +149,7 @@
       content::NotificationService::AllSources(),
       content::NotificationService::NoDetails());
 
-  g_browser_process->platform_part()->AttemptExit();
+  g_browser_process->platform_part()->AttemptExit(try_to_quit_application);
 }
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/media/webrtc/webrtc_disable_encryption_flag_browsertest.cc b/chrome/browser/media/webrtc/webrtc_disable_encryption_flag_browsertest.cc
index c380d88..3d8d11c 100644
--- a/chrome/browser/media/webrtc/webrtc_disable_encryption_flag_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_disable_encryption_flag_browsertest.cc
@@ -49,6 +49,12 @@
 };
 
 // Makes a call and checks that there's encryption or not in the SDP offer.
+// TODO(crbub.com/910216): De-flake this for ChromeOs.
+#if defined(OS_CHROMEOS)
+#define MAYBE_TestInMenuDrag DISABLED_VerifyEncryption
+#else
+#define MAYBE_TestInMenuDrag VerifyEncryption
+#endif
 IN_PROC_BROWSER_TEST_F(WebRtcDisableEncryptionFlagBrowserTest,
                        VerifyEncryption) {
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_browsertest.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_browsertest.cc
index d6463f4..9b1a8a5 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_browsertest.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_browsertest.cc
@@ -22,6 +22,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
+#include "components/data_reduction_proxy/core/common/uma_util.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/network_service_instance.h"
diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc
index e627337..9411427 100644
--- a/chrome/browser/notifications/platform_notification_service_impl.cc
+++ b/chrome/browser/notifications/platform_notification_service_impl.cc
@@ -74,7 +74,7 @@
     if (browser->profile() != profile)
       continue;
 
-    const content::WebContents* active_contents =
+    content::WebContents* active_contents =
         browser->tab_strip_model()->GetActiveWebContents();
     if (!active_contents)
       continue;
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index bc9eb5b..31fae59f 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -685,20 +685,6 @@
   }
 }
 
-bool MetricsWebContentsObserver::ReportNavigationRestartPenalty(
-    content::NavigationHandle* navigation_handle,
-    const base::TimeDelta mainframe_navigation_restart_penalty) {
-  DCHECK(!navigation_handle->HasCommitted());
-  auto it = provisional_loads_.find(navigation_handle);
-  if (it == provisional_loads_.end())
-    return false;
-
-  it->second->metrics_update_dispatcher()
-      ->ReportMainFrameNavigationRestartPenalty(
-          mainframe_navigation_restart_penalty);
-  return true;
-}
-
 void MetricsWebContentsObserver::UpdateTiming(
     mojom::PageLoadTimingPtr timing,
     mojom::PageLoadMetadataPtr metadata,
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index 73596f0..a5a8004 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -154,18 +154,6 @@
       const std::vector<mojom::ResourceDataUpdatePtr>& resources,
       mojom::PageRenderDataPtr render_data);
 
-  // Used to report that a NavigationThrottle or other browser intervention
-  // canceled a navigation and replaced it with a new navigation.
-  // |mainframe_navigation_restart_penalty| will be applied across all relevant
-  // timing deltas within the page for the page load tracker corresponding to
-  // |navigation_handle|. Returns whether or not there was an applicable
-  // navigation to update. This must be called before commit. Note: This method
-  // should not be called from a WebContentsObserver's DidFinishNavigation or
-  // DidStartNavigation since this would cause a race condition.
-  bool ReportNavigationRestartPenalty(
-      content::NavigationHandle* navigation_handle,
-      const base::TimeDelta mainframe_navigation_restart_penalty);
-
   // Informs the observers of the currently committed load that the event
   // corresponding to |event_key| has occurred. This should not be called within
   // WebContentsObserver::DidFinishNavigation methods.
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index 334a545..1692289 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -459,35 +459,6 @@
   CheckNoErrorEvents();
 }
 
-TEST_F(MetricsWebContentsObserverTest, ReportNavigationRestartPenalty) {
-  mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::FromDoubleT(1);
-  timing.response_start = base::TimeDelta::FromMilliseconds(10);
-
-  std::unique_ptr<content::NavigationSimulator> navigation_simulator =
-      content::NavigationSimulator::CreateRendererInitiated(
-          GURL(kDefaultTestUrl), main_rfh());
-  navigation_simulator->Start();
-
-  const base::TimeDelta penalty = base::TimeDelta::FromMilliseconds(5);
-  observer()->ReportNavigationRestartPenalty(
-      navigation_simulator->GetNavigationHandle(), penalty);
-
-  navigation_simulator->Commit();
-  SimulateTimingUpdate(timing);
-
-  NavigateToUntrackedUrl();
-  CheckNoErrorEvents();
-
-  ASSERT_EQ(1, CountCompleteTimingReported());
-  mojom::PageLoadTimingPtr got_timing = complete_timings()[0].Clone();
-  // TODO(crbug.com/906718): Test all updated metrics.
-  EXPECT_EQ(got_timing->navigation_start, timing.navigation_start - penalty);
-  EXPECT_EQ(got_timing->response_start.value(),
-            timing.response_start.value() - penalty);
-}
-
 TEST_F(MetricsWebContentsObserverTest, SameDocumentNoTrigger) {
   mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
index 8a9d503..4e6f63f 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
@@ -377,7 +377,7 @@
   vanilla_script_response->WaitForRequest();
   vanilla_script_response->Send(kHttpResponseHeader);
   vanilla_script_response->Send(std::string(1024, ' '));
-  waiter->AddMinimumResourceBytesExpectation(4000);
+  waiter->AddMinimumNetworkBytesExpectation(4000);
   waiter->Wait();
 
   // Verify correct numbers of resources are recorded.
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
index c8ef974..8f443bd 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/page_load_metrics/observers/previews_ukm_observer.h"
 
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
@@ -109,6 +110,9 @@
 void PreviewsUKMObserver::RecordPreviewsTypes(
     const page_load_metrics::PageLoadExtraInfo& info) {
   // Record the page end reason in UMA.
+  UMA_HISTOGRAM_ENUMERATION(
+      "Previews.PageEndReason", info.page_end_reason,
+      page_load_metrics::PageEndReason::PAGE_END_REASON_COUNT);
   base::UmaHistogramExactLinear(
       base::StringPrintf(
           "Previews.PageEndReason.%s",
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
index 4a9631e..2d84087 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
@@ -873,6 +873,9 @@
     NavigateToUntrackedUrl();
 
     tester.ExpectUniqueSample(
+        "Previews.PageEndReason",
+        page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
+    tester.ExpectUniqueSample(
         "Previews.PageEndReason." + GetStringNameForType(type),
         page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
   }
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index a3a8728..0009c4f 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -1795,7 +1795,7 @@
       browser(), embedded_test_server()->GetURL(
                      "foo.com", "/cross_site_iframe_factory.html?foo"));
   waiter->Wait();
-  int64_t one_frame_page_size = waiter->current_resource_bytes();
+  int64_t one_frame_page_size = waiter->current_network_bytes();
 
   waiter = CreatePageLoadMetricsTestWaiter();
   waiter->AddPageExpectation(TimingField::kLoadEvent);
@@ -1805,10 +1805,47 @@
           "a.com", "/cross_site_iframe_factory.html?a(b,c,d(e,f,g))"));
   // Verify that 7 iframes are fetched, with some amount of tolerance since
   // favicon is fetched only once.
-  waiter->AddMinimumResourceBytesExpectation(7 * (one_frame_page_size - 100));
+  waiter->AddMinimumNetworkBytesExpectation(7 * (one_frame_page_size - 100));
   waiter->Wait();
 }
 
+IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
+                       ChunkedResponse_OverheadDoesNotCountForBodyBytes) {
+  const char kHttpResponseHeader[] =
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=utf-8\r\n"
+      "Transfer-Encoding: chunked\r\n"
+      "\r\n";
+  const int kChunkSize = 5;
+  const int kNumChunks = 5;
+  auto main_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/mock_page.html",
+          true /*relative_url_is_prefix*/);
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+
+  browser()->OpenURL(content::OpenURLParams(
+      embedded_test_server()->GetURL("/mock_page.html"), content::Referrer(),
+      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
+
+  main_response->WaitForRequest();
+  main_response->Send(kHttpResponseHeader);
+  for (int i = 0; i < kNumChunks; i++) {
+    main_response->Send(std::to_string(kChunkSize));
+    main_response->Send("\r\n");
+    main_response->Send(std::string(kChunkSize, '*'));
+    main_response->Send("\r\n");
+  }
+  main_response->Done();
+  waiter->AddMinimumCompleteResourcesExpectation(1);
+  waiter->Wait();
+
+  // Verify that overheads for each chunk are not reported as body bytes.
+  EXPECT_EQ(waiter->current_network_body_bytes(), kChunkSize * kNumChunks);
+}
+
 IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, ReceivedCompleteResources) {
   const char kHttpResponseHeader[] =
       "HTTP/1.1 200 OK\r\n"
@@ -1841,7 +1878,7 @@
   main_html_response->Send(std::string(1000, ' '));
   main_html_response->Done();
   waiter->AddMinimumCompleteResourcesExpectation(1);
-  waiter->AddMinimumResourceBytesExpectation(1000);
+  waiter->AddMinimumNetworkBytesExpectation(1000);
   waiter->Wait();
 
   script_response->WaitForRequest();
@@ -1853,7 +1890,7 @@
   script_response->Send(std::string(1000, ' '));
   // Data received but resource not complete
   waiter->AddMinimumCompleteResourcesExpectation(1);
-  waiter->AddMinimumResourceBytesExpectation(2000);
+  waiter->AddMinimumNetworkBytesExpectation(2000);
   waiter->Wait();
   script_response->Done();
   waiter->AddMinimumCompleteResourcesExpectation(2);
@@ -1865,7 +1902,7 @@
   iframe_response->Send(std::string(2000, ' '));
   iframe_response->Done();
   waiter->AddMinimumCompleteResourcesExpectation(3);
-  waiter->AddMinimumResourceBytesExpectation(4000);
+  waiter->AddMinimumNetworkBytesExpectation(4000);
   waiter->Wait();
 }
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
index 84367dd..8a6bfc8 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
@@ -43,9 +43,9 @@
   expected_minimum_complete_resources_ = expected_minimum_complete_resources;
 }
 
-void PageLoadMetricsTestWaiter::AddMinimumResourceBytesExpectation(
-    int expected_minimum_resource_bytes) {
-  expected_minimum_resource_bytes_ = expected_minimum_resource_bytes;
+void PageLoadMetricsTestWaiter::AddMinimumNetworkBytesExpectation(
+    int expected_minimum_network_bytes) {
+  expected_minimum_network_bytes_ = expected_minimum_network_bytes;
 }
 
 bool PageLoadMetricsTestWaiter::DidObserveInPage(TimingField field) const {
@@ -117,9 +117,12 @@
                               std::forward_as_tuple(resource->request_id),
                               std::forward_as_tuple(resource->Clone()));
     }
-    if (resource->is_complete)
+    if (resource->is_complete) {
       current_complete_resources_++;
-    current_resource_bytes_ += resource->delta_bytes;
+      if (!resource->was_fetched_via_cache)
+        current_network_body_bytes_ += resource->encoded_body_length;
+    }
+    current_network_bytes_ += resource->delta_bytes;
   }
   if (ExpectationsSatisfied() && run_loop_)
     run_loop_->Quit();
@@ -186,8 +189,8 @@
   return (expected_minimum_complete_resources_ == 0 ||
           current_complete_resources_ >=
               expected_minimum_complete_resources_) &&
-         (expected_minimum_resource_bytes_ == 0 ||
-          current_resource_bytes_ >= expected_minimum_resource_bytes_);
+         (expected_minimum_network_bytes_ == 0 ||
+          current_network_bytes_ >= expected_minimum_network_bytes_);
 }
 
 bool PageLoadMetricsTestWaiter::ExpectationsSatisfied() const {
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
index 88eca45c..d7046df 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
@@ -45,7 +45,7 @@
       int expected_minimum_complete_resources);
 
   // Add aggregate received resource bytes expectation.
-  void AddMinimumResourceBytesExpectation(int expected_minimum_resource_bytes);
+  void AddMinimumNetworkBytesExpectation(int expected_minimum_network_bytes);
 
   // Whether the given TimingField was observed in the page.
   bool DidObserveInPage(TimingField field) const;
@@ -54,7 +54,11 @@
   // expectation methods. All matching fields must be set to end this wait.
   void Wait();
 
-  int64_t current_resource_bytes() const { return current_resource_bytes_; }
+  int64_t current_network_bytes() const { return current_network_bytes_; }
+
+  int64_t current_network_body_bytes() const {
+    return current_network_body_bytes_;
+  }
 
  protected:
   virtual bool ExpectationsSatisfied() const;
@@ -163,9 +167,12 @@
   TimingFieldBitSet observed_page_fields_;
 
   int current_complete_resources_ = 0;
-  int64_t current_resource_bytes_ = 0;
+  int64_t current_network_bytes_ = 0;
+
+  // Network body bytes are only counted for complete resources.
+  int64_t current_network_body_bytes_ = 0;
   int expected_minimum_complete_resources_ = 0;
-  int expected_minimum_resource_bytes_ = 0;
+  int expected_minimum_network_bytes_ = 0;
 
   bool attach_on_tracker_creation_ = false;
   bool did_add_observer_ = false;
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
index 301c122..7968182 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
@@ -261,34 +261,15 @@
       : target_(target) {}
 
   // Merge timing values from |new_page_load_timing| into the target
-  // PageLoadTiming, applying the |navigation_start_offset| to applicable
-  // metrics with respect to |is_main_frame|.
-  void MergeAndApplyOffset(base::TimeDelta navigation_start_offset,
-                           const mojom::PageLoadTiming& new_page_load_timing,
-                           bool is_main_frame) {
-    // TODO(crbug.com/906718): This approach is hard to maintain and likely to
-    // get stale over time.
+  // PageLoadTiming;
+  void Merge(base::TimeDelta navigation_start_offset,
+             const mojom::PageLoadTiming& new_page_load_timing,
+             bool is_main_frame) {
     MergePaintTiming(navigation_start_offset,
                      *new_page_load_timing.paint_timing, is_main_frame);
     MergeInteractiveTiming(navigation_start_offset,
                            *new_page_load_timing.interactive_timing,
                            is_main_frame);
-    // Don't need to merge these main-frame only metrics: merely apply the
-    // offset.
-    if (is_main_frame) {
-      target_->navigation_start =
-          new_page_load_timing.navigation_start + navigation_start_offset;
-      MaybeUpdateTimeDelta(&target_->response_start, navigation_start_offset,
-                           new_page_load_timing.response_start);
-      MaybeUpdateTimeDelta(&target_->input_to_navigation_start,
-                           navigation_start_offset,
-                           new_page_load_timing.input_to_navigation_start);
-
-      ApplyDocumentTimingOffset(navigation_start_offset,
-                                *new_page_load_timing.document_timing);
-      ApplyParseTimingOffset(navigation_start_offset,
-                             *new_page_load_timing.parse_timing);
-    }
   }
 
   // Whether we merged a new value.
@@ -362,25 +343,10 @@
     MaybeUpdateTimeDelta(&target_paint_timing->first_contentful_paint,
                          navigation_start_offset,
                          new_paint_timing.first_contentful_paint);
-    // These are currently a no-op on main frames.
-    MaybeUpdateTimeDelta(&target_paint_timing->largest_image_paint,
-                         navigation_start_offset,
-                         new_paint_timing.largest_image_paint);
-    MaybeUpdateTimeDelta(&target_paint_timing->last_image_paint,
-                         navigation_start_offset,
-                         new_paint_timing.last_image_paint);
-    MaybeUpdateTimeDelta(&target_paint_timing->largest_text_paint,
-                         navigation_start_offset,
-                         new_paint_timing.largest_text_paint);
-    MaybeUpdateTimeDelta(&target_paint_timing->last_text_paint,
-                         navigation_start_offset,
-                         new_paint_timing.last_text_paint);
-
     if (is_main_frame) {
       // First meaningful paint is only tracked in the main frame.
-      MaybeUpdateTimeDelta(&target_paint_timing->first_meaningful_paint,
-                           navigation_start_offset,
-                           new_paint_timing.first_meaningful_paint);
+      target_paint_timing->first_meaningful_paint =
+          new_paint_timing.first_meaningful_paint;
     }
   }
 
@@ -425,32 +391,6 @@
     }
   }
 
-  void ApplyDocumentTimingOffset(
-      base::TimeDelta navigation_start_offset,
-      const mojom::DocumentTiming& new_document_timing) {
-    mojom::DocumentTiming* target_document_timing =
-        target_->document_timing.get();
-    MaybeUpdateTimeDelta(
-        &target_document_timing->dom_content_loaded_event_start,
-        navigation_start_offset,
-        new_document_timing.dom_content_loaded_event_start);
-    MaybeUpdateTimeDelta(&target_document_timing->load_event_start,
-                         navigation_start_offset,
-                         new_document_timing.load_event_start);
-    MaybeUpdateTimeDelta(&target_document_timing->first_layout,
-                         navigation_start_offset,
-                         new_document_timing.first_layout);
-  }
-
-  void ApplyParseTimingOffset(base::TimeDelta navigation_start_offset,
-                              const mojom::ParseTiming& new_parse_timing) {
-    mojom::ParseTiming* target_parse_timing = target_->parse_timing.get();
-    MaybeUpdateTimeDelta(&target_parse_timing->parse_start,
-                         navigation_start_offset, new_parse_timing.parse_start);
-    MaybeUpdateTimeDelta(&target_parse_timing->parse_stop,
-                         navigation_start_offset, new_parse_timing.parse_stop);
-  }
-
   // The target PageLoadTiming we are merging values into.
   mojom::PageLoadTiming* const target_;
 
@@ -522,11 +462,6 @@
   }
 }
 
-void PageLoadMetricsUpdateDispatcher::ReportMainFrameNavigationRestartPenalty(
-    base::TimeDelta penalty) {
-  total_main_frame_navigation_restart_penalty_ += penalty;
-}
-
 void PageLoadMetricsUpdateDispatcher::UpdateMetrics(
     content::RenderFrameHost* render_frame_host,
     mojom::PageLoadTimingPtr new_timing,
@@ -600,13 +535,9 @@
 
   client_->OnSubFrameTimingChanged(render_frame_host, *new_timing);
 
-  // |total_main_frame_navigation_restart_penalty_| should also be applied to
-  // subframes since this penalty is associated with the main frame.
-  base::TimeDelta navigation_start_offset =
-      it->second - total_main_frame_navigation_restart_penalty_;
+  base::TimeDelta navigation_start_offset = it->second;
   PageLoadTimingMerger merger(pending_merged_page_timing_.get());
-  merger.MergeAndApplyOffset(navigation_start_offset, *new_timing,
-                             false /* is_main_frame */);
+  merger.Merge(navigation_start_offset, *new_timing, false /* is_main_frame */);
 
   MaybeDispatchTimingUpdates(merger.should_buffer_timing_update_callback());
 }
@@ -654,33 +585,9 @@
   mojom::InteractiveTimingPtr last_interactive_timing =
       std::move(pending_merged_page_timing_->interactive_timing);
 
-  base::TimeDelta navigation_start_offset =
-      -total_main_frame_navigation_restart_penalty_;
   // Update the latest candidate to the corresponding buffers. We will dispatch
   // the last candidate at the page load end. Because we don't want to dispatch
   // the non-last candidate here, we clear it from |new_timing|.
-  // Since this also means that these timings won't get
-  // |navigation_start_offset| applied in the merger, apply it here.
-  if (new_timing->paint_timing->largest_image_paint.has_value()) {
-    new_timing->paint_timing->largest_image_paint =
-        new_timing->paint_timing->largest_image_paint.value() +
-        navigation_start_offset;
-  }
-  if (new_timing->paint_timing->last_image_paint.has_value()) {
-    new_timing->paint_timing->last_image_paint =
-        new_timing->paint_timing->last_image_paint.value() +
-        navigation_start_offset;
-  }
-  if (new_timing->paint_timing->largest_text_paint.has_value()) {
-    new_timing->paint_timing->largest_text_paint =
-        new_timing->paint_timing->largest_text_paint.value() +
-        navigation_start_offset;
-  }
-  if (new_timing->paint_timing->last_text_paint.has_value()) {
-    new_timing->paint_timing->last_text_paint =
-        new_timing->paint_timing->last_text_paint.value() +
-        navigation_start_offset;
-  }
   largest_image_paint_.swap(new_timing->paint_timing->largest_image_paint);
   new_timing->paint_timing->largest_image_paint.reset();
   last_image_paint_.swap(new_timing->paint_timing->last_image_paint);
@@ -699,8 +606,7 @@
       std::move(last_interactive_timing);
 
   PageLoadTimingMerger merger(pending_merged_page_timing_.get());
-  merger.MergeAndApplyOffset(navigation_start_offset, *new_timing,
-                             true /* is_main_frame */);
+  merger.Merge(base::TimeDelta(), *new_timing, true /* is_main_frame */);
   MaybeDispatchTimingUpdates(merger.should_buffer_timing_update_callback());
 }
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
index 3307afc4b..f343290 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
@@ -136,11 +136,6 @@
   void DidFinishSubFrameNavigation(
       content::NavigationHandle* navigation_handle);
 
-  // Increases |total_main_frame_navigation_restart_penalty_| by the given
-  // delta. This should be fully updated before commit. See the comment on
-  // |total_main_frame_navigation_restart_penalty_|.
-  void ReportMainFrameNavigationRestartPenalty(base::TimeDelta penalty);
-
   void ShutDown();
 
   const mojom::PageLoadTiming& timing() const {
@@ -204,13 +199,6 @@
   mojom::PageLoadMetadataPtr subframe_metadata_;
 
   mojom::PageRenderDataPtr main_frame_render_data_;
-  // This time delta is an otherwise unknown penalty where the final navigation
-  // to a page was not the original (i.e.: user started) navigation. This can be
-  // caused by client redirects or other browser-based interventions like
-  // previews. Applying this delta will reconcile this page load metric with the
-  // time when the original navigation started. This will be applied to all
-  // metrics just before they are sent to the client.
-  base::TimeDelta total_main_frame_navigation_restart_penalty_;
 
   // Navigation start offsets for the most recently committed document in each
   // frame.
diff --git a/chrome/browser/permissions/permission_uma_util.cc b/chrome/browser/permissions/permission_uma_util.cc
index e2c560d..e409651 100644
--- a/chrome/browser/permissions/permission_uma_util.cc
+++ b/chrome/browser/permissions/permission_uma_util.cc
@@ -101,7 +101,7 @@
 }
 
 void RecordEngagementMetric(const std::vector<PermissionRequest*>& requests,
-                            const content::WebContents* web_contents,
+                            content::WebContents* web_contents,
                             const std::string& action) {
   PermissionRequestType type = requests[0]->GetPermissionRequestType();
   if (requests.size() > 1)
@@ -239,7 +239,7 @@
 
 void PermissionUmaUtil::PermissionPromptResolved(
     const std::vector<PermissionRequest*>& requests,
-    const content::WebContents* web_contents,
+    content::WebContents* web_contents,
     PermissionAction permission_action) {
   std::string action_string;
 
diff --git a/chrome/browser/permissions/permission_uma_util.h b/chrome/browser/permissions/permission_uma_util.h
index afa054e2..59f2372b 100644
--- a/chrome/browser/permissions/permission_uma_util.h
+++ b/chrome/browser/permissions/permission_uma_util.h
@@ -87,7 +87,7 @@
 
   static void PermissionPromptResolved(
       const std::vector<PermissionRequest*>& requests,
-      const content::WebContents* web_contents,
+      content::WebContents* web_contents,
       PermissionAction permission_action);
 
   static void RecordWithBatteryBucket(const std::string& histogram);
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index 4346da0..bd8ad51a 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -636,10 +636,6 @@
         1);
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        false, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
   }
 
   {
@@ -649,10 +645,6 @@
     VerifyPreviewLoaded();
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
   }
 
   {
@@ -668,10 +660,6 @@
         1);
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        false, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
   }
 
   {
@@ -693,10 +681,6 @@
         PreviewsLitePageNavigationThrottle::IneligibleReason::kHttpPost, 1);
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        false, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
   }
 
   {
@@ -712,10 +696,6 @@
         1);
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        false, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
   }
 
   {
@@ -729,10 +709,6 @@
         1);
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        false, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
     VerifyErrorPageLoaded();
   }
 
@@ -749,10 +725,6 @@
         1);
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        false, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
   }
 
   {
@@ -772,10 +744,6 @@
         1);
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        false, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
 
     // Reset ECT for future tests.
     g_browser_process->network_quality_tracker()
@@ -885,10 +853,6 @@
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
         "Previews.ServerLitePage.HttpOnlyFallbackPenalty", 1);
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.ServerResponse",
@@ -908,10 +872,6 @@
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
         "Previews.ServerLitePage.HttpOnlyFallbackPenalty", 1);
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.ServerResponse",
@@ -941,10 +901,6 @@
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 2);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
         "Previews.ServerLitePage.HttpOnlyFallbackPenalty", 1);
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.ServerResponse",
@@ -960,10 +916,6 @@
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
     histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 2);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
-    histogram_tester.ExpectTotalCount(
         "Previews.ServerLitePage.HttpOnlyFallbackPenalty", 1);
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.ServerResponse",
@@ -1088,29 +1040,6 @@
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.ServerResponse",
         PreviewsLitePageNavigationThrottle::ServerResponse::kTimeout, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 2);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
-    // Since this test already has a delay baked in, make sure the reported
-    // penalty is at least the length of the delay.
-    int max_penalty = 0;
-    for (const base::Bucket& bucket : histogram_tester.GetAllSamples(
-             "Previews.ServerLitePage.ReportedNavigationRestartPenalty")) {
-      if (bucket.min > max_penalty) {
-        max_penalty = bucket.min;
-      }
-    }
-    // Expecting |max_penalty| > |kTimeoutMs| is flaky in release builds because
-    // of histogram bucketing. Since HistogramTester::Bucket doesn't provide a
-    // bucket max, if |max_penalty| < |kTimeoutMs|, check that a sample exists
-    // in the |kTimeoutMs| bucket.
-    if (max_penalty <= kTimeoutMs) {
-      EXPECT_GE(histogram_tester.GetBucketCount(
-                    "Previews.ServerLitePage.ReportedNavigationRestartPenalty",
-                    kTimeoutMs),
-                1);
-    }  // else, test passes
   }
 
   {
@@ -1154,10 +1083,6 @@
 
     histogram_tester.ExpectBucketCount("Previews.ServerLitePage.Triggered",
                                        true, 1);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", 2);
-    histogram_tester.ExpectTotalCount(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", 0);
     histogram_tester.ExpectBucketCount(
         "Previews.ServerLitePage.ServerResponse",
         PreviewsLitePageNavigationThrottle::ServerResponse::kFailed, 1);
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index 70ebe5f5..335f475c 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
 #include "chrome/browser/previews/previews_lite_page_decider.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
@@ -93,43 +92,6 @@
   return url_params;
 }
 
-// This is called on |WillStartRequest| to check whether the given |handle| is
-// for a navigation that was started by this preview. If it was, the penalty
-// (time between the original navigation start and the navigation start of
-// |handle|) is reported to PLM.
-void MaybeReportNavigationRestartPenalty(content::NavigationHandle* handle) {
-  PreviewsUITabHelper* ui_tab_helper =
-      PreviewsUITabHelper::FromWebContents(handle->GetWebContents());
-  if (!ui_tab_helper)
-    return;
-
-  previews::PreviewsUserData* previews_data =
-      ui_tab_helper->GetPreviewsUserData(handle);
-  if (!previews_data)
-    return;
-
-  previews::PreviewsUserData::ServerLitePageInfo* info =
-      previews_data->server_lite_page_info();
-  if (!info)
-    return;
-
-  DCHECK_GT(info->original_navigation_start, base::TimeTicks());
-  base::TimeDelta penalty =
-      handle->NavigationStart() - info->original_navigation_start;
-
-  bool updated = page_load_metrics::MetricsWebContentsObserver::FromWebContents(
-                     handle->GetWebContents())
-                     ->ReportNavigationRestartPenalty(handle, penalty);
-  DCHECK(updated);
-  if (updated) {
-    UMA_HISTOGRAM_MEDIUM_TIMES(
-        "Previews.ServerLitePage.ReportedNavigationRestartPenalty", penalty);
-  } else {
-    UMA_HISTOGRAM_MEDIUM_TIMES(
-        "Previews.ServerLitePage.NotReportedNavigationRestartPenalty", penalty);
-  }
-}
-
 // Gets the ServerLitePageInfo struct from an existing attempted lite page
 // navigation, if there is one. If not, returns nullptr.
 std::unique_ptr<previews::PreviewsUserData::ServerLitePageInfo>
@@ -581,7 +543,6 @@
     return content::NavigationThrottle::CANCEL;
   }
 
-  MaybeReportNavigationRestartPenalty(navigation_handle());
   return MaybeNavigateToPreview();
 }
 
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
index 7f44a891..4664e65 100644
--- a/chrome/browser/printing/print_job.cc
+++ b/chrome/browser/printing/print_job.cc
@@ -43,7 +43,7 @@
     : is_job_pending_(false),
       is_canceling_(false),
       task_runner_(base::ThreadTaskRunnerHandle::Get()) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 }
 
 PrintJob::~PrintJob() {
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.cc b/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.cc
index 3df598de..b44f906 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.cc
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.cc
@@ -23,7 +23,7 @@
 ContextMenuContentTypePlatformApp::~ContextMenuContentTypePlatformApp() {
 }
 
-const Extension* ContextMenuContentTypePlatformApp::GetExtension() const {
+const Extension* ContextMenuContentTypePlatformApp::GetExtension() {
   ProcessManager* process_manager =
       ProcessManager::Get(source_web_contents()->GetBrowserContext());
   return process_manager->GetExtensionForWebContents(
diff --git a/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.h b/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.h
index 1dccf81f..407a930 100644
--- a/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.h
+++ b/chrome/browser/renderer_context_menu/context_menu_content_type_platform_app.h
@@ -26,7 +26,7 @@
  private:
   friend class ContextMenuContentTypeFactory;
 
-  const extensions::Extension* GetExtension() const;
+  const extensions::Extension* GetExtension();
 
   DISALLOW_COPY_AND_ASSIGN(ContextMenuContentTypePlatformApp);
 };
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 0e22e0e..6482e62 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -2541,8 +2541,9 @@
   else
     base::RecordAction(UserMetricsAction("MediaContextMenu_Pause"));
 
-  MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
-                      WebMediaPlayerAction(WebMediaPlayerAction::kPlay, play));
+  MediaPlayerActionAt(
+      gfx::Point(params_.x, params_.y),
+      WebMediaPlayerAction(WebMediaPlayerAction::Type::kPlay, play));
 }
 
 void RenderViewContextMenu::ExecMute() {
@@ -2552,15 +2553,16 @@
   else
     base::RecordAction(UserMetricsAction("MediaContextMenu_Unmute"));
 
-  MediaPlayerActionAt(gfx::Point(params_.x, params_.y),
-                      WebMediaPlayerAction(WebMediaPlayerAction::kMute, mute));
+  MediaPlayerActionAt(
+      gfx::Point(params_.x, params_.y),
+      WebMediaPlayerAction(WebMediaPlayerAction::Type::kMute, mute));
 }
 
 void RenderViewContextMenu::ExecLoop() {
   base::RecordAction(UserMetricsAction("MediaContextMenu_Loop"));
   MediaPlayerActionAt(
       gfx::Point(params_.x, params_.y),
-      WebMediaPlayerAction(WebMediaPlayerAction::kLoop,
+      WebMediaPlayerAction(WebMediaPlayerAction::Type::kLoop,
                            !IsCommandIdChecked(IDC_CONTENT_CONTEXT_LOOP)));
 }
 
@@ -2568,7 +2570,7 @@
   base::RecordAction(UserMetricsAction("MediaContextMenu_Controls"));
   MediaPlayerActionAt(
       gfx::Point(params_.x, params_.y),
-      WebMediaPlayerAction(WebMediaPlayerAction::kControls,
+      WebMediaPlayerAction(WebMediaPlayerAction::Type::kControls,
                            !IsCommandIdChecked(IDC_CONTENT_CONTEXT_CONTROLS)));
 }
 
@@ -2700,7 +2702,7 @@
 
   MediaPlayerActionAt(
       gfx::Point(params_.x, params_.y),
-      WebMediaPlayerAction(WebMediaPlayerAction::kPictureInPicture,
+      WebMediaPlayerAction(WebMediaPlayerAction::Type::kPictureInPicture,
                            !picture_in_picture_active));
 }
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 948b53d..f62c3f2 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -205,7 +205,7 @@
 }
 
 void CheckIfTabCanCommunicateWithUserWhileInBackground(
-    const content::WebContents* web_contents,
+    content::WebContents* web_contents,
     DecisionDetails* details) {
   DCHECK(details);
 
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger.cc b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
index bec14f94..28493d78 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger.cc
@@ -81,7 +81,7 @@
 
 // static
 int TabMetricsLogger::GetSiteEngagementScore(
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   if (!SiteEngagementService::IsEnabled())
     return -1;
 
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger.h b/chrome/browser/resource_coordinator/tab_metrics_logger.h
index c60888c..519d188 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger.h
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger.h
@@ -89,7 +89,7 @@
 
   // Returns the site engagement score for the WebContents, rounded down to 10s
   // to limit granularity. Returns -1 if site engagement service is disabled.
-  static int GetSiteEngagementScore(const content::WebContents* web_contents);
+  static int GetSiteEngagementScore(content::WebContents* web_contents);
 
   // Creates TabFeatures for logging or scoring tabs.
   // A common function for populating these features ensures that the same
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs
index bf7256d..6ff3391 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs
@@ -131,7 +131,8 @@
 /**
  * Test adding element to a atomic and non-atomic live regions.
  */
-TEST_F('ChromeVoxLiveRegionsUnitTest', 'AddToLiveRegion', function() {
+// Flaky on Chromium OS: crbug.com/909973.
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'DISABLED_AddToLiveRegion', function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="non_atomic_buddylist" aria-live="polite">
diff --git a/chrome/browser/resources/settings/people_page/BUILD.gn b/chrome/browser/resources/settings/people_page/BUILD.gn
index dc855b4..dab2acb9 100644
--- a/chrome/browser/resources/settings/people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/people_page/BUILD.gn
@@ -19,6 +19,7 @@
     ":lock_state_behavior",
     ":manage_profile",
     ":manage_profile_browser_proxy",
+    ":people_browser_proxy",
     ":people_page",
     ":profile_info_browser_proxy",
     ":setup_fingerprint_dialog",
@@ -150,10 +151,17 @@
   ]
 }
 
+js_library("people_browser_proxy") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
 js_library("people_page") {
   deps = [
     ":lock_screen",
     ":lock_state_behavior",
+    ":people_browser_proxy",
     ":profile_info_browser_proxy",
     ":signout_dialog",
     ":sync_browser_proxy",
diff --git a/chrome/browser/resources/settings/people_page/people_browser_proxy.html b/chrome/browser/resources/settings/people_page/people_browser_proxy.html
new file mode 100644
index 0000000..8fa2b67c
--- /dev/null
+++ b/chrome/browser/resources/settings/people_page/people_browser_proxy.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="people_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/people_page/people_browser_proxy.js b/chrome/browser/resources/settings/people_page/people_browser_proxy.js
new file mode 100644
index 0000000..1484661
--- /dev/null
+++ b/chrome/browser/resources/settings/people_page/people_browser_proxy.js
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A helper object used from the People section to interact with
+ * the browser.
+ */
+
+cr.exportPath('settings');
+
+cr.define('settings', function() {
+  /** @interface */
+  class PeopleBrowserProxy {
+    // TODO(dpapad): Create a simple OpenWindowProxy class that replaces the
+    // need for this method and similar methods in other BrowserProxy classes.
+    /**
+     * Opens the specified URL in a new tab.
+     * @param {string} url
+     */
+    openURL(url) {}
+  }
+
+  /** @implements {settings.PeopleBrowserProxy} */
+  class PeopleBrowserProxyImpl {
+    /** @override */
+    openURL(url) {
+      window.open(url);
+    }
+  }
+
+  cr.addSingletonGetter(PeopleBrowserProxyImpl);
+
+  return {
+    PeopleBrowserProxy: PeopleBrowserProxy,
+    PeopleBrowserProxyImpl: PeopleBrowserProxyImpl,
+  };
+});
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index b5ad088..5700245 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -12,9 +12,10 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="../controls/settings_toggle_button.html">
-<link rel="import" href="sync_page.html">
+<link rel="import" href="people_browser_proxy.html">
 <link rel="import" href="profile_info_browser_proxy.html">
 <link rel="import" href="sync_browser_proxy.html">
+<link rel="import" href="sync_page.html">
 <link rel="import" href="../icons.html">
 <link rel="import" href="../route.html">
 <link rel="import" href="../settings_page/settings_animated_pages.html">
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js
index f49e7fd..c84611e 100644
--- a/chrome/browser/resources/settings/people_page/people_page.js
+++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -311,12 +311,16 @@
   },
 
   /**
-   * Shows the manage passwords sub page.
+   * Shows a page to manage passwords. This is either the passwords sub page or
+   * the Google Password Manager page.
    * @param {!Event} event
    * @private
    */
   onPasswordsTap_: function(event) {
-    settings.navigateTo(settings.routes.MANAGE_PASSWORDS);
+    loadTimeData.getBoolean('navigateToGooglePasswordManager') ?
+        settings.PeopleBrowserProxyImpl.getInstance().openURL(
+            loadTimeData.getString('googlePasswordManagerUrl')) :
+        settings.navigateTo(settings.routes.MANAGE_PASSWORDS);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 4099d46..ca10f698 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -763,6 +763,12 @@
       <structure name="IDR_SETTINGS_PAYMENTS_SECTION_JS"
                  file="passwords_and_forms_page/payments_section.js"
                  type="chrome_html" />
+      <structure name="IDR_SETTINGS_PEOPLE_BROWSER_PROXY_HTML"
+                 file="people_page/people_browser_proxy.html"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_PEOPLE_BROWSER_PROXY_JS"
+                 file="people_page/people_browser_proxy.js"
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_PEOPLE_PAGE_HTML"
                  file="people_page/people_page.html"
                  type="chrome_html"
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index b8f2eec..4e16a6fa 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -27,6 +27,7 @@
     sources += [
       "chrome_cleaner/chrome_cleaner_controller_impl_win.cc",
       "chrome_cleaner/chrome_cleaner_controller_impl_win.h",
+      "chrome_cleaner/chrome_cleaner_controller_win.cc",
       "chrome_cleaner/chrome_cleaner_controller_win.h",
       "chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.cc",
       "chrome_cleaner/chrome_cleaner_dialog_controller_impl_win.h",
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
index 728e850..c66b45f 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
@@ -168,7 +168,7 @@
     // Feature extraction takes ownership of the request object
     // and passes it along to the done callback in the end.
     StartExtractMalwareFeatures(request);
-    ASSERT_TRUE(base::MessageLoopForUI::IsCurrent());
+    ASSERT_TRUE(base::MessageLoopCurrentForUI::IsSet());
     base::RunLoop().Run();
     EXPECT_EQ(1U, success_.count(request));
     EXPECT_TRUE(success_[request]);
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
index acda30f..23e1a3e 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
@@ -28,6 +28,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
@@ -50,10 +51,13 @@
 using ::testing::Values;
 using ::testing::ValuesIn;
 using CrashPoint = MockChromeCleanerProcess::CrashPoint;
+using ExtensionCleaningFeatureStatus =
+    MockChromeCleanerProcess::ExtensionCleaningFeatureStatus;
 using IdleReason = ChromeCleanerController::IdleReason;
 using ItemsReporting = MockChromeCleanerProcess::ItemsReporting;
 using State = ChromeCleanerController::State;
 using UserResponse = ChromeCleanerController::UserResponse;
+using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus;
 
 // Returns the PromptAcceptance value that ChromeCleanerController is supposed
 // to send to the Chrome Cleaner process when ReplyWithUserResponse() is
@@ -221,17 +225,6 @@
   kFetchSuccessValidProcess,
 };
 
-enum class UwsFoundStatus {
-  kNoUwsFound,
-  kUwsFoundRebootRequired,
-  kUwsFoundNoRebootRequired,
-};
-
-enum class ExtensionCleaningFeatureStatus {
-  kEnabled,
-  kDisabled,
-};
-
 typedef std::tuple<CleanerProcessStatus,
                    CrashPoint,
                    UwsFoundStatus,
@@ -660,100 +653,6 @@
   }
 }
 
-std::ostream& operator<<(std::ostream& out, CrashPoint crash_point) {
-  switch (crash_point) {
-    case CrashPoint::kNone:
-      return out << "NoCrash";
-    case CrashPoint::kOnStartup:
-      return out << "CrashOnStartup";
-    case CrashPoint::kAfterConnection:
-      return out << "CrashAfterConnection";
-    case CrashPoint::kAfterRequestSent:
-      return out << "CrashAfterRequestSent";
-    case CrashPoint::kAfterResponseReceived:
-      return out << "CrashAfterResponseReceived";
-    default:
-      NOTREACHED();
-      return out << "UnknownCrashPoint" << crash_point;
-  }
-}
-
-std::ostream& operator<<(std::ostream& out, UwsFoundStatus status) {
-  switch (status) {
-    case UwsFoundStatus::kNoUwsFound:
-      return out << "NoUwsFound";
-    case UwsFoundStatus::kUwsFoundRebootRequired:
-      return out << "UwsFoundRebootRequired";
-    case UwsFoundStatus::kUwsFoundNoRebootRequired:
-      return out << "UwsFoundNoRebootRequired";
-    default:
-      NOTREACHED();
-      return out << "UnknownFoundStatus" << status;
-  }
-}
-
-std::ostream& operator<<(std::ostream& out,
-                         ExtensionCleaningFeatureStatus status) {
-  switch (status) {
-    case ExtensionCleaningFeatureStatus::kEnabled:
-      return out << "ExtensionCleaningEnabled";
-    case ExtensionCleaningFeatureStatus::kDisabled:
-      return out << "ExtensionCleaningDisabled";
-    default:
-      NOTREACHED();
-      return out << "UnknownExtensionCleaningStatus" << status;
-  }
-}
-
-std::ostream& operator<<(std::ostream& out, ItemsReporting items_reporting) {
-  switch (items_reporting) {
-    case ItemsReporting::kUnsupported:
-      return out << "kUnsupported";
-    case ItemsReporting::kNotReported:
-      return out << "kNotReported";
-    case ItemsReporting::kReported:
-      return out << "kReported";
-    default:
-      NOTREACHED();
-      return out << "UnknownItemsReporting";
-  }
-}
-
-std::ostream& operator<<(std::ostream& out, UserResponse response) {
-  switch (response) {
-    case UserResponse::kAcceptedWithLogs:
-      return out << "UserAcceptedWithLogs";
-    case UserResponse::kAcceptedWithoutLogs:
-      return out << "UserAcceptedWithoutLogs";
-    case UserResponse::kDenied:
-      return out << "UserDenied";
-    case UserResponse::kDismissed:
-      return out << "UserDismissed";
-    default:
-      NOTREACHED();
-      return out << "UnknownUserResponse" << response;
-  }
-}
-
-// ::testing::PrintToStringParamName does not format tuples as a valid test
-// name, so this functor can be used to get each element in the tuple
-// explicitly and format them using the above operator<< overrides.
-struct ChromeCleanerControllerTestParamsToString {
-  std::string operator()(
-      const ::testing::TestParamInfo<ChromeCleanerControllerTestParams>& info)
-      const {
-    std::ostringstream param_name;
-    param_name << std::get<0>(info.param) << "_";
-    param_name << std::get<1>(info.param) << "_";
-    param_name << std::get<2>(info.param) << "_";
-    param_name << std::get<3>(info.param) << "_";
-    param_name << std::get<4>(info.param) << "_";
-    param_name << std::get<5>(info.param) << "_";
-    param_name << std::get<6>(info.param);
-    return param_name.str();
-  }
-};
-
 // This includes all crash points after kOnStartup, except kAfterRequestSent.
 // That one's not used because if we induce a crash after the mock cleaner
 // sends a request, there would be a race condition between the request being
@@ -783,7 +682,7 @@
                    UserResponse::kAcceptedWithoutLogs,
                    UserResponse::kDenied,
                    UserResponse::kDismissed)),
-    ChromeCleanerControllerTestParamsToString());
+    chrome_cleaner::GetParamNameForTest());
 
 // Tests where the process gets past the startup phase but finds nothing to
 // clean. Since we don't progress to any stage where the parameters after
@@ -799,7 +698,7 @@
             Values(ItemsReporting::kNotReported),
             Values(ItemsReporting::kNotReported),
             Values(UserResponse::kDismissed)),
-    ChromeCleanerControllerTestParamsToString());
+    chrome_cleaner::GetParamNameForTest());
 
 // Tests where the process fails before starting a scan. This never gets far
 // enough to collect results, so we can save time by not repeating the tests
@@ -819,7 +718,7 @@
             Values(ItemsReporting::kUnsupported),
             Values(ItemsReporting::kUnsupported),
             Values(UserResponse::kAcceptedWithLogs)),
-    ChromeCleanerControllerTestParamsToString());
+    chrome_cleaner::GetParamNameForTest());
 
 // Tests for the interaction between reporter runs and all possible states.
 // Signals from reporter execution may lead to state transitions only if there
@@ -939,36 +838,15 @@
       SwReporterInvocationResult::kCleanupToBeOffered);
 }
 
-std::ostream& operator<<(std::ostream& out,
-                         ChromeCleanerController::State state) {
-  switch (state) {
-    case ChromeCleanerController::State::kIdle:
-      return out << "Idle";
-    case ChromeCleanerController::State::kReporterRunning:
-      return out << "ReporterRunning";
-    case ChromeCleanerController::State::kScanning:
-      return out << "Scanning";
-    case ChromeCleanerController::State::kInfected:
-      return out << "Infected";
-    case ChromeCleanerController::State::kCleaning:
-      return out << "Cleaning";
-    case ChromeCleanerController::State::kRebootRequired:
-      return out << "RebootRequired";
-    default:
-      NOTREACHED();
-      return out << "UnknownUserResponse" << state;
-  }
-}
-
-INSTANTIATE_TEST_CASE_P(
-    All,
-    ChromeCleanerControllerReporterInteractionTest,
-    Values(ChromeCleanerController::State::kIdle,
-           ChromeCleanerController::State::kReporterRunning,
-           ChromeCleanerController::State::kScanning,
-           ChromeCleanerController::State::kInfected,
-           ChromeCleanerController::State::kCleaning,
-           ChromeCleanerController::State::kRebootRequired));
+INSTANTIATE_TEST_CASE_P(All,
+                        ChromeCleanerControllerReporterInteractionTest,
+                        Values(ChromeCleanerController::State::kIdle,
+                               ChromeCleanerController::State::kReporterRunning,
+                               ChromeCleanerController::State::kScanning,
+                               ChromeCleanerController::State::kInfected,
+                               ChromeCleanerController::State::kCleaning,
+                               ChromeCleanerController::State::kRebootRequired),
+                        chrome_cleaner::GetParamNameForTest());
 
 }  // namespace
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc
new file mode 100644
index 0000000..2ab9d444
--- /dev/null
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.cc
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h"
+
+namespace safe_browsing {
+
+using UserResponse = ChromeCleanerController::UserResponse;
+
+std::ostream& operator<<(std::ostream& out,
+                         ChromeCleanerController::State state) {
+  switch (state) {
+    case ChromeCleanerController::State::kIdle:
+      return out << "Idle";
+    case ChromeCleanerController::State::kReporterRunning:
+      return out << "ReporterRunning";
+    case ChromeCleanerController::State::kScanning:
+      return out << "Scanning";
+    case ChromeCleanerController::State::kInfected:
+      return out << "Infected";
+    case ChromeCleanerController::State::kCleaning:
+      return out << "Cleaning";
+    case ChromeCleanerController::State::kRebootRequired:
+      return out << "RebootRequired";
+    default:
+      NOTREACHED();
+      return out << "UnknownUserResponse" << state;
+  }
+}
+
+std::ostream& operator<<(std::ostream& out, UserResponse response) {
+  switch (response) {
+    case UserResponse::kAcceptedWithLogs:
+      return out << "UserAcceptedWithLogs";
+    case UserResponse::kAcceptedWithoutLogs:
+      return out << "UserAcceptedWithoutLogs";
+    case UserResponse::kDenied:
+      return out << "UserDenied";
+    case UserResponse::kDismissed:
+      return out << "UserDismissed";
+    default:
+      NOTREACHED();
+      return out << "UnknownUserResponse";
+  }
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
index 048c5cb..dae7ce3a 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h
@@ -224,6 +224,13 @@
   DISALLOW_COPY_AND_ASSIGN(ChromeCleanerController);
 };
 
+//  These are used for debug output in tests.
+std::ostream& operator<<(std::ostream& out,
+                         ChromeCleanerController::State state);
+
+std::ostream& operator<<(std::ostream& out,
+                         ChromeCleanerController::UserResponse response);
+
 }  // namespace safe_browsing
 
 #endif  // CHROME_BROWSER_SAFE_BROWSING_CHROME_CLEANER_CHROME_CLEANER_CONTROLLER_WIN_H_
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
index d21a5142..d4f6cc91 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
@@ -26,6 +26,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
 #include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -45,6 +46,9 @@
 using ::testing::UnorderedElementsAreArray;
 using ::testing::Values;
 using ChromeMetricsStatus = ChromeCleanerRunner::ChromeMetricsStatus;
+using ExtensionCleaningFeatureStatus =
+    MockChromeCleanerProcess::ExtensionCleaningFeatureStatus;
+using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus;
 
 enum class ReporterEngine {
   kUnspecified,
@@ -234,33 +238,18 @@
                    ChromePromptValue::kUserInitiated),
             Bool()));
 
-// Enum to be used as parameter for the ChromeCleanerRunnerTest fixture below.
-enum class UwsFoundState {
-  kNoUwsFound,
-  kUwsFoundRebootRequired,
-  kUwsFoundNoRebootRequired,
-};
+typedef std::tuple<UwsFoundStatus,
+                   ExtensionCleaningFeatureStatus,
+                   MockChromeCleanerProcess::ItemsReporting,
+                   MockChromeCleanerProcess::ItemsReporting,
+                   MockChromeCleanerProcess::CrashPoint,
+                   PromptAcceptance>
+    ChromeCleanerRunnerTestParams;
 
 // Test fixture for testing ChromeCleanerRunner with a mock Chrome Cleaner
 // process.
-//
-// Parameters:
-//
-// - uws_found_state (UwsFoundState): Whether the Chrome Cleaner process should
-//       find UwS on the system and if so whether reboot is required.
-// - crash_point (CrashPoint): a single crash point where the Chrome Cleaner
-//       process will crash. See the MockChromeCleanerProcess documentation for
-//       possible values.
-// - prompt_acceptance_to_send (PromptAcceptance): the prompt acceptance value
-//       that Chrome should send to the Chrome Cleaner process. Must be DENIED
-//       if |found_uws| is false.
 class ChromeCleanerRunnerTest
-    : public testing::TestWithParam<
-          std::tuple<UwsFoundState,
-                     MockChromeCleanerProcess::ItemsReporting,
-                     MockChromeCleanerProcess::ItemsReporting,
-                     MockChromeCleanerProcess::CrashPoint,
-                     PromptAcceptance>>,
+    : public testing::TestWithParam<ChromeCleanerRunnerTestParams>,
       public ChromeCleanerRunnerTestDelegate {
  public:
   ChromeCleanerRunnerTest()
@@ -274,26 +263,31 @@
     testing_profile_ = profile_manager_.CreateTestingProfile("Profile 1");
     MockChromeCleanerProcess::AddMockExtensionsToProfile(testing_profile_);
 
-    UwsFoundState uws_found_state;
+    UwsFoundStatus uws_found_state;
     MockChromeCleanerProcess::ItemsReporting registry_keys_reporting;
     MockChromeCleanerProcess::ItemsReporting extensions_reporting;
     MockChromeCleanerProcess::CrashPoint crash_point;
-    PromptAcceptance prompt_acceptance_to_send;
-    std::tie(uws_found_state, registry_keys_reporting, extensions_reporting,
-             crash_point, prompt_acceptance_to_send) = GetParam();
+    std::tie(uws_found_state, extension_cleaning_feature_status_,
+             registry_keys_reporting, extensions_reporting, crash_point,
+             prompt_acceptance_to_send_) = GetParam();
 
-    ASSERT_FALSE(uws_found_state == UwsFoundState::kNoUwsFound &&
-                 prompt_acceptance_to_send != PromptAcceptance::DENIED);
+    ASSERT_FALSE(uws_found_state == UwsFoundStatus::kNoUwsFound &&
+                 prompt_acceptance_to_send_ != PromptAcceptance::DENIED);
+
+    if (extension_cleaning_feature_status_ ==
+        ExtensionCleaningFeatureStatus::kEnabled)
+      features_.InitAndEnableFeature(kChromeCleanupExtensionsFeature);
+    else
+      features_.InitAndDisableFeature(kChromeCleanupExtensionsFeature);
 
     cleaner_process_options_.SetReportedResults(
-        uws_found_state != UwsFoundState::kNoUwsFound, registry_keys_reporting,
+        uws_found_state != UwsFoundStatus::kNoUwsFound, registry_keys_reporting,
         extensions_reporting);
     cleaner_process_options_.set_reboot_required(
-        uws_found_state == UwsFoundState::kUwsFoundRebootRequired);
+        uws_found_state == UwsFoundStatus::kUwsFoundRebootRequired);
     cleaner_process_options_.set_crash_point(crash_point);
     cleaner_process_options_.set_expected_user_response(
-        prompt_acceptance_to_send);
-    prompt_acceptance_to_send_ = prompt_acceptance_to_send;
+        prompt_acceptance_to_send_);
 
     SetChromeCleanerRunnerTestDelegateForTesting(this);
   }
@@ -376,6 +370,7 @@
 
   MockChromeCleanerProcess::Options cleaner_process_options_;
   PromptAcceptance prompt_acceptance_to_send_ = PromptAcceptance::UNSPECIFIED;
+  ExtensionCleaningFeatureStatus extension_cleaning_feature_status_;
 
   // Set by OnProcessDone().
   ChromeCleanerRunner::ProcessStatus process_status_;
@@ -386,6 +381,8 @@
   bool on_prompt_user_called_ = false;
   bool on_connection_closed_called_ = false;
   bool on_process_done_called_ = false;
+
+  base::test::ScopedFeatureList features_;
 };
 
 MULTIPROCESS_TEST_MAIN(MockChromeCleanerProcessMain) {
@@ -409,8 +406,7 @@
   return mock_cleaner_process.Run();
 }
 
-// Fails for official builds. http://crbug.com/907443
-TEST_P(ChromeCleanerRunnerTest, DISABLED_WithMockCleanerProcess) {
+TEST_P(ChromeCleanerRunnerTest, WithMockCleanerProcess) {
   CallRunChromeCleaner();
   run_loop_.Run();
 
@@ -439,7 +435,9 @@
     std::set<base::string16> extension_names;
     received_scanner_results_.FetchExtensionNames(testing_profile_,
                                                   &extension_names);
-    if (cleaner_process_options_.extension_ids()) {
+    if (cleaner_process_options_.extension_ids() &&
+        extension_cleaning_feature_status_ ==
+            ExtensionCleaningFeatureStatus::kEnabled) {
       EXPECT_THAT(extension_names,
                   UnorderedElementsAreArray(
                       *cleaner_process_options_.expected_extension_names()));
@@ -456,10 +454,12 @@
 }
 
 INSTANTIATE_TEST_CASE_P(
-    All,
+    NoUwsFound,
     ChromeCleanerRunnerTest,
     Combine(
-        Values(UwsFoundState::kNoUwsFound),
+        Values(UwsFoundStatus::kNoUwsFound),
+        // When no UwS is found we don't care about extension removel.
+        Values(ExtensionCleaningFeatureStatus::kDisabled),
         Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported,
                MockChromeCleanerProcess::ItemsReporting::kNotReported,
                MockChromeCleanerProcess::ItemsReporting::kReported),
@@ -471,14 +471,17 @@
                MockChromeCleanerProcess::CrashPoint::kAfterConnection,
                MockChromeCleanerProcess::CrashPoint::kAfterRequestSent,
                MockChromeCleanerProcess::CrashPoint::kAfterResponseReceived),
-        Values(PromptAcceptance::DENIED)));
+        Values(PromptAcceptance::DENIED)),
+    chrome_cleaner::GetParamNameForTest());
 
 INSTANTIATE_TEST_CASE_P(
     UwsFound,
     ChromeCleanerRunnerTest,
     Combine(
-        Values(UwsFoundState::kUwsFoundRebootRequired,
-               UwsFoundState::kUwsFoundNoRebootRequired),
+        Values(UwsFoundStatus::kUwsFoundRebootRequired,
+               UwsFoundStatus::kUwsFoundNoRebootRequired),
+        Values(ExtensionCleaningFeatureStatus::kEnabled,
+               ExtensionCleaningFeatureStatus::kDisabled),
         Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported,
                MockChromeCleanerProcess::ItemsReporting::kNotReported,
                MockChromeCleanerProcess::ItemsReporting::kReported),
@@ -492,7 +495,8 @@
                MockChromeCleanerProcess::CrashPoint::kAfterResponseReceived),
         Values(PromptAcceptance::DENIED,
                PromptAcceptance::ACCEPTED_WITH_LOGS,
-               PromptAcceptance::ACCEPTED_WITHOUT_LOGS)));
+               PromptAcceptance::ACCEPTED_WITHOUT_LOGS)),
+    chrome_cleaner::GetParamNameForTest());
 
 }  // namespace
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
index 2b9a0b7..af403fa 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
@@ -39,6 +39,11 @@
 using ::chrome_cleaner::mojom::ChromePromptPtr;
 using ::chrome_cleaner::mojom::ChromePromptPtrInfo;
 using ::chrome_cleaner::mojom::PromptAcceptance;
+using CrashPoint = MockChromeCleanerProcess::CrashPoint;
+using ExtensionCleaningFeatureStatus =
+    MockChromeCleanerProcess::ExtensionCleaningFeatureStatus;
+using ItemsReporting = MockChromeCleanerProcess::ItemsReporting;
+using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus;
 
 constexpr char kCrashPointSwitch[] = "mock-crash-point";
 constexpr char kUwsFoundSwitch[] = "mock-uws-found";
@@ -420,4 +425,63 @@
   std::move(quit_closure).Run();
 }
 
+std::ostream& operator<<(std::ostream& out, CrashPoint crash_point) {
+  switch (crash_point) {
+    case CrashPoint::kNone:
+      return out << "NoCrash";
+    case CrashPoint::kOnStartup:
+      return out << "CrashOnStartup";
+    case CrashPoint::kAfterConnection:
+      return out << "CrashAfterConnection";
+    case CrashPoint::kAfterRequestSent:
+      return out << "CrashAfterRequestSent";
+    case CrashPoint::kAfterResponseReceived:
+      return out << "CrashAfterResponseReceived";
+    default:
+      NOTREACHED();
+      return out << "UnknownCrashPoint";
+  }
+}
+
+std::ostream& operator<<(std::ostream& out, UwsFoundStatus status) {
+  switch (status) {
+    case UwsFoundStatus::kNoUwsFound:
+      return out << "NoUwsFound";
+    case UwsFoundStatus::kUwsFoundRebootRequired:
+      return out << "UwsFoundRebootRequired";
+    case UwsFoundStatus::kUwsFoundNoRebootRequired:
+      return out << "UwsFoundNoRebootRequired";
+    default:
+      NOTREACHED();
+      return out << "UnknownFoundStatus";
+  }
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         ExtensionCleaningFeatureStatus status) {
+  switch (status) {
+    case ExtensionCleaningFeatureStatus::kEnabled:
+      return out << "ExtensionCleaningEnabled";
+    case ExtensionCleaningFeatureStatus::kDisabled:
+      return out << "ExtensionCleaningDisabled";
+    default:
+      NOTREACHED();
+      return out << "UnknownExtensionCleaningStatus";
+  }
+}
+
+std::ostream& operator<<(std::ostream& out, ItemsReporting items_reporting) {
+  switch (items_reporting) {
+    case ItemsReporting::kUnsupported:
+      return out << "kUnsupported";
+    case ItemsReporting::kNotReported:
+      return out << "kNotReported";
+    case ItemsReporting::kReported:
+      return out << "kReported";
+    default:
+      NOTREACHED();
+      return out << "UnknownItemsReporting";
+  }
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
index 509438a5..3b82927 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
@@ -59,6 +59,17 @@
     kNumItemsReporting,
   };
 
+  enum class UwsFoundStatus {
+    kNoUwsFound,
+    kUwsFoundRebootRequired,
+    kUwsFoundNoRebootRequired,
+  };
+
+  enum class ExtensionCleaningFeatureStatus {
+    kEnabled,
+    kDisabled,
+  };
+
   static constexpr int kInternalTestFailureExitCode = 100001;
   static constexpr int kDeliberateCrashExitCode = 100002;
   static constexpr int kNothingFoundExitCode = 2;
@@ -174,6 +185,22 @@
   chrome_cleaner::mojom::ChromePromptPtr* chrome_prompt_ptr_ = nullptr;
 };
 
+// Making test parameter types printable.
+
+std::ostream& operator<<(std::ostream& out,
+                         MockChromeCleanerProcess::CrashPoint crash_point);
+
+std::ostream& operator<<(std::ostream& out,
+                         MockChromeCleanerProcess::UwsFoundStatus status);
+
+std::ostream& operator<<(
+    std::ostream& out,
+    MockChromeCleanerProcess::ExtensionCleaningFeatureStatus status);
+
+std::ostream& operator<<(
+    std::ostream& out,
+    MockChromeCleanerProcess::ItemsReporting items_reporting);
+
 }  // namespace safe_browsing
 
 #endif  // CHROME_BROWSER_SAFE_BROWSING_CHROME_CLEANER_MOCK_CHROME_CLEANER_PROCESS_WIN_H_
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 56199a15..933dfac 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -117,7 +117,7 @@
 
 // Given a |web_contents|, returns the navigation id of its last committed
 // navigation.
-int64_t GetLastCommittedNavigationID(const content::WebContents* web_contents) {
+int64_t GetLastCommittedNavigationID(content::WebContents* web_contents) {
   if (!web_contents)
     return 0;
   content::NavigationEntry* navigation =
@@ -546,7 +546,7 @@
       safe_browsing::TriggerType::GAIA_PASSWORD_REUSE, web_contents, resource,
       url_loader_factory, /*history_service=*/nullptr,
       TriggerManager::GetSBErrorDisplayOptions(*profile_->GetPrefs(),
-                                               *web_contents));
+                                               web_contents));
 }
 
 void ChromePasswordProtectionService::MaybeFinishCollectingThreatDetails(
@@ -563,7 +563,7 @@
       safe_browsing::TriggerType::GAIA_PASSWORD_REUSE, web_contents,
       base::TimeDelta::FromMilliseconds(0), did_proceed, /*num_visit=*/0,
       TriggerManager::GetSBErrorDisplayOptions(*profile_->GetPrefs(),
-                                               *web_contents));
+                                               web_contents));
 }
 
 PrefService* ChromePasswordProtectionService::GetPrefs() {
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index 424ff8a..9535a63 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -183,7 +183,7 @@
     download::DownloadItem* item,
     const CheckDownloadCallback& callback) {
   DCHECK(!item->GetUrlChain().empty());
-  const content::WebContents* web_contents =
+  content::WebContents* web_contents =
       content::DownloadItemUtils::GetWebContents(item);
   // |web_contents| can be null in tests.
   // Checks if this download is whitelisted by enterprise policy.
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index c25cab16..2fbcf07 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -202,7 +202,7 @@
   }
 
   void MaybeReportSafeBrowsingHit(const HitReport& hit_report,
-                                  const WebContents* web_contents) override {
+                                  WebContents* web_contents) override {
     if (SafeBrowsingUIManager::ShouldSendHitReport(hit_report, web_contents)) {
       hit_report_sent_ = true;
     }
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index a44e796a..be573ea 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -290,9 +290,8 @@
 
 class FakeSafeBrowsingUIManager : public TestSafeBrowsingUIManager {
  public:
-  void MaybeReportSafeBrowsingHit(
-      const safe_browsing::HitReport& hit_report,
-      const content::WebContents* web_contents) override {
+  void MaybeReportSafeBrowsingHit(const safe_browsing::HitReport& hit_report,
+                                  content::WebContents* web_contents) override {
     EXPECT_FALSE(got_hit_report_);
     got_hit_report_ = true;
     hit_report_ = hit_report;
diff --git a/chrome/browser/safe_browsing/trigger_creator.cc b/chrome/browser/safe_browsing/trigger_creator.cc
index 9a11840..94765c2 100644
--- a/chrome/browser/safe_browsing/trigger_creator.cc
+++ b/chrome/browser/safe_browsing/trigger_creator.cc
@@ -46,7 +46,7 @@
   // running on old tabs, but that's acceptable. The trigger will be started for
   // new tabs.
   SBErrorOptions options = TriggerManager::GetSBErrorDisplayOptions(
-      *profile->GetPrefs(), *web_contents);
+      *profile->GetPrefs(), web_contents);
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
       content::BrowserContext::GetDefaultStoragePartition(profile)
           ->GetURLLoaderFactoryForBrowserProcess();
diff --git a/chrome/browser/safe_browsing/ui_manager.cc b/chrome/browser/safe_browsing/ui_manager.cc
index 7c3ab4e..606bd95 100644
--- a/chrome/browser/safe_browsing/ui_manager.cc
+++ b/chrome/browser/safe_browsing/ui_manager.cc
@@ -105,9 +105,8 @@
 }
 
 // static
-bool SafeBrowsingUIManager::ShouldSendHitReport(
-    const HitReport& hit_report,
-    const WebContents* web_contents) {
+bool SafeBrowsingUIManager::ShouldSendHitReport(const HitReport& hit_report,
+                                                WebContents* web_contents) {
   return web_contents &&
          hit_report.extended_reporting_level != SBER_LEVEL_OFF &&
          !web_contents->GetBrowserContext()->IsOffTheRecord();
@@ -118,7 +117,7 @@
 // extended-reporting users.
 void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit(
     const HitReport& hit_report,
-    const WebContents* web_contents) {
+    WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Send report if user opted-in to extended reporting and is not in
diff --git a/chrome/browser/safe_browsing/ui_manager.h b/chrome/browser/safe_browsing/ui_manager.h
index 4287c044..073567a 100644
--- a/chrome/browser/safe_browsing/ui_manager.h
+++ b/chrome/browser/safe_browsing/ui_manager.h
@@ -75,9 +75,8 @@
   // Report hits to unsafe contents (malware, phishing, unsafe download URL)
   // to the server. Can only be called on UI thread.  The hit report will
   // only be sent if the user has enabled SBER and is not in incognito mode.
-  void MaybeReportSafeBrowsingHit(
-      const safe_browsing::HitReport& hit_report,
-      const content::WebContents* web_contents) override;
+  void MaybeReportSafeBrowsingHit(const safe_browsing::HitReport& hit_report,
+                                  content::WebContents* web_contents) override;
 
   // Creates the whitelist URL set for tests that create a blocking page
   // themselves and then simulate OnBlockingPageDone(). OnBlockingPageDone()
@@ -108,7 +107,7 @@
   // Helper method to ensure hit reports are only sent when the user has
   // opted in to extended reporting and is not currently in incognito mode.
   static bool ShouldSendHitReport(const HitReport& hit_report,
-                                  const content::WebContents* web_contents);
+                                  content::WebContents* web_contents);
 
  private:
   friend class SafeBrowsingUIManagerTest;
diff --git a/chrome/browser/search/iframe_source.cc b/chrome/browser/search/iframe_source.cc
index e8e9bba9..a772485 100644
--- a/chrome/browser/search/iframe_source.cc
+++ b/chrome/browser/search/iframe_source.cc
@@ -57,7 +57,7 @@
     std::string* origin) const {
   if (wc_getter.is_null())
     return false;
-  const content::WebContents* contents = wc_getter.Run();
+  content::WebContents* contents = wc_getter.Run();
   if (!contents)
     return false;
   const content::NavigationEntry* entry =
diff --git a/chrome/browser/search/search.cc b/chrome/browser/search/search.cc
index 6b0b967..c5730d8 100644
--- a/chrome/browser/search/search.cc
+++ b/chrome/browser/search/search.cc
@@ -219,7 +219,7 @@
   const NewTabURLState state;
 };
 
-bool IsRenderedInInstantProcess(const content::WebContents* contents,
+bool IsRenderedInInstantProcess(content::WebContents* contents,
                                 Profile* profile) {
 #if defined(OS_ANDROID)
   return false;
@@ -270,7 +270,7 @@
                      url == chrome::kChromeSearchLocalNtpUrl);
 }
 
-bool IsInstantNTP(const content::WebContents* contents) {
+bool IsInstantNTP(content::WebContents* contents) {
   if (!contents)
     return false;
 
@@ -284,7 +284,7 @@
   return NavEntryIsInstantNTP(contents, entry);
 }
 
-bool NavEntryIsInstantNTP(const content::WebContents* contents,
+bool NavEntryIsInstantNTP(content::WebContents* contents,
                           const content::NavigationEntry* entry) {
   if (!contents || !entry || !IsInstantExtendedAPIEnabled())
     return false;
diff --git a/chrome/browser/search/search.h b/chrome/browser/search/search.h
index 04ed373..0bca746 100644
--- a/chrome/browser/search/search.h
+++ b/chrome/browser/search/search.h
@@ -39,11 +39,11 @@
 // Returns true if the active navigation entry of |contents| is a New Tab page
 // rendered in an Instant process. This is the last committed entry if it
 // exists, and otherwise the visible entry.
-bool IsInstantNTP(const content::WebContents* contents);
+bool IsInstantNTP(content::WebContents* contents);
 
 // Same as IsInstantNTP but uses |nav_entry| to determine the URL for the page
 // instead of using the visible entry.
-bool NavEntryIsInstantNTP(const content::WebContents* contents,
+bool NavEntryIsInstantNTP(content::WebContents* contents,
                           const content::NavigationEntry* nav_entry);
 
 // Returns true if |url| corresponds to a New Tab page that would get rendered
diff --git a/chrome/browser/search/search_unittest.cc b/chrome/browser/search/search_unittest.cc
index 3fb4b3ca..eb6be50 100644
--- a/chrome/browser/search/search_unittest.cc
+++ b/chrome/browser/search/search_unittest.cc
@@ -89,7 +89,7 @@
     template_url_service->SetUserSelectedDefaultSearchProvider(template_url);
   }
 
-  bool InInstantProcess(const content::WebContents* contents) {
+  bool InInstantProcess(content::WebContents* contents) {
     InstantService* instant_service =
         InstantServiceFactory::GetForProfile(profile());
     return instant_service->IsInstantProcess(
@@ -181,7 +181,7 @@
   for (size_t i = 0; i < arraysize(kProcessIsolationTestCases); ++i) {
     const ProcessIsolationTestCase& test = kProcessIsolationTestCases[i];
     AddTab(browser(), GURL("chrome://blank"));
-    const content::WebContents* contents =
+    content::WebContents* contents =
         browser()->tab_strip_model()->GetActiveWebContents();
 
     // Navigate to start URL.
@@ -278,7 +278,7 @@
   AddTab(browser(), GURL("chrome://blank"));
   for (const SearchTestCase& test : kInstantNTPTestCases) {
     NavigateAndCommitActiveTab(GURL(test.url));
-    const content::WebContents* contents =
+    content::WebContents* contents =
         browser()->tab_strip_model()->GetWebContentsAt(0);
     EXPECT_EQ(test.expected_result, IsInstantNTP(contents))
         << test.url << " " << test.comment;
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index cc4bc001..4cd122a1 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -81,7 +81,7 @@
 bool HasSingleNewTabPage(Browser* browser) {
   if (browser->tab_strip_model()->count() != 1)
     return false;
-  const content::WebContents* active_tab =
+  content::WebContents* active_tab =
       browser->tab_strip_model()->GetWebContentsAt(0);
   return active_tab->GetURL() == chrome::kChromeUINewTabURL ||
          search::IsInstantNTP(active_tab);
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 9fb41d85..1408ff4c 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -608,7 +608,7 @@
 
   // Make sure that the restored tab was restored with the correct
   // timestamp and status code.
-  const content::WebContents* contents =
+  content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_TRUE(contents);
   const content::NavigationEntry* entry =
diff --git a/chrome/browser/ssl/cert_verifier_browser_test.cc b/chrome/browser/ssl/cert_verifier_browser_test.cc
index b47891c..be8e279 100644
--- a/chrome/browser/ssl/cert_verifier_browser_test.cc
+++ b/chrome/browser/ssl/cert_verifier_browser_test.cc
@@ -11,11 +11,13 @@
 ChromeMockCertVerifier::~ChromeMockCertVerifier() = default;
 
 void ChromeMockCertVerifier::SetUpInProcessBrowserTestFixture() {
+  ContentMockCertVerifier::SetUpInProcessBrowserTestFixture();
   IOThread::SetCertVerifierForTesting(mock_cert_verifier_internal());
   ProfileIOData::SetCertVerifierForTesting(mock_cert_verifier_internal());
 }
 
 void ChromeMockCertVerifier::TearDownInProcessBrowserTestFixture() {
+  ContentMockCertVerifier::TearDownInProcessBrowserTestFixture();
   IOThread::SetCertVerifierForTesting(nullptr);
   ProfileIOData::SetCertVerifierForTesting(nullptr);
 }
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index 48e40f54..2ceec889 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -82,7 +82,7 @@
 
 // ========== LOG TRANSLATE EVENT ==============
 
-void LogTranslateEvent(const content::WebContents* const web_contents,
+void LogTranslateEvent(content::WebContents* const web_contents,
                        const metrics::TranslateEventProto& translate_event) {
   if (!FeatureList::IsEnabled(switches::kSyncUserTranslationEvents))
     return;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index fcd74aad..8e91c8a 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1336,6 +1336,8 @@
       "ash/chrome_screenshot_grabber_test_observer.h",
       "ash/chrome_shell_delegate.cc",
       "ash/chrome_shell_delegate.h",
+      "ash/contained_shell_client.cc",
+      "ash/contained_shell_client.h",
       "ash/ime_controller_client.cc",
       "ash/ime_controller_client.h",
       "ash/ksv/keyboard_shortcut_viewer_util.cc",
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 2ff497b..c7fdf59 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -1325,7 +1325,9 @@
   }
 
   std::unordered_set<std::string> apps_to_remove =
-      GetAppsForPackage(package_name);
+      GetAppsAndShortcutsForPackage(package_name,
+                                    true, /* include_only_launchable_apps */
+                                    false /* include_shortcuts */);
 
   for (const auto& app : apps) {
     const std::string app_id = GetAppId(app->package_name, app->activity);
@@ -1386,11 +1388,13 @@
 std::unordered_set<std::string> ArcAppListPrefs::GetAppsForPackage(
     const std::string& package_name) const {
   return GetAppsAndShortcutsForPackage(package_name,
+                                       false, /* include_only_launchable_apps */
                                        false /* include_shortcuts */);
 }
 
 std::unordered_set<std::string> ArcAppListPrefs::GetAppsAndShortcutsForPackage(
     const std::string& package_name,
+    bool include_only_launchable_apps,
     bool include_shortcuts) const {
   std::unordered_set<std::string> app_set;
   const base::DictionaryValue* apps =
@@ -1422,6 +1426,13 @@
         continue;
     }
 
+    if (include_only_launchable_apps) {
+      // Filter out non-lauchable apps.
+      bool launchable = false;
+      if (!app->GetBoolean(kLaunchable, &launchable) || !launchable)
+        continue;
+    }
+
     app_set.insert(app_it.key());
   }
 
@@ -1431,7 +1442,9 @@
 void ArcAppListPrefs::HandlePackageRemoved(const std::string& package_name) {
   DCHECK(IsArcAndroidEnabledForProfile(profile_));
   const std::unordered_set<std::string> apps_to_remove =
-      GetAppsAndShortcutsForPackage(package_name, true /* include_shortcuts */);
+      GetAppsAndShortcutsForPackage(package_name,
+                                    false /* include_only_launchable_apps */,
+                                    true /* include_shortcuts */);
   for (const auto& app_id : apps_to_remove)
     RemoveApp(app_id);
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
index f52a4b3..0c74d78 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -394,8 +394,13 @@
   void DisableAllApps();
   void RemoveAllAppsAndPackages();
   std::vector<std::string> GetAppIdsNoArcEnabledCheck() const;
+  // Retrieves registered apps and shortcuts for specific package |package_name|
+  // If |include_only_launchable_apps| is set to true then only launchable apps
+  // are included and runtime apps are ignored. Otherwise all apps are returned.
+  // |include_shortcuts| specifies if shorcuts needs to be included.
   std::unordered_set<std::string> GetAppsAndShortcutsForPackage(
       const std::string& package_name,
+      bool include_only_launchable_apps,
       bool include_shortcuts) const;
 
   // Enumerates apps from preferences and notifies listeners about available
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index f9738a6f..a2110813 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -186,6 +186,20 @@
   }
 }
 
+ArcAppListPrefs::AppInfo GetAppInfoExpectation(const arc::mojom::AppInfo& app,
+                                               bool launchable) {
+  return ArcAppListPrefs::AppInfo(
+      app.name, app.package_name, app.activity, std::string() /* intent_uri */,
+      std::string() /* icon_resource_id */, base::Time() /* last_launch_time */,
+      base::Time() /* install_time */, app.sticky, app.notifications_enabled,
+      true /* ready */, false /* suspended */, launchable /* show_in_launcher*/,
+      false /* shortcut */, launchable);
+}
+
+MATCHER_P(ArcPackageInfoIs, package, "") {
+  return arg.Equals(package);
+}
+
 }  // namespace
 
 class ArcAppModelBuilderTest : public extensions::ExtensionServiceTestBase,
@@ -1235,12 +1249,8 @@
   const std::string app_id = ArcAppTest::GetAppId(app);
 
   ArcAppListPrefs::AppInfo::SetIgnoreCompareInstallTimeForTesting(true);
-  const ArcAppListPrefs::AppInfo expected_app_info_registered(
-      app.name, app.package_name, app.activity, std::string() /* intent_uri */,
-      std::string() /* icon_resource_id */, base::Time() /* last_launch_time */,
-      base::Time() /* install_time */, app.sticky, app.notifications_enabled,
-      true /* ready */, false /* suspended */, true /* show_in_launcher*/,
-      false /* shortcut */, true /* launchable */);
+  const ArcAppListPrefs::AppInfo expected_app_info_registered =
+      GetAppInfoExpectation(app, true /* launchable */);
 
   ArcAppListPrefs::AppInfo expected_app_info_disabled(
       expected_app_info_registered);
@@ -2146,6 +2156,76 @@
                                std::vector<arc::mojom::ShortcutInfo>());
 }
 
+// This validates that runtime apps are not removed on package change event.
+TEST_P(ArcAppModelBuilderTest, DontRemoveRuntimeAppOnPackageChange) {
+  ArcAppListPrefs::AppInfo::SetIgnoreCompareInstallTimeForTesting(true);
+  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get());
+  ASSERT_TRUE(prefs);
+
+  arc::MockArcAppListPrefsObserver observer;
+
+  ASSERT_GE(fake_apps().size(), 2U);
+
+  // Second app should be preserved after the package update.
+  std::vector<arc::mojom::AppInfo> apps(fake_apps().begin(),
+                                        fake_apps().begin() + 2);
+  apps[0].package_name = apps[1].package_name;
+
+  const std::string app_id1 = ArcAppTest::GetAppId(apps[0]);
+  const std::string app_id2 = ArcAppTest::GetAppId(apps[1]);
+
+  arc::mojom::ArcPackageInfoPtr package = CreatePackage(apps[0].package_name);
+
+  prefs->AddObserver(&observer);
+
+  EXPECT_CALL(observer, OnPackageInstalled(ArcPackageInfoIs(*package)))
+      .Times(1);
+  EXPECT_CALL(observer,
+              OnAppRegistered(app_id1, GetAppInfoExpectation(
+                                           apps[0], true /* launchable */)))
+      .Times(1);
+  EXPECT_CALL(observer,
+              OnAppRegistered(app_id2, GetAppInfoExpectation(
+                                           apps[1], true /* launchable */)))
+      .Times(1);
+
+  AddPackage(package);
+
+  app_instance()->RefreshAppList();
+  app_instance()->SendRefreshAppList(apps);
+
+  // Send a task for non-existing lauchable app. That would register new runtime
+  // app.
+  arc::mojom::AppInfo app_runtime = apps[0];
+  app_runtime.activity += "_runtime";
+  // Runtime apps have notifications_enabled and sticky false.
+  app_runtime.notifications_enabled = false;
+  app_runtime.sticky = false;
+  const std::string app_id3 = ArcAppTest::GetAppId(app_runtime);
+
+  EXPECT_CALL(observer, OnAppRegistered(
+                            app_id3, GetAppInfoExpectation(
+                                         app_runtime, false /* launchable */)))
+      .Times(1);
+  EXPECT_CALL(observer,
+              OnTaskCreated(1 /* task_id */, app_runtime.package_name,
+                            app_runtime.activity, std::string() /* name */))
+      .Times(1);
+
+  app_instance()->SendTaskCreated(1, app_runtime, std::string());
+
+  // Simulate package update when first launchable app is removed. This should
+  // trigger app removing for it but not for the runtime app.
+  EXPECT_CALL(observer, OnAppRemoved(app_id1)).Times(1);
+  EXPECT_CALL(observer, OnAppRemoved(app_id2)).Times(0);
+  EXPECT_CALL(observer, OnAppRemoved(app_id3)).Times(0);
+
+  apps.erase(apps.begin());
+  app_instance()->SendPackageAppListRefreshed(apps[0].package_name, apps);
+
+  prefs->RemoveObserver(&observer);
+}
+
 TEST_P(ArcDefaulAppTest, DefaultApps) {
   ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_.get());
   ASSERT_NE(nullptr, prefs);
diff --git a/chrome/browser/ui/ash/assistant/assistant_context_util.cc b/chrome/browser/ui/ash/assistant/assistant_context_util.cc
index 2d34ddec..89e2caed 100644
--- a/chrome/browser/ui/ash/assistant/assistant_context_util.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_context_util.cc
@@ -33,7 +33,7 @@
 }
 
 ax::mojom::AssistantExtraPtr CreateAssistantExtra(
-    const content::WebContents* web_contents,
+    content::WebContents* web_contents,
     const gfx::Rect& bounds_pixel) {
   auto assistant_extra = ax::mojom::AssistantExtra::New();
   assistant_extra->url = web_contents->GetLastCommittedURL();
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index b4f8f6e..d1b8b57 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ui/ash/ash_shell_init.h"
 #include "chrome/browser/ui/ash/cast_config_client_media_router.h"
 #include "chrome/browser/ui/ash/chrome_new_window_client.h"
+#include "chrome/browser/ui/ash/contained_shell_client.h"
 #include "chrome/browser/ui/ash/ime_controller_client.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/login_screen_client.h"
@@ -283,6 +284,9 @@
     // Initialize TabScrubber after the Ash Shell has been initialized.
     TabScrubber::GetInstance();
   }
+
+  if (base::FeatureList::IsEnabled(ash::features::kContainedShell))
+    contained_shell_client_ = std::make_unique<ContainedShellClient>();
 }
 
 void ChromeBrowserMainExtraPartsAsh::PostBrowserStart() {
@@ -316,6 +320,7 @@
   media_client_.reset();
   login_screen_client_.reset();
   cast_config_client_media_router_.reset();
+  contained_shell_client_.reset();
 
   // Initialized in PreProfileInit:
   system_tray_client_.reset();
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
index 2ff85ee..1b2510f6 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.h
@@ -36,6 +36,7 @@
 class AshShellInit;
 class CastConfigClientMediaRouter;
 class ChromeNewWindowClient;
+class ContainedShellClient;
 class DataPromoNotification;
 class ImeControllerClient;
 class ImmersiveContextMus;
@@ -118,6 +119,7 @@
 
   // Initialized in PostProfileInit in all configs:
   std::unique_ptr<CastConfigClientMediaRouter> cast_config_client_media_router_;
+  std::unique_ptr<ContainedShellClient> contained_shell_client_;
   std::unique_ptr<LoginScreenClient> login_screen_client_;
   std::unique_ptr<MediaClient> media_client_;
   std::unique_ptr<policy::DisplaySettingsHandler> display_settings_handler_;
diff --git a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
index 27d074c5..b0db9e2 100644
--- a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
+++ b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
@@ -335,7 +335,7 @@
                     scoped_refptr<base::RefCountedMemory> png_data,
                     ScreenshotFileResult result,
                     const base::FilePath& local_path) {
-  DCHECK(!base::MessageLoopForUI::IsCurrent());
+  DCHECK(!base::MessageLoopCurrentForUI::IsSet());
   DCHECK(!screenshot_path.empty());
 
   ScreenshotResult screenshot_result = ScreenshotResult::SUCCESS;
@@ -369,7 +369,7 @@
 void EnsureLocalDirectoryExists(
     const base::FilePath& path,
     ChromeScreenshotGrabber::FileCallback callback) {
-  DCHECK(!base::MessageLoopForUI::IsCurrent());
+  DCHECK(!base::MessageLoopCurrentForUI::IsSet());
   DCHECK(!path.empty());
 
   if (!base::CreateDirectory(path.DirName())) {
diff --git a/chrome/browser/ui/ash/contained_shell_client.cc b/chrome/browser/ui/ash/contained_shell_client.cc
new file mode 100644
index 0000000..92e3473d
--- /dev/null
+++ b/chrome/browser/ui/ash/contained_shell_client.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 "chrome/browser/ui/ash/contained_shell_client.h"
+
+#include <utility>
+
+#include "ash/public/interfaces/constants.mojom.h"
+#include "content/public/common/service_manager_connection.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+ContainedShellClient::ContainedShellClient() {
+  ash::mojom::ContainedShellControllerPtr contained_shell_controller;
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(ash::mojom::kServiceName, &contained_shell_controller);
+
+  ash::mojom::ContainedShellClientPtr client;
+  binding_.Bind(mojo::MakeRequest(&client));
+  contained_shell_controller->SetClient(std::move(client));
+}
+
+ContainedShellClient::~ContainedShellClient() = default;
diff --git a/chrome/browser/ui/ash/contained_shell_client.h b/chrome/browser/ui/ash/contained_shell_client.h
new file mode 100644
index 0000000..d21d124
--- /dev/null
+++ b/chrome/browser/ui/ash/contained_shell_client.h
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_CONTAINED_SHELL_CLIENT_H_
+#define CHROME_BROWSER_UI_ASH_CONTAINED_SHELL_CLIENT_H_
+
+#include "ash/public/interfaces/contained_shell.mojom.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+class ContainedShellClient : public ash::mojom::ContainedShellClient {
+ public:
+  ContainedShellClient();
+  ~ContainedShellClient() override;
+
+ private:
+  mojo::Binding<ash::mojom::ContainedShellClient> binding_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(ContainedShellClient);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_CONTAINED_SHELL_CLIENT_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index b52414d8..9a34d36 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -1070,7 +1070,7 @@
 }
 
 bool ChromeLauncherController::IsIncognito(
-    const content::WebContents* web_contents) const {
+    content::WebContents* web_contents) const {
   const Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   return profile->IsOffTheRecord() && !profile->IsGuestSession() &&
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 736d2fc..1d6b09f 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -321,7 +321,7 @@
   void CreateBrowserShortcutLauncherItem();
 
   // Check if the given |web_contents| is in incognito mode.
-  bool IsIncognito(const content::WebContents* web_contents) const;
+  bool IsIncognito(content::WebContents* web_contents) const;
 
   // Finds the index of where to insert the next item.
   int FindInsertionPoint();
diff --git a/chrome/browser/ui/ash/media_client.cc b/chrome/browser/ui/ash/media_client.cc
index a4d68ae..db2788a 100644
--- a/chrome/browser/ui/ash/media_client.cc
+++ b/chrome/browser/ui/ash/media_client.cc
@@ -226,7 +226,7 @@
                                   int render_frame_id,
                                   content::MediaStreamType stream_type,
                                   const content::MediaRequestState state) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   // The PostTask is necessary because the state of MediaStreamCaptureIndicator
   // gets updated after this.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc b/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc
index c7ffcfb..bc7e4456 100644
--- a/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc
+++ b/chrome/browser/ui/ash/tablet_mode_page_behavior_browsertest.cc
@@ -53,11 +53,11 @@
   }
 
   content::WebPreferences GetWebKitPreferences(
-      const content::WebContents* web_contents) const {
+      content::WebContents* web_contents) const {
     return web_contents->GetRenderViewHost()->GetWebkitPreferences();
   }
 
-  void ValidateWebPrefs(const content::WebContents* web_contents,
+  void ValidateWebPrefs(content::WebContents* web_contents,
                         bool tablet_mode_enabled) const {
     const content::WebPreferences web_prefs =
         GetWebKitPreferences(web_contents);
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
index cc760f1..bd09cc0 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.cc
@@ -180,7 +180,7 @@
 }
 
 bool SafeBrowsingTriggeredPopupBlocker::IsEnabled(
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   // If feature is disabled, return false. This is done so that if the feature
   // is broken it can be disabled irrespective of the policy.
   if (!base::FeatureList::IsEnabled(kAbusiveExperienceEnforce))
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
index cd289e9..3d3ec5eb 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h
@@ -97,7 +97,7 @@
 
   // Enabled state is governed by both a feature flag and a pref (which can be
   // controlled by enterprise policy).
-  static bool IsEnabled(const content::WebContents* web_contents);
+  static bool IsEnabled(content::WebContents* web_contents);
 
   // Data scoped to a single page. Will be reset at navigation commit.
   class PageData {
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 9d52c42..2cb2def 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -1616,7 +1616,7 @@
 
 namespace {
 
-int GetZoomPercent(const content::WebContents* contents,
+int GetZoomPercent(content::WebContents* contents,
                    bool* enable_plus,
                    bool* enable_minus) {
   int percent =
diff --git a/chrome/browser/ui/browser_tabrestore_browsertest.cc b/chrome/browser/ui/browser_tabrestore_browsertest.cc
index 2a72fa14..d5c2064 100644
--- a/chrome/browser/ui/browser_tabrestore_browsertest.cc
+++ b/chrome/browser/ui/browser_tabrestore_browsertest.cc
@@ -89,7 +89,7 @@
   // Thus we should wait for "load stop" event before we will perform
   // CheckVisbility on "about:blank".
   {
-    const content::WebContents* about_blank_contents =
+    content::WebContents* about_blank_contents =
         browser->tab_strip_model()->GetWebContentsAt(0);
     EXPECT_EQ("about:blank", about_blank_contents->GetURL().spec());
     if (about_blank_contents->IsLoading() ||
@@ -146,7 +146,7 @@
   // The same as in RecentTabsMenuTabDisposition test case.
   // See there for the explanation.
   {
-    const content::WebContents* about_blank_contents =
+    content::WebContents* about_blank_contents =
         browser->tab_strip_model()->GetWebContentsAt(0);
     EXPECT_EQ("about:blank", about_blank_contents->GetURL().spec());
     if (about_blank_contents->IsLoading() ||
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index 209fa583..6548c59 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -271,7 +271,7 @@
     return false;
   if (scheme.empty())  // Any trusted popup window
     return true;
-  const content::WebContents* web_contents =
+  content::WebContents* web_contents =
       browser->tab_strip_model()->GetWebContentsAt(0);
   if (!web_contents)
     return false;
diff --git a/chrome/browser/ui/cocoa/confirm_quit_panel_controller.h b/chrome/browser/ui/cocoa/confirm_quit_panel_controller.h
index 34ba656..36d30e97 100644
--- a/chrome/browser/ui/cocoa/confirm_quit_panel_controller.h
+++ b/chrome/browser/ui/cocoa/confirm_quit_panel_controller.h
@@ -22,9 +22,8 @@
 + (ConfirmQuitPanelController*)sharedController;
 
 // Runs a modal loop that brings up the panel and handles the logic for if and
-// when to terminate. Returns NSApplicationTerminateReply for use in
-// -[NSApplicationDelegate applicationShouldTerminate:].
-- (NSApplicationTerminateReply)runModalLoopForApplication:(NSApplication*)app;
+// when to terminate. Returns YES if the quit should continue.
+- (BOOL)runModalLoopForApplication:(NSApplication*)app;
 
 // Shows the window.
 - (void)showWindow:(id)sender;
diff --git a/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm b/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm
index d237f4a..9192773a 100644
--- a/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm
+++ b/chrome/browser/ui/cocoa/confirm_quit_panel_controller.mm
@@ -198,7 +198,7 @@
   return self;
 }
 
-- (NSApplicationTerminateReply)runModalLoopForApplication:(NSApplication*)app {
+- (BOOL)runModalLoopForApplication:(NSApplication*)app {
   base::scoped_nsobject<ConfirmQuitPanelController> keepAlive([self retain]);
 
   // If this is the second of two such attempts to quit within a certain time
@@ -227,7 +227,7 @@
       confirm_quit::RecordHistogram(confirm_quit::kDoubleTap);
     else
       confirm_quit::RecordHistogram(confirm_quit::kTapHold);
-    return NSTerminateNow;
+    return YES;
   } else {
     [lastQuitAttempt release];  // Harmless if already nil.
     lastQuitAttempt = [timeNow retain];  // Record this attempt for next time.
@@ -277,16 +277,16 @@
     // The user held down the combination long enough that quitting should
     // happen.
     confirm_quit::RecordHistogram(confirm_quit::kHoldDuration);
-    return NSTerminateNow;
+    return YES;
   } else {
     // Slowly fade the confirm window out in case the user doesn't
     // understand what they have to do to quit.
     [self dismissPanel];
-    return NSTerminateCancel;
+    return NO;
   }
 
   // Default case: terminate.
-  return NSTerminateNow;
+  return YES;
 }
 
 - (void)windowWillClose:(NSNotification*)notif {
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 9b2f1d15..f6e8831f 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -191,7 +191,7 @@
 
   DCHECK(extension->is_hosted_app());
 
-  const content::WebContents* web_contents =
+  content::WebContents* web_contents =
       browser_->tab_strip_model()->GetActiveWebContents();
 
   // Don't show a toolbar until a navigation has occurred.
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
index cd5a3458..9ebd1d3 100644
--- a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
@@ -190,6 +190,14 @@
   return net::AppendQueryParameter(url, "utm_campaign", campaign);
 }
 
+bool ShouldManagePasswordsinGooglePasswordManager(Profile* profile) {
+  return base::FeatureList::IsEnabled(
+             password_manager::features::kGooglePasswordManager) &&
+         password_manager_util::GetPasswordSyncState(
+             ProfileSyncServiceFactory::GetForProfile(profile)) ==
+             password_manager::SYNCING_NORMAL_ENCRYPTION;
+}
+
 // Navigation is handled differently on Android.
 #if !defined(OS_ANDROID)
 void NavigateToGooglePasswordManager(Profile* profile,
@@ -202,11 +210,7 @@
 
 void NavigateToManagePasswordsPage(Browser* browser,
                                    ManagePasswordsReferrer referrer) {
-  if (base::FeatureList::IsEnabled(
-          password_manager::features::kGooglePasswordManager) &&
-      password_manager_util::GetPasswordSyncState(
-          ProfileSyncServiceFactory::GetForProfile(browser->profile())) ==
-          password_manager::SYNCING_NORMAL_ENCRYPTION) {
+  if (ShouldManagePasswordsinGooglePasswordManager(browser->profile())) {
     NavigateToGooglePasswordManager(browser->profile(), referrer);
   } else {
     chrome::ShowPasswordManager(browser);
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.h b/chrome/browser/ui/passwords/manage_passwords_view_utils.h
index b2ac3b33..2d32a2fd 100644
--- a/chrome/browser/ui/passwords/manage_passwords_view_utils.h
+++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.h
@@ -79,6 +79,13 @@
 GURL GetGooglePasswordManagerURL(
     password_manager::ManagePasswordsReferrer referrer);
 
+// Returns whether users should manage their passwords in the Google Password
+// Manager. This includes users that are syncing their passwords without a
+// custom passphrase and for which the Google Password Manager experiment is
+// activated. For these users links to the Chrome password settings will be
+// repaced with links to the Google Password Manager, i.e. passwords.google.com.
+bool ShouldManagePasswordsinGooglePasswordManager(Profile* profile);
+
 // Navigates to the Google Password Manager, i.e. passwords.google.com.
 void NavigateToGooglePasswordManager(
     Profile* profile,
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
index 634f89d5..76719a82 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
@@ -9,9 +9,8 @@
 #include "content/public/browser/web_contents.h"
 
 SearchIPCRouterPolicyImpl::SearchIPCRouterPolicyImpl(
-    const content::WebContents* web_contents)
-    : web_contents_(web_contents),
-      is_incognito_(true) {
+    content::WebContents* web_contents)
+    : web_contents_(web_contents), is_incognito_(true) {
   DCHECK(web_contents);
 
   Profile* profile =
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.h b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
index ea9bc595..d1ecd9b 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.h
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
@@ -20,7 +20,7 @@
 // The SearchIPCRouter::Policy implementation.
 class SearchIPCRouterPolicyImpl : public SearchIPCRouter::Policy {
  public:
-  explicit SearchIPCRouterPolicyImpl(const content::WebContents* web_contents);
+  explicit SearchIPCRouterPolicyImpl(content::WebContents* web_contents);
   ~SearchIPCRouterPolicyImpl() override;
 
  private:
@@ -54,7 +54,7 @@
     is_incognito_ = is_incognito;
   }
 
-  const content::WebContents* web_contents_;
+  content::WebContents* web_contents_;
   bool is_incognito_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchIPCRouterPolicyImpl);
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
index bb84aca..2738eaf 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
@@ -57,7 +57,7 @@
 
   // Now the visible URL corresponds to the download, but the last committed URL
   // is still the NTP.
-  const content::WebContents* tab =
+  content::WebContents* tab =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_EQ(GURL("http://foo/download.zip"), tab->GetVisibleURL());
   ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl), tab->GetLastCommittedURL());
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 2d04367a..27f7033 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -49,7 +49,7 @@
 
 namespace {
 
-bool IsCacheableNTP(const content::WebContents* contents) {
+bool IsCacheableNTP(content::WebContents* contents) {
   const content::NavigationEntry* entry =
       contents->GetController().GetLastCommittedEntry();
   return search::NavEntryIsInstantNTP(contents, entry) &&
@@ -58,7 +58,7 @@
 
 // Returns true if |contents| are rendered inside an Instant process.
 bool InInstantProcess(const InstantService* instant_service,
-                      const content::WebContents* contents) {
+                      content::WebContents* contents) {
   if (!instant_service || !contents)
     return false;
 
diff --git a/chrome/browser/ui/settings_window_manager_chromeos.cc b/chrome/browser/ui/settings_window_manager_chromeos.cc
index ce55f02..01da3df 100644
--- a/chrome/browser/ui/settings_window_manager_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_chromeos.cc
@@ -47,7 +47,7 @@
   Browser* browser = FindBrowserForProfile(profile);
   if (browser) {
     DCHECK(browser->profile() == profile);
-    const content::WebContents* web_contents =
+    content::WebContents* web_contents =
         browser->tab_strip_model()->GetWebContentsAt(0);
     if (web_contents && web_contents->GetURL() == gurl) {
       browser->window()->Show();
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index 6a3cf24..ec106e9 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -1088,14 +1088,7 @@
   policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
 }
 
-// http://crbug.com/691707
-#if defined(OS_MACOSX)
-#define MAYBE_AddFirstRunTab DISABLED_AddFirstRunTab
-#else
-#define MAYBE_AddFirstRunTab AddFirstRunTab
-#endif
-IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorFirstRunTest,
-                       MAYBE_AddFirstRunTab) {
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorFirstRunTest, AddFirstRunTab) {
   ASSERT_TRUE(embedded_test_server()->Start());
   StartupBrowserCreator browser_creator;
   browser_creator.AddFirstRunTab(
@@ -1230,13 +1223,7 @@
             tab_strip->GetWebContentsAt(0)->GetURL().ExtractFileName());
 }
 
-// http://crbug.com/691707
-#if defined(OS_MACOSX)
-#define MAYBE_WelcomePages DISABLED_WelcomePages
-#else
-#define MAYBE_WelcomePages WelcomePages
-#endif
-IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorFirstRunTest, MAYBE_WelcomePages) {
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorFirstRunTest, WelcomePages) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   ProfileManager* profile_manager = g_browser_process->profile_manager();
@@ -1287,14 +1274,8 @@
             tab_strip->GetWebContentsAt(0)->GetURL().possibly_invalid_spec());
 }
 
-// http://crbug.com/691707
-#if defined(OS_MACOSX)
-#define MAYBE_WelcomePagesWithPolicy DISABLED_WelcomePagesWithPolicy
-#else
-#define MAYBE_WelcomePagesWithPolicy WelcomePagesWithPolicy
-#endif
 IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorFirstRunTest,
-                       MAYBE_WelcomePagesWithPolicy) {
+                       WelcomePagesWithPolicy) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Set the following user policies:
diff --git a/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc b/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
index fa65cee..16359c5 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
@@ -26,7 +26,7 @@
 views::ViewsDelegate::ProcessMenuAcceleratorResult
 ChromeViewsDelegate::ProcessAcceleratorWhileMenuShowing(
     const ui::Accelerator& accelerator) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   // Early return because mash chrome does not have access to ash::Shell
   if (features::IsMultiProcessMash())
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index f7d1fbd..4ba264d 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -394,8 +394,7 @@
 
 bool GlassBrowserFrameView::ShouldTabIconViewAnimate() const {
   DCHECK(ShowCustomIcon());
-  const content::WebContents* current_tab =
-      browser_view()->GetActiveWebContents();
+  content::WebContents* current_tab = browser_view()->GetActiveWebContents();
   return current_tab && current_tab->IsLoading();
 }
 
diff --git a/chrome/browser/ui/views/menu_model_adapter_test.cc b/chrome/browser/ui/views/menu_model_adapter_test.cc
index 170cb20c..7e52499 100644
--- a/chrome/browser/ui/views/menu_model_adapter_test.cc
+++ b/chrome/browser/ui/views/menu_model_adapter_test.cc
@@ -234,7 +234,7 @@
 
     menu_model_adapter_.BuildMenu(menu_);
 
-    ASSERT_TRUE(base::MessageLoopForUI::IsCurrent());
+    ASSERT_TRUE(base::MessageLoopCurrentForUI::IsSet());
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, CreateEventTask(this, &MenuModelAdapterTest::Step3));
   }
diff --git a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
index f55a790..3a1947b 100644
--- a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
+++ b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
@@ -345,12 +345,7 @@
 // Disabled for being flaky. Tracked in:
 // TODO(erg): Fix DND tests on linux_aura. http://crbug.com/163931.
 // TODO(tapted): De-flake and run on Mac. http://crbug.com/449058.
-#if defined(OS_WIN)
-#define MAYBE_TestInMenuDrag TestInMenuDrag
-#else
-#define MAYBE_TestInMenuDrag DISABLED_TestInMenuDrag
-#endif
-VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, MAYBE_TestInMenuDrag)
+VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, DISABLED_TestInMenuDrag)
 
 class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest {
  public:
diff --git a/chrome/browser/ui/views/simple_message_box_views.cc b/chrome/browser/ui/views/simple_message_box_views.cc
index 93a41ef..d8d1cda2 100644
--- a/chrome/browser/ui/views/simple_message_box_views.cc
+++ b/chrome/browser/ui/views/simple_message_box_views.cc
@@ -109,7 +109,7 @@
 // ResourceBundle is not initialized yet.
 // Fallback to logging with a default response or a Windows MessageBox.
 #if defined(OS_WIN)
-  if (!base::MessageLoopForUI::IsCurrent() ||
+  if (!base::MessageLoopCurrentForUI::IsSet() ||
       !base::RunLoop::IsRunningOnCurrentThread() ||
       !ui::ResourceBundle::HasSharedInstance()) {
     LOG_IF(ERROR, !checkbox_text.empty()) << "Dialog checkbox won't be shown";
@@ -121,7 +121,7 @@
     return chrome::MESSAGE_BOX_RESULT_DEFERRED;
   }
 #elif defined(OS_MACOSX)
-  if (!base::MessageLoopForUI::IsCurrent() ||
+  if (!base::MessageLoopCurrentForUI::IsSet() ||
       !base::RunLoop::IsRunningOnCurrentThread() ||
       !ui::ResourceBundle::HasSharedInstance()) {
     // Even though this function could return a value synchronously here in
@@ -132,7 +132,7 @@
     return chrome::MESSAGE_BOX_RESULT_DEFERRED;
   }
 #else
-  if (!base::MessageLoopForUI::IsCurrent() ||
+  if (!base::MessageLoopCurrentForUI::IsSet() ||
       !ui::ResourceBundle::HasSharedInstance() ||
       !display::Screen::GetScreen()) {
     LOG(ERROR) << "Unable to show a dialog outside the UI thread message loop: "
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index e83c5a5..e19fc68 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -250,10 +250,6 @@
       : LabelButton(listener, text) {}
   ~InMenuButton() override {}
 
-  void set_role_is_button(bool role_is_button) {
-    role_is_button_ = role_is_button;
-  }
-
   void Init(InMenuButtonBackground::ButtonType type) {
     // An InMenuButton should always be focusable regardless of the platform.
     // Hence we don't use SetFocusForPlatform().
@@ -268,8 +264,7 @@
 
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
     LabelButton::GetAccessibleNodeData(node_data);
-    if (!role_is_button_)
-      node_data->role = ax::mojom::Role::kMenuItem;
+    node_data->role = ax::mojom::Role::kMenuItem;
   }
 
   // views::LabelButton
@@ -295,10 +290,6 @@
   }
 
  private:
-  // Indicates whether to expose this to accessibility as a Button.  If it is a
-  // button, the accelerator will not be added to the accessible label.
-  bool role_is_button_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(InMenuButton);
 };
 
@@ -337,14 +328,15 @@
       int string_id,
       InMenuButtonBackground::ButtonType type,
       int index) {
-    return CreateButtonWithAccName(string_id, type, index, string_id, false);
+    return CreateButtonWithAccName(string_id, type, index, string_id,
+                                   /*add_accelerator_text*/ true);
   }
 
   InMenuButton* CreateButtonWithAccName(int string_id,
                                         InMenuButtonBackground::ButtonType type,
                                         int index,
                                         int acc_string_id,
-                                        bool role_is_button) {
+                                        bool add_accelerator_text) {
     // Should only be invoked during construction when |menu_| is valid.
     DCHECK(menu_);
     InMenuButton* button = new InMenuButton(
@@ -352,10 +344,9 @@
                                          '&', nullptr, nullptr));
     button->Init(type);
     button->SetAccessibleName(GetAccessibleNameForAppMenuItem(
-        menu_model_, index, acc_string_id, !role_is_button));
+        menu_model_, index, acc_string_id, add_accelerator_text));
     button->set_tag(index);
     button->SetEnabled(menu_model_->IsEnabledAt(index));
-    button->set_role_is_button(role_is_button);
 
     AddChildView(button);
     // all buttons on menu should must be a custom button in order for
@@ -502,7 +493,8 @@
 
     decrement_button_ = CreateButtonWithAccName(
         IDS_ZOOM_MINUS2, InMenuButtonBackground::LEADING_BORDER,
-        decrement_index, IDS_ACCNAME_ZOOM_MINUS2, true);
+        decrement_index, IDS_ACCNAME_ZOOM_MINUS2,
+        /*add_accelerator_text*/ false);
 
     zoom_label_ = new Label(base::FormatPercent(100));
     zoom_label_->SetAutoColorReadabilityEnabled(false);
@@ -520,7 +512,7 @@
 
     increment_button_ = CreateButtonWithAccName(
         IDS_ZOOM_PLUS2, InMenuButtonBackground::NO_BORDER, increment_index,
-        IDS_ACCNAME_ZOOM_PLUS2, true);
+        IDS_ACCNAME_ZOOM_PLUS2, /*add_accelerator_text*/ false);
 
     fullscreen_button_ = new FullscreenButton(this);
     // all buttons on menu should must be a custom button in order for
@@ -540,7 +532,8 @@
     fullscreen_button_->SetBackground(std::make_unique<InMenuButtonBackground>(
         InMenuButtonBackground::LEADING_BORDER));
     fullscreen_button_->SetAccessibleName(GetAccessibleNameForAppMenuItem(
-        menu_model, fullscreen_index, IDS_ACCNAME_FULLSCREEN, true));
+        menu_model, fullscreen_index, IDS_ACCNAME_FULLSCREEN,
+        /*add_accelerator_text*/ true));
     AddChildView(fullscreen_button_);
 
     // Need to set a font list for the zoom label width calculations.
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.h b/chrome/browser/ui/views/toolbar/browser_actions_container.h
index 09337f88..f39a410 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.h
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.h
@@ -164,7 +164,7 @@
   std::string GetIdAt(size_t index) const;
 
   // Returns the ToolbarActionView* associated with the given |extension|, or
-  // NULL if none exists.
+  // nullptr if none exists.
   ToolbarActionView* GetViewForId(const std::string& id);
 
   // Update the views to reflect the state of the toolbar actions.
@@ -258,8 +258,6 @@
   // A struct representing the position at which an action will be dropped.
   struct DropPosition;
 
-  typedef std::vector<std::unique_ptr<ToolbarActionView>> ToolbarActionViews;
-
   // Clears the |active_bubble_|, and unregisters the container as an observer.
   void ClearActiveBubble(views::Widget* widget);
 
@@ -285,8 +283,8 @@
   // The controlling ToolbarActionsBar, which handles most non-view logic.
   std::unique_ptr<ToolbarActionsBar> toolbar_actions_bar_;
 
-  // The vector of toolbar actions (icons/image buttons for each action).
-  ToolbarActionViews toolbar_action_views_;
+  // Child toolbar action buttons.
+  std::vector<std::unique_ptr<ToolbarActionView>> toolbar_action_views_;
 
   // The Browser object the container is associated with.
   Browser* const browser_;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index f4a61c4..8cfbe4a 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -97,7 +97,8 @@
 
 void ToolbarActionView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   views::MenuButton::GetAccessibleNodeData(node_data);
-  node_data->role = ax::mojom::Role::kButton;
+  node_data->role = delegate_->ShownInsideMenu() ? ax::mojom::Role::kMenuItem
+                                                 : ax::mojom::Role::kButton;
 }
 
 std::unique_ptr<LabelButtonBorder> ToolbarActionView::CreateDefaultBorder()
diff --git a/chrome/browser/ui/views/translate/translate_language_browsertest.cc b/chrome/browser/ui/views/translate/translate_language_browsertest.cc
index cba454e..9a8c9209 100644
--- a/chrome/browser/ui/views/translate/translate_language_browsertest.cc
+++ b/chrome/browser/ui/views/translate/translate_language_browsertest.cc
@@ -140,7 +140,7 @@
   }
 
   language::UrlLanguageHistogram* GetUrlLanguageHistogram() {
-    const content::WebContents* const web_contents =
+    content::WebContents* const web_contents =
         browser_->tab_strip_model()->GetActiveWebContents();
     EXPECT_TRUE(web_contents);
     content::BrowserContext* const browser_context =
diff --git a/chrome/browser/ui/views/uninstall_view.cc b/chrome/browser/ui/views/uninstall_view.cc
index 559d893..bd1f281 100644
--- a/chrome/browser/ui/views/uninstall_view.cc
+++ b/chrome/browser/ui/views/uninstall_view.cc
@@ -179,7 +179,7 @@
 namespace chrome {
 
 int ShowUninstallBrowserPrompt() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   int result = service_manager::RESULT_CODE_NORMAL_EXIT;
 
   base::RunLoop run_loop;
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/discover_window_manager.cc b/chrome/browser/ui/webui/chromeos/login/discover/discover_window_manager.cc
index 908dff82..648f2e7 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/discover_window_manager.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/discover_window_manager.cc
@@ -54,7 +54,7 @@
   Browser* browser = FindBrowserForProfile(profile);
   if (browser) {
     DCHECK(browser->profile() == profile);
-    const content::WebContents* web_contents =
+    content::WebContents* web_contents =
         browser->tab_strip_model()->GetWebContentsAt(0);
     if (web_contents && web_contents->GetURL() == gurl) {
       browser->window()->Show();
diff --git a/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc b/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc
index 69675d4..3632a26 100644
--- a/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc
+++ b/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc
@@ -152,7 +152,7 @@
 }
 
 void CustomHomePagesTableModel::SetToCurrentlyOpenPages(
-    const content::WebContents* ignore_contents) {
+    content::WebContents* ignore_contents) {
   // Remove the current entries.
   while (RowCount())
     RemoveWithoutNotification(0);
@@ -166,7 +166,7 @@
     for (int tab_index = 0;
          tab_index < browser->tab_strip_model()->count();
          ++tab_index) {
-      const content::WebContents* contents =
+      content::WebContents* contents =
           browser->tab_strip_model()->GetWebContentsAt(tab_index);
       if (contents == ignore_contents)
         continue;
diff --git a/chrome/browser/ui/webui/settings/custom_home_pages_table_model.h b/chrome/browser/ui/webui/settings/custom_home_pages_table_model.h
index 04760e43..80937b2 100644
--- a/chrome/browser/ui/webui/settings/custom_home_pages_table_model.h
+++ b/chrome/browser/ui/webui/settings/custom_home_pages_table_model.h
@@ -54,7 +54,7 @@
 
   // Clears any entries and fills the list with pages currently opened in the
   // browser. |ignore_contents| is omitted from the open pages.
-  void SetToCurrentlyOpenPages(const content::WebContents* ignore_contents);
+  void SetToCurrentlyOpenPages(content::WebContents* ignore_contents);
 
   // Returns the set of urls this model contains.
   std::vector<GURL> GetURLs();
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 82bc2fca..23b674e 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1500,14 +1500,15 @@
                                     IDS_SETTINGS_CREDIT_CARD_NONE);
   }
 
+  GURL google_password_manager_url = GetGooglePasswordManagerURL(
+      password_manager::ManagePasswordsReferrer::kChromeSettings);
   html_source->AddString(
       "managePasswordsLabel",
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_PASSWORDS_MANAGE_PASSWORDS,
-          base::UTF8ToUTF16(
-              GetGooglePasswordManagerURL(
-                  password_manager::ManagePasswordsReferrer::kChromeSettings)
-                  .spec())));
+          base::UTF8ToUTF16(google_password_manager_url.spec())));
+  html_source->AddString("googlePasswordManagerUrl",
+                         google_password_manager_url.spec());
   html_source->AddString("passwordManagerLearnMoreURL",
                          chrome::kPasswordManagerLearnMoreURL);
   html_source->AddString("manageAddressesUrl",
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index d1d9811..da2476b 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/browser/ui/webui/metrics_handler.h"
 #include "chrome/browser/ui/webui/settings/about_handler.h"
 #include "chrome/browser/ui/webui/settings/appearance_handler.h"
@@ -368,6 +369,10 @@
       "autofillHomeEnabled",
       base::FeatureList::IsEnabled(password_manager::features::kAutofillHome));
 
+  html_source->AddBoolean(
+      "navigateToGooglePasswordManager",
+      ShouldManagePasswordsinGooglePasswordManager(profile));
+
   html_source->AddBoolean("showImportPasswords",
                           base::FeatureList::IsEnabled(
                               password_manager::features::kPasswordImport));
diff --git a/chrome/browser/ui/webui/signin/signin_utils_desktop.cc b/chrome/browser/ui/webui/signin/signin_utils_desktop.cc
index ce84ff5..5c72f19 100644
--- a/chrome/browser/ui/webui/signin/signin_utils_desktop.cc
+++ b/chrome/browser/ui/webui/signin/signin_utils_desktop.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/chrome_signin_client.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/investigator_dependency_provider.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_util.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -21,6 +21,7 @@
 #include "components/signin/core/browser/identity_utils.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 
 bool CanOfferSignin(Profile* profile,
@@ -34,15 +35,15 @@
   if (!profile)
     return false;
 
-  SigninManager* manager = SigninManagerFactory::GetForProfile(profile);
-  if (manager && !manager->IsSigninAllowed())
+  if (!profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed))
     return false;
 
   if (!ChromeSigninClient::ProfileAllowsSigninCookies(profile))
     return false;
 
   if (!email.empty()) {
-    if (!manager)
+    auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
+    if (!identity_manager)
       return false;
 
     // Make sure this username is not prohibited by policy.
@@ -62,7 +63,7 @@
     // If the signin manager already has an authenticated name, then this is a
     // re-auth scenario.  Make sure the email just signed in corresponds to
     // the one sign in manager expects.
-    std::string current_email = manager->GetAuthenticatedAccountInfo().email;
+    std::string current_email = identity_manager->GetPrimaryAccountInfo().email;
     const bool same_email = gaia::AreEmailsSame(current_email, email);
     if (!current_email.empty() && !same_email) {
       UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
index b765da2..e4f43af 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
@@ -18,7 +18,7 @@
 BookmarkAppInstallManager::~BookmarkAppInstallManager() = default;
 
 bool BookmarkAppInstallManager::CanInstallWebApp(
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   return extensions::TabHelper::FromWebContents(web_contents)
       ->CanCreateBookmarkApp();
 }
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
index 3820cf8..36366e7 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
@@ -16,7 +16,7 @@
   ~BookmarkAppInstallManager() override;
 
   // InstallManager interface implementation.
-  bool CanInstallWebApp(const content::WebContents* web_contents) override;
+  bool CanInstallWebApp(content::WebContents* web_contents) override;
   void InstallWebApp(content::WebContents* web_contents,
                      bool force_shortcut_app,
                      OnceInstallCallback callback) override;
diff --git a/chrome/browser/web_applications/components/install_manager.h b/chrome/browser/web_applications/components/install_manager.h
index 41666a0..36a0672 100644
--- a/chrome/browser/web_applications/components/install_manager.h
+++ b/chrome/browser/web_applications/components/install_manager.h
@@ -22,7 +22,7 @@
       base::OnceCallback<void(const AppId& app_id, InstallResultCode code)>;
 
   // Returns true if a web app can be installed for a given |web_contents|.
-  virtual bool CanInstallWebApp(const content::WebContents* web_contents) = 0;
+  virtual bool CanInstallWebApp(content::WebContents* web_contents) = 0;
 
   // Starts a web app installation process for a given |web_contents|.
   // |force_shortcut_app| forces the creation of a shortcut app instead of a PWA
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index f463ef6..73996f2 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -31,7 +31,7 @@
 WebAppInstallManager::~WebAppInstallManager() = default;
 
 bool WebAppInstallManager::CanInstallWebApp(
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   return IsValidWebAppUrl(web_contents->GetURL());
 }
 
diff --git a/chrome/browser/web_applications/web_app_install_manager.h b/chrome/browser/web_applications/web_app_install_manager.h
index 130b7191..8137030 100644
--- a/chrome/browser/web_applications/web_app_install_manager.h
+++ b/chrome/browser/web_applications/web_app_install_manager.h
@@ -37,7 +37,7 @@
   ~WebAppInstallManager() override;
 
   // InstallManager:
-  bool CanInstallWebApp(const content::WebContents* web_contents) override;
+  bool CanInstallWebApp(content::WebContents* web_contents) override;
   void InstallWebApp(content::WebContents* contents,
                      bool force_shortcut_app,
                      OnceInstallCallback callback) override;
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index c61e2a9..9a493716 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -41,7 +41,7 @@
 
 // static
 WebAppProvider* WebAppProvider::GetForWebContents(
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   DCHECK(profile);
@@ -105,8 +105,7 @@
 }
 
 // static
-bool WebAppProvider::CanInstallWebApp(
-    const content::WebContents* web_contents) {
+bool WebAppProvider::CanInstallWebApp(content::WebContents* web_contents) {
   auto* provider = WebAppProvider::GetForWebContents(web_contents);
   if (!provider || !provider->install_manager_)
     return false;
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index e37b6cb..468c7b91 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -48,8 +48,7 @@
                        public content::NotificationObserver {
  public:
   static WebAppProvider* Get(Profile* profile);
-  static WebAppProvider* GetForWebContents(
-      const content::WebContents* web_contents);
+  static WebAppProvider* GetForWebContents(content::WebContents* web_contents);
 
   explicit WebAppProvider(Profile* profile);
 
@@ -62,7 +61,7 @@
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
   // Returns true if a bookmark can be installed for a given |web_contents|.
-  static bool CanInstallWebApp(const content::WebContents* web_contents);
+  static bool CanInstallWebApp(content::WebContents* web_contents);
 
   // Starts a bookmark installation process for a given |web_contents|.
   static void InstallWebApp(content::WebContents* web_contents,
diff --git a/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn b/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn
index a3dfc69..ad355e3 100644
--- a/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn
+++ b/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn
@@ -18,6 +18,7 @@
     "//chrome/chrome_cleaner/strings",
     "//chrome/chrome_cleaner/strings:string_test_helpers",
     "//chrome/chrome_cleaner/test:test_util",
+    "//components/chrome_cleaner/test:test_name_helper",
     "//mojo/core/embedder:embedder",
     "//testing/gtest",
   ]
diff --git a/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc b/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc
index a08c485..c67b81e 100644
--- a/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc
+++ b/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc
@@ -9,8 +9,8 @@
 #include "chrome/chrome_cleaner/ipc/ipc_test_util.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
-#include "chrome/chrome_cleaner/test/test_name_helper.h"
 #include "chrome/chrome_cleaner/test/test_util.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/chrome_cleaner/ipc/BUILD.gn b/chrome/chrome_cleaner/ipc/BUILD.gn
index da4d7cd7..937919b 100644
--- a/chrome/chrome_cleaner/ipc/BUILD.gn
+++ b/chrome/chrome_cleaner/ipc/BUILD.gn
@@ -93,6 +93,7 @@
     "//chrome/chrome_cleaner/os:common_os",
     "//chrome/chrome_cleaner/test:test_util",
     "//components/chrome_cleaner/public/interfaces:interfaces",
+    "//components/chrome_cleaner/test:test_name_helper",
     "//mojo/core/embedder",
     "//sandbox/win:sandbox",
     "//testing/gmock",
diff --git a/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc b/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc
index 23eaf89..71c59c4 100644
--- a/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc
+++ b/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc
@@ -17,9 +17,9 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/chrome_cleaner/ipc/ipc_test_util.h"
 #include "chrome/chrome_cleaner/logging/scoped_logging.h"
-#include "chrome/chrome_cleaner/test/test_name_helper.h"
 #include "chrome/chrome_cleaner/test/test_util.h"
 #include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/chrome_cleaner/logging/BUILD.gn b/chrome/chrome_cleaner/logging/BUILD.gn
index 4bb2c19..c832a5f3 100644
--- a/chrome/chrome_cleaner/logging/BUILD.gn
+++ b/chrome/chrome_cleaner/logging/BUILD.gn
@@ -264,6 +264,7 @@
     "//chrome/chrome_cleaner/test:test_util",
     "//chrome/chrome_cleaner/test/resources:test_resources",
     "//components/chrome_cleaner/public/constants:constants",
+    "//components/chrome_cleaner/test:test_name_helper",
     "//net/traffic_annotation:test_support",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc b/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc
index 3924fc0..eb94ffa0 100644
--- a/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc
+++ b/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc
@@ -37,10 +37,10 @@
 #include "chrome/chrome_cleaner/proto/shared_pup_enums.pb.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
 #include "chrome/chrome_cleaner/test/test_file_util.h"
-#include "chrome/chrome_cleaner/test/test_name_helper.h"
 #include "chrome/chrome_cleaner/test/test_settings_util.h"
 #include "chrome/chrome_cleaner/test/test_task_scheduler.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 
diff --git a/chrome/chrome_cleaner/logging/pending_logs_service.cc b/chrome/chrome_cleaner/logging/pending_logs_service.cc
index 7e5731a..6730fb3 100644
--- a/chrome/chrome_cleaner/logging/pending_logs_service.cc
+++ b/chrome/chrome_cleaner/logging/pending_logs_service.cc
@@ -39,7 +39,7 @@
     const ChromeCleanerReport& chrome_cleaner_report,
     base::FilePath* log_file,
     RegistryLogger* registry_logger) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(log_file);
   DCHECK(registry_logger);
   // This can happen when we fail while retrying. The logging service is not
@@ -123,7 +123,7 @@
 }
 
 PendingLogsService::PendingLogsService() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 }
 
 PendingLogsService::~PendingLogsService() = default;
diff --git a/chrome/chrome_cleaner/os/BUILD.gn b/chrome/chrome_cleaner/os/BUILD.gn
index 206ddef5..c9073ae 100644
--- a/chrome/chrome_cleaner/os/BUILD.gn
+++ b/chrome/chrome_cleaner/os/BUILD.gn
@@ -167,6 +167,7 @@
     "//chrome/chrome_cleaner/zip_archiver/broker:common",
     "//chrome/chrome_cleaner/zip_archiver/target:common",
     "//components/chrome_cleaner/public/constants:constants",
+    "//components/chrome_cleaner/test:test_name_helper",
     "//sandbox/win:sandbox",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/chrome/chrome_cleaner/os/file_remover_unittest.cc b/chrome/chrome_cleaner/os/file_remover_unittest.cc
index c1a9ad6..64abccd 100644
--- a/chrome/chrome_cleaner/os/file_remover_unittest.cc
+++ b/chrome/chrome_cleaner/os/file_remover_unittest.cc
@@ -35,12 +35,12 @@
 #include "chrome/chrome_cleaner/test/resources/grit/test_resources.h"
 #include "chrome/chrome_cleaner/test/test_file_util.h"
 #include "chrome/chrome_cleaner/test/test_layered_service_provider.h"
-#include "chrome/chrome_cleaner/test/test_name_helper.h"
 #include "chrome/chrome_cleaner/test/test_strings.h"
 #include "chrome/chrome_cleaner/test/test_util.h"
 #include "chrome/chrome_cleaner/zip_archiver/broker/sandbox_setup.h"
 #include "chrome/chrome_cleaner/zip_archiver/sandboxed_zip_archiver.h"
 #include "chrome/chrome_cleaner/zip_archiver/target/sandbox_setup.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/chrome_cleaner/scanner/BUILD.gn b/chrome/chrome_cleaner/scanner/BUILD.gn
index a994a5a..877b5e8 100644
--- a/chrome/chrome_cleaner/scanner/BUILD.gn
+++ b/chrome/chrome_cleaner/scanner/BUILD.gn
@@ -143,6 +143,7 @@
     "//chrome/chrome_cleaner/test:test_strings",
     "//chrome/chrome_cleaner/test:test_util",
     "//chrome/chrome_cleaner/test/resources:test_resources",
+    "//components/chrome_cleaner/test:test_name_helper",
     "//sandbox/win:sandbox",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc b/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc
index 21f7c7fa..4ab8a780 100644
--- a/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc
+++ b/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc
@@ -42,12 +42,12 @@
 #include "chrome/chrome_cleaner/proto/shared_pup_enums.pb.h"
 #include "chrome/chrome_cleaner/strings/string_util.h"
 #include "chrome/chrome_cleaner/test/test_file_util.h"
-#include "chrome/chrome_cleaner/test/test_name_helper.h"
 #include "chrome/chrome_cleaner/test/test_pup_data.h"
 #include "chrome/chrome_cleaner/test/test_registry_util.h"
 #include "chrome/chrome_cleaner/test/test_signature_matcher.h"
 #include "chrome/chrome_cleaner/test/test_strings.h"
 #include "chrome/chrome_cleaner/test/test_util.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/chrome_cleaner/settings/BUILD.gn b/chrome/chrome_cleaner/settings/BUILD.gn
index 549872d..a2d2f23 100644
--- a/chrome/chrome_cleaner/settings/BUILD.gn
+++ b/chrome/chrome_cleaner/settings/BUILD.gn
@@ -74,6 +74,7 @@
     "//chrome/chrome_cleaner/constants:common_strings",
     "//chrome/chrome_cleaner/test:test_util",
     "//components/chrome_cleaner/public/constants:constants",
+    "//components/chrome_cleaner/test:test_name_helper",
     "//testing/gtest",
   ]
 }
diff --git a/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc b/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc
index 973ce34a..9219991 100644
--- a/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc
+++ b/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc
@@ -9,8 +9,8 @@
 #include "base/command_line.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
-#include "chrome/chrome_cleaner/test/test_name_helper.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chrome_cleaner {
diff --git a/chrome/chrome_cleaner/test/BUILD.gn b/chrome/chrome_cleaner/test/BUILD.gn
index 098bad2..e28ebf92 100644
--- a/chrome/chrome_cleaner/test/BUILD.gn
+++ b/chrome/chrome_cleaner/test/BUILD.gn
@@ -142,7 +142,6 @@
     "test_file_util.h",
     "test_layered_service_provider.cc",
     "test_layered_service_provider.h",
-    "test_name_helper.h",
     "test_registry_util.cc",
     "test_registry_util.h",
     "test_settings_util.cc",
diff --git a/chrome/chrome_cleaner/ui/BUILD.gn b/chrome/chrome_cleaner/ui/BUILD.gn
index 45d0f6c3..39629ae 100644
--- a/chrome/chrome_cleaner/ui/BUILD.gn
+++ b/chrome/chrome_cleaner/ui/BUILD.gn
@@ -42,6 +42,7 @@
     "//chrome/chrome_cleaner/ipc:mock_chrome_prompt_ipc",
     "//chrome/chrome_cleaner/test:test_pup_data",
     "//chrome/chrome_cleaner/test:test_util",
+    "//components/chrome_cleaner/test:test_name_helper",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/chrome/chrome_cleaner/ui/silent_main_dialog_unittest.cc b/chrome/chrome_cleaner/ui/silent_main_dialog_unittest.cc
index 83854565..b727c4c 100644
--- a/chrome/chrome_cleaner/ui/silent_main_dialog_unittest.cc
+++ b/chrome/chrome_cleaner/ui/silent_main_dialog_unittest.cc
@@ -11,10 +11,10 @@
 
 #include "base/files/file_path.h"
 #include "chrome/chrome_cleaner/constants/uws_id.h"
-#include "chrome/chrome_cleaner/test/test_name_helper.h"
 #include "chrome/chrome_cleaner/test/test_pup_data.h"
 #include "chrome/chrome_cleaner/test/test_settings_util.h"
 #include "chrome/chrome_cleaner/ui/mock_main_dialog_delegate.h"
+#include "components/chrome_cleaner/test/test_name_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/common/apps/platform_apps/chrome_apps_api_permissions.cc b/chrome/common/apps/platform_apps/chrome_apps_api_permissions.cc
index 1a71362..d67da619 100644
--- a/chrome/common/apps/platform_apps/chrome_apps_api_permissions.cc
+++ b/chrome/common/apps/platform_apps/chrome_apps_api_permissions.cc
@@ -4,15 +4,17 @@
 
 #include "chrome/common/apps/platform_apps/chrome_apps_api_permissions.h"
 
+#include <memory>
+
 #include "chrome/common/apps/platform_apps/media_galleries_permission.h"
 
 namespace chrome_apps_api_permissions {
 namespace {
 
 template <typename T>
-extensions::APIPermission* CreateAPIPermission(
+std::unique_ptr<extensions::APIPermission> CreateAPIPermission(
     const extensions::APIPermissionInfo* permission) {
-  return new T(permission);
+  return std::make_unique<T>(permission);
 }
 
 // WARNING: If you are modifying a permission message in this list, be sure to
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 69238bf..6fa6139d 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -21,8 +21,9 @@
 namespace {
 
 template <typename T>
-APIPermission* CreateAPIPermission(const APIPermissionInfo* permission) {
-  return new T(permission);
+std::unique_ptr<APIPermission> CreateAPIPermission(
+    const APIPermissionInfo* permission) {
+  return std::make_unique<T>(permission);
 }
 
 // WARNING: If you are modifying a permission message in this list, be sure to
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index 0dab07f..9d0543d 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -345,8 +345,8 @@
 
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket);
-  std::unique_ptr<APIPermission> permission(
-      permission_info->CreateAPIPermission());
+  std::unique_ptr<APIPermission> permission =
+      permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -389,7 +389,7 @@
   apis2.insert(APIPermission::kProxy);
   apis2.insert(APIPermission::kClipboardWrite);
 
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -402,7 +402,7 @@
   expected_apis.insert(APIPermission::kProxy);
   expected_apis.insert(APIPermission::kClipboardWrite);
 
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -466,8 +466,8 @@
   // Intersection with an empty set.
   apis1.insert(APIPermission::kTab);
   apis1.insert(APIPermission::kBackground);
-  std::unique_ptr<APIPermission> permission(
-      permission_info->CreateAPIPermission());
+  std::unique_ptr<APIPermission> permission =
+      permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -502,7 +502,7 @@
   apis2.insert(APIPermission::kTab);
   apis2.insert(APIPermission::kProxy);
   apis2.insert(APIPermission::kClipboardWrite);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("udp-bind::8080");
@@ -513,7 +513,7 @@
   apis2.insert(std::move(permission));
 
   expected_apis.insert(APIPermission::kTab);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("udp-bind::8080");
@@ -574,8 +574,8 @@
   // Difference with an empty set.
   apis1.insert(APIPermission::kTab);
   apis1.insert(APIPermission::kBackground);
-  std::unique_ptr<APIPermission> permission(
-      permission_info->CreateAPIPermission());
+  std::unique_ptr<APIPermission> permission =
+      permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -599,7 +599,7 @@
   apis2.insert(APIPermission::kTab);
   apis2.insert(APIPermission::kProxy);
   apis2.insert(APIPermission::kClipboardWrite);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -609,7 +609,7 @@
   apis2.insert(std::move(permission));
 
   expected_apis.insert(APIPermission::kBackground);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("udp-bind::8080");
diff --git a/chrome/common/page_load_metrics/page_load_metrics.mojom b/chrome/common/page_load_metrics/page_load_metrics.mojom
index 6f8e7a3c..c54ca69 100644
--- a/chrome/common/page_load_metrics/page_load_metrics.mojom
+++ b/chrome/common/page_load_metrics/page_load_metrics.mojom
@@ -180,7 +180,7 @@
   int64 received_data_length = 0;
 
   // The length of the response body for the resource before removing any
-  // content encodings.
+  // content encodings. Only set for complete resources.
   int64 encoded_body_length = 0;
 
   // Whether this resource load has completed.
diff --git a/chrome/common/safe_browsing/binary_feature_extractor_fuzzer.cc b/chrome/common/safe_browsing/binary_feature_extractor_fuzzer.cc
new file mode 100644
index 0000000..6372463
--- /dev/null
+++ b/chrome/common/safe_browsing/binary_feature_extractor_fuzzer.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/safe_browsing/binary_feature_extractor.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "components/safe_browsing/proto/csd.pb.h"
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+  static safe_browsing::BinaryFeatureExtractor* extractor =
+      new safe_browsing::BinaryFeatureExtractor();
+
+  google::protobuf::RepeatedPtrField<std::string> signed_data;
+  safe_browsing::ClientDownloadRequest_ImageHeaders image_headers;
+  extractor->ExtractImageFeaturesFromData(
+      data, size, safe_browsing::BinaryFeatureExtractor::kDefaultOptions,
+      &image_headers, &signed_data);
+  return 0;
+}
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.cc b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
index b31015b..35d6cbc 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.cc
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
@@ -38,8 +38,6 @@
       data_reduction_proxy::EstimateCompressionRatioFromHeaders(&response_head);
   proxy_used_ = !response_head.proxy_server.is_direct();
   mime_type_ = response_head.mime_type;
-  total_received_bytes_ = 0;
-  last_update_bytes_ = 0;
   was_fetched_via_cache_ = response_head.was_fetched_via_cache;
   is_secure_scheme_ = response_url.SchemeIsCryptographic();
 }
@@ -82,7 +80,7 @@
 int PageResourceDataUse::CalculateNewlyReceivedBytes() {
   int newly_received_bytes = total_received_bytes_ - last_update_bytes_;
   last_update_bytes_ = total_received_bytes_;
-  DCHECK(newly_received_bytes >= 0);
+  DCHECK_GE(newly_received_bytes, 0);
   return newly_received_bytes;
 }
 
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.h b/chrome/renderer/page_load_metrics/page_resource_data_use.h
index 9723bd0..7431f65 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.h
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.h
@@ -63,8 +63,8 @@
   // used.
   double data_reduction_proxy_compression_ratio_estimate_;
 
-  uint64_t total_received_bytes_;
-  uint64_t last_update_bytes_;
+  uint64_t total_received_bytes_ = 0;
+  uint64_t last_update_bytes_ = 0;
   uint64_t encoded_body_length_ = 0;
 
   bool is_complete_;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index fbcc2c9..95eb77f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3299,6 +3299,7 @@
       "//chrome/browser/resource_coordinator/tab_ranker:tab_features_test_helper",
       "//chrome/services/app_service:unit_tests",
       "//chrome/services/app_service/public/cpp/:unit_tests",
+      "//components/chrome_cleaner/test:test_name_helper",
       "//components/signin/core/browser:signin_buildflags",
       "//services/metrics/public/cpp:ukm_builders",
       "//third_party/libaddressinput",
@@ -5789,12 +5790,12 @@
   }
 }
 
-# Note: this compiles and runs on Mac but may cause
-# system instability; if you try it out, close other
-# programs and then reboot afterwards. It should be
-# possible to make it work on Linux if you use the
-# --enable-speech-dispatcher flag.
 if (is_win) {
+  # Note: this compiles and runs on Mac but may cause
+  # system instability; if you try it out, close other
+  # programs and then reboot afterwards. It should be
+  # possible to make it work on Linux if you use the
+  # --enable-speech-dispatcher flag.
   fuzzer_test("tts_platform_fuzzer") {
     sources = [
       "../browser/speech/mock_tts_controller.cc",
@@ -5813,4 +5814,23 @@
       "//ui/base",
     ]
   }
+
+  fuzzer_test("safe_browsing_binary_feature_extractor_fuzzer") {
+    sources = [
+      "../common/safe_browsing/binary_feature_extractor.cc",
+      "../common/safe_browsing/binary_feature_extractor_fuzzer.cc",
+      "../common/safe_browsing/binary_feature_extractor_win.cc",
+      "../common/safe_browsing/pe_image_reader_win.cc",
+    ]
+    deps = [
+      "//base",
+      "//components/safe_browsing:csd_proto",
+      "//crypto",
+    ]
+    libs = [ "wintrust.lib" ]
+
+    # Directory contains useful and non-useful files. Don't worry about
+    # non-useful since they are eliminated during pruning.
+    seed_corpus = "data/safe_browsing/download_protection/"
+  }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
index 2c3ea67..4e4bed3 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
@@ -6,8 +6,6 @@
 
 import android.accounts.Account;
 import android.annotation.SuppressLint;
-import android.app.Instrumentation;
-import android.content.Context;
 import android.support.annotation.WorkerThread;
 
 import org.chromium.base.ContextUtils;
@@ -34,8 +32,6 @@
     private static final String DEFAULT_ACCOUNT = "test@gmail.com";
 
     @SuppressLint("StaticFieldLeak")
-    private static Context sContext;
-    @SuppressLint("StaticFieldLeak")
     private static FakeAccountManagerDelegate sAccountManager;
     @SuppressLint("StaticFieldLeak")
     private static List<AccountHolder> sAddedAccounts = new ArrayList<>();
@@ -46,15 +42,9 @@
      * This must be called before native is loaded.
      */
     @WorkerThread
-    public static void setUpAuthForTest(Instrumentation instrumentation) {
-        assert sContext == null;
-        sContext = instrumentation.getTargetContext();
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                ProcessInitializationHandler.getInstance().initializePreNative();
-            }
-        });
+    public static void setUpAuthForTest() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> ProcessInitializationHandler.getInstance().initializePreNative());
         sAccountManager = new FakeAccountManagerDelegate(
                 FakeAccountManagerDelegate.DISABLE_PROFILE_DATA_SOURCE);
         AccountManagerFacade.overrideAccountManagerFacadeForTests(sAccountManager);
@@ -71,14 +61,12 @@
             sAccountManager.removeAccountHolderBlocking(accountHolder);
         }
         sAddedAccounts.clear();
-        sContext = null;
     }
 
     /**
      * Returns the currently signed in account.
      */
     public static Account getCurrentAccount() {
-        assert sContext != null;
         return ChromeSigninController.get().getSignedInUser();
     }
 
@@ -115,21 +103,18 @@
     }
 
     private static void overrideAccountIdProvider() {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                AccountIdProvider.setInstanceForTest(new AccountIdProvider() {
-                    @Override
-                    public String getAccountId(String accountName) {
-                        return "gaia-id-" + accountName;
-                    }
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            AccountIdProvider.setInstanceForTest(new AccountIdProvider() {
+                @Override
+                public String getAccountId(String accountName) {
+                    return "gaia-id-" + accountName;
+                }
 
-                    @Override
-                    public boolean canBeUsed() {
-                        return true;
-                    }
-                });
-            }
+                @Override
+                public boolean canBeUsed() {
+                    return true;
+                }
+            });
         });
     }
 
@@ -142,7 +127,7 @@
         ChromeSigninController.get().setSignedInAccountName(null);
         ContextUtils.getAppSharedPreferences()
                 .edit()
-                .putStringSet(OAuth2TokenService.STORED_ACCOUNTS_KEY, new HashSet<String>())
+                .putStringSet(OAuth2TokenService.STORED_ACCOUNTS_KEY, new HashSet<>())
                 .apply();
     }
 
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index 8e344e50..84e9f39 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -756,8 +756,6 @@
     parser_map["networkConnectionEnabled"] =
         base::BindRepeating(&ParseBoolean, &network_emulation_enabled);
   }
-  // goog:testName is set by some tests to help debugging, and is ignored.
-  parser_map["goog:testName"] = base::BindRepeating(&IgnoreCapability);
 
   for (base::DictionaryValue::Iterator it(desired_caps); !it.IsAtEnd();
        it.Advance()) {
@@ -765,9 +763,10 @@
       continue;
     if (parser_map.find(it.key()) == parser_map.end()) {
       // The specified capability is unrecognized. W3C spec requires us to
-      // return an error. In legacy mode, for backward compatibility reasons,
+      // return an error if capability does not contain ":".
+      // In legacy mode, for backward compatibility reasons,
       // we ignore unrecognized capabilities.
-      if (w3c_compliant)
+      if (w3c_compliant && it.key().find(':') == std::string::npos)
         return Status(kInvalidArgument, "unrecognized capability: " + it.key());
       else
         continue;
diff --git a/chrome/test/chromedriver/chrome/adb_impl.cc b/chrome/test/chromedriver/chrome/adb_impl.cc
index acd002e0..77be2f60 100644
--- a/chrome/test/chromedriver/chrome/adb_impl.cc
+++ b/chrome/test/chromedriver/chrome/adb_impl.cc
@@ -82,7 +82,7 @@
 void ExecuteCommandOnIOThread(
     const std::string& command, scoped_refptr<ResponseBuffer> response_buffer,
     int port) {
-  CHECK(base::MessageLoopForIO::IsCurrent());
+  CHECK(base::MessageLoopCurrentForIO::IsSet());
   AdbClientSocket::AdbQuery(port, command,
       base::Bind(&ResponseBuffer::OnResponse, response_buffer));
 }
@@ -92,7 +92,7 @@
                         const std::string& content,
                         scoped_refptr<ResponseBuffer> response_buffer,
                         int port) {
-  CHECK(base::MessageLoopForIO::IsCurrent());
+  CHECK(base::MessageLoopCurrentForIO::IsSet());
   AdbClientSocket::SendFile(
       port, device_serial, filename, content,
       base::Bind(&ResponseBuffer::OnResponse, response_buffer));
diff --git a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
index 8bb7100..f4f7def 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
@@ -3074,8 +3074,11 @@
     // Focus twice, then make sure that the internal element is still focused.
     webview.focus();
     webview.focus();
-    embedder.test.assertTrue(document.activeElement = webview);
-    embedder.test.assertTrue(webview.shadowRoot.activeElement);
+    embedder.test.assertEq(document.activeElement, webview);
+    var webviewPrivates =
+        chrome.test.getModuleSystem(webview).privates(webview);
+    var shadowRoot = webviewPrivates.internal.shadowRoot;
+    embedder.test.assertTrue(shadowRoot.activeElement);
     embedder.test.succeed();
   });
 
diff --git a/chrome/test/data/webui/settings/people_page_test.js b/chrome/test/data/webui/settings/people_page_test.js
index 6a108653..0066bbe 100644
--- a/chrome/test/data/webui/settings/people_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_test.js
@@ -3,6 +3,20 @@
 // found in the LICENSE file.
 
 cr.define('settings_people_page', function() {
+  /** @implements {settings.PeopleBrowserProxy} */
+  class TestPeopleBrowserProxy extends TestBrowserProxy {
+    constructor() {
+      super([
+        'openURL',
+      ]);
+    }
+
+    /** @override */
+    openURL(url) {
+      this.methodCalled('openURL', url);
+    }
+  }
+
   suite('ProfileInfoTests', function() {
     /** @type {SettingsPeoplePageElement} */
     let peoplePage = null;
@@ -501,4 +515,66 @@
       assertEquals(settings.getCurrentRoute(), settings.routes.SYNC);
     });
   });
+
+  suite('PasswordsUITest', function() {
+    /** @type {SettingsPeoplePageElement} */
+    let peoplePage = null;
+    /** @type {settings.PeopleBrowserProxy} */
+    let browserProxy = null;
+
+    suiteSetup(function() {
+      // Forces navigation to Google Password Manager to be off by default.
+      loadTimeData.overrideValues({
+        navigateToGooglePasswordManager: false,
+      });
+    });
+
+    setup(function() {
+      browserProxy = new TestPeopleBrowserProxy();
+      settings.PeopleBrowserProxyImpl.instance_ = browserProxy;
+
+      PolymerTest.clearBody();
+      peoplePage = document.createElement('settings-people-page');
+
+      // Force enable Autofill Home.
+      // TODO(jdoerrie): https://crbug.com/854562.
+      // Remove when removing Autofill Home flag.
+      peoplePage.autofillHomeEnabled = true;
+      document.body.appendChild(peoplePage);
+
+      Polymer.dom.flush();
+    });
+
+    teardown(function() {
+      peoplePage.remove();
+    });
+
+    test('Google Password Manager Off', function() {
+      assertTrue(!!peoplePage.$$('#passwordManagerButton'));
+      peoplePage.$$('#passwordManagerButton').click();
+      Polymer.dom.flush();
+
+      assertEquals(
+          settings.getCurrentRoute(), settings.routes.MANAGE_PASSWORDS);
+    });
+
+    test('Google Password Manager On', function() {
+      // Hardcode this value so that the test is independent of the production
+      // implementation that might include additional query parameters.
+      const googlePasswordManagerUrl = 'https://passwords.google.com';
+
+      loadTimeData.overrideValues({
+        navigateToGooglePasswordManager: true,
+        googlePasswordManagerUrl: googlePasswordManagerUrl,
+      });
+
+      assertTrue(!!peoplePage.$$('#passwordManagerButton'));
+      peoplePage.$$('#passwordManagerButton').click();
+      Polymer.dom.flush();
+
+      return browserProxy.whenCalled('openURL').then(url => {
+        assertEquals(googlePasswordManagerUrl, url);
+      });
+    });
+  });
 });
diff --git a/chromecast/browser/extensions/api/automation_internal/automation_internal_api.cc b/chromecast/browser/extensions/api/automation_internal/automation_internal_api.cc
index 25ef92eb..89209dc4 100644
--- a/chromecast/browser/extensions/api/automation_internal/automation_internal_api.cc
+++ b/chromecast/browser/extensions/api/automation_internal/automation_internal_api.cc
@@ -136,7 +136,7 @@
 
 bool CanRequestAutomation(const Extension* extension,
                           const AutomationInfo* automation_info,
-                          const content::WebContents* contents) {
+                          content::WebContents* contents) {
   if (automation_info->desktop)
     return true;
 
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 593dec5d..7e3099f 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -296,6 +296,9 @@
 // Enables "hide Skip button" for ARC setup in the OOBE flow.
 const char kEnableArcOobeOptinNoSkip[] = "enable-arc-oobe-optin-no-skip";
 
+// Enables ARC VM.
+const char kEnableArcVm[] = "enable-arcvm";
+
 // Enables using a random url for captive portal detection.
 const char kEnableCaptivePortalRandomUrl[] = "enable-captive-portal-random-url";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 743c983a..de729f7 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -86,6 +86,7 @@
 CHROMEOS_EXPORT extern const char kDisableWakeOnWifi[];
 CHROMEOS_EXPORT extern const char kEnableArc[];
 CHROMEOS_EXPORT extern const char kEnableArcOobeOptinNoSkip[];
+CHROMEOS_EXPORT extern const char kEnableArcVm[];
 CHROMEOS_EXPORT extern const char kEnableCaptivePortalRandomUrl[];
 CHROMEOS_EXPORT extern const char kEnableCastReceiver[];
 CHROMEOS_EXPORT extern const char kEnableChromevoxDeveloperOption[];
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 04159182..aaff5903 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -71,6 +71,11 @@
           base::FeatureList::IsEnabled(kEnableArcFeature));
 }
 
+bool IsArcVmEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      chromeos::switches::kEnableArcVm);
+}
+
 bool ShouldArcAlwaysStart() {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
diff --git a/components/arc/arc_util.h b/components/arc/arc_util.h
index 7d6ec124..c066692 100644
--- a/components/arc/arc_util.h
+++ b/components/arc/arc_util.h
@@ -36,6 +36,9 @@
 // check, so it is ok to access them directly.
 bool IsArcAvailable();
 
+// Returns true if ARC VM is enabled.
+bool IsArcVmEnabled();
+
 // Returns true if ARC should always start within the primary user session
 // (opted in user or not), and other supported mode such as guest and Kiosk
 // mode.
diff --git a/components/arc/arc_util_unittest.cc b/components/arc/arc_util_unittest.cc
index bf81c4ef..fd75726 100644
--- a/components/arc/arc_util_unittest.cc
+++ b/components/arc/arc_util_unittest.cc
@@ -155,6 +155,14 @@
   EXPECT_TRUE(IsArcKioskAvailable());
 }
 
+TEST_F(ArcUtilTest, IsArcVmEnablede) {
+  EXPECT_FALSE(IsArcVmEnabled());
+
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv({"", "--enable-arcvm"});
+  EXPECT_TRUE(IsArcVmEnabled());
+}
+
 // TODO(hidehiko): Add test for IsArcKioskMode().
 // It depends on UserManager, but a utility to inject fake instance is
 // available only in chrome/. To use it in components/, refactoring is needed.
diff --git a/components/assist_ranker/base_predictor.cc b/components/assist_ranker/base_predictor.cc
index 9060cd8..07e585a 100644
--- a/components/assist_ranker/base_predictor.cc
+++ b/components/assist_ranker/base_predictor.cc
@@ -124,6 +124,10 @@
   return GURL(config_.field_trial_url_param->Get());
 }
 
+float BasePredictor::GetPredictThresholdReplacement() const {
+  return config_.field_trial_threshold_replacement_param;
+}
+
 RankerExample BasePredictor::PreprocessExample(const RankerExample& example) {
   if (ranker_model_->proto().has_metadata() &&
       ranker_model_->proto().metadata().input_features_names_are_hex_hashes()) {
diff --git a/components/assist_ranker/base_predictor.h b/components/assist_ranker/base_predictor.h
index 6904ad2..c89a4ae6 100644
--- a/components/assist_ranker/base_predictor.h
+++ b/components/assist_ranker/base_predictor.h
@@ -22,6 +22,10 @@
 
 namespace assist_ranker {
 
+// Value to use for when no prediction threshold replacement should be applied.
+// See |GetPredictThresholdReplacement| method.
+const float kNoPredictThresholdReplacement = 0.0;
+
 class Feature;
 class RankerExample;
 class RankerModel;
@@ -29,7 +33,7 @@
 // Predictors are objects that provide an interface for prediction, as well as
 // encapsulate the logic for loading the model and logging. Sub-classes of
 // BasePredictor implement an interface that depends on the nature of the
-// suported model. Subclasses of BasePredictor will also need to implement an
+// supported model. Subclasses of BasePredictor will also need to implement an
 // Initialize method that will be called once the model is available, and a
 // static validation function with the following signature:
 //
@@ -49,6 +53,9 @@
 
   // Returns the model URL.
   GURL GetModelUrl() const;
+  // Returns the threshold to use for prediction, or
+  // kNoPredictThresholdReplacement to leave it unchanged.
+  float GetPredictThresholdReplacement() const;
   // Returns the model name.
   std::string GetModelName() const;
 
diff --git a/components/assist_ranker/base_predictor_unittest.cc b/components/assist_ranker/base_predictor_unittest.cc
index 5b770768..6b330ba 100644
--- a/components/assist_ranker/base_predictor_unittest.cc
+++ b/components/assist_ranker/base_predictor_unittest.cc
@@ -54,37 +54,41 @@
 const base::FeatureParam<std::string> kTestRankerUrl{
     &kTestRankerQuery, kTestUrlParamName, kTestDefaultModelUrl};
 
-const PredictorConfig kTestPredictorConfig = PredictorConfig{
-    kTestModelName,     kTestLoggingName,  kTestUmaPrefixName, LOG_UKM,
-    &kFeatureWhitelist, &kTestRankerQuery, &kTestRankerUrl};
+const PredictorConfig kTestPredictorConfig =
+    PredictorConfig{kTestModelName,     kTestLoggingName,
+                    kTestUmaPrefixName, LOG_UKM,
+                    &kFeatureWhitelist, &kTestRankerQuery,
+                    &kTestRankerUrl,    kNoPredictThresholdReplacement};
 
 // Class that implements virtual functions of the base class.
 class FakePredictor : public BasePredictor {
  public:
-  static std::unique_ptr<FakePredictor> Create();
+  // Creates a |FakePredictor| using the default config (from this file).
+  static std::unique_ptr<FakePredictor> Create() {
+    return Create(kTestPredictorConfig);
+  }
+  // Creates a |FakePredictor| using the |PredictorConfig| passed in
+  // |predictor_config|.
+  static std::unique_ptr<FakePredictor> Create(
+      PredictorConfig predictor_config);
   ~FakePredictor() override{};
   // Validation will always succeed.
-  static RankerModelStatus ValidateModel(const RankerModel& model);
+  static RankerModelStatus ValidateModel(const RankerModel& model) {
+    return RankerModelStatus::OK;
+  }
 
  protected:
   // Not implementing any inference logic.
   bool Initialize() override { return true; };
 
  private:
-  FakePredictor(const PredictorConfig& config);
+  FakePredictor(const PredictorConfig& config) : BasePredictor(config) {}
   DISALLOW_COPY_AND_ASSIGN(FakePredictor);
 };
 
-FakePredictor::FakePredictor(const PredictorConfig& config)
-    : BasePredictor(config) {}
-
-RankerModelStatus FakePredictor::ValidateModel(const RankerModel& model) {
-  return RankerModelStatus::OK;
-}
-
-std::unique_ptr<FakePredictor> FakePredictor::Create() {
-  std::unique_ptr<FakePredictor> predictor(
-      new FakePredictor(kTestPredictorConfig));
+std::unique_ptr<FakePredictor> FakePredictor::Create(
+    PredictorConfig predictor_config) {
+  std::unique_ptr<FakePredictor> predictor(new FakePredictor(predictor_config));
   auto ranker_model = std::make_unique<RankerModel>();
   auto fake_model_loader = std::make_unique<FakeRankerModelLoader>(
       base::BindRepeating(&FakePredictor::ValidateModel),
@@ -184,4 +188,14 @@
       GetTestUkmRecorder()->EntryHasMetric(entries[0], kFeatureNotWhitelisted));
 }
 
+TEST_F(BasePredictorTest, GetPredictThresholdReplacement) {
+  float altered_threshold = 0.78f;  // Arbitrary value.
+  const PredictorConfig altered_threshold_config{
+      kTestModelName,  kTestLoggingName,   kTestUmaPrefixName,
+      LOG_UKM,         &kFeatureWhitelist, &kTestRankerQuery,
+      &kTestRankerUrl, altered_threshold};
+  auto predictor = FakePredictor::Create(altered_threshold_config);
+  EXPECT_EQ(altered_threshold, predictor->GetPredictThresholdReplacement());
+}
+
 }  // namespace assist_ranker
diff --git a/components/assist_ranker/binary_classifier_predictor.cc b/components/assist_ranker/binary_classifier_predictor.cc
index 651eaab8..402aa59 100644
--- a/components/assist_ranker/binary_classifier_predictor.cc
+++ b/components/assist_ranker/binary_classifier_predictor.cc
@@ -36,6 +36,8 @@
   const GURL& model_url = predictor->GetModelUrl();
   DVLOG(1) << "Creating predictor instance for " << predictor->GetModelName();
   DVLOG(1) << "Model URL: " << model_url;
+  DVLOG(1) << "Using predict threshold replacement: "
+           << predictor->GetPredictThresholdReplacement();
   auto model_loader = std::make_unique<RankerModelLoaderImpl>(
       base::BindRepeating(&BinaryClassifierPredictor::ValidateModel),
       base::BindRepeating(&BinaryClassifierPredictor::OnModelAvailable,
@@ -52,7 +54,13 @@
     return false;
   }
 
-  *prediction = inference_module_->Predict(PreprocessExample(example));
+  float predict_threshold_replacement = GetPredictThresholdReplacement();
+  if (predict_threshold_replacement != kNoPredictThresholdReplacement) {
+    *prediction = inference_module_->PredictScore(PreprocessExample(example)) >=
+                  predict_threshold_replacement;
+  } else {
+    *prediction = inference_module_->Predict(PreprocessExample(example));
+  }
   DVLOG(1) << "Predictor " << GetModelName() << " predicted: " << *prediction;
   return true;
 }
diff --git a/components/assist_ranker/binary_classifier_predictor_unittest.cc b/components/assist_ranker/binary_classifier_predictor_unittest.cc
index 672c80f..03dfa53 100644
--- a/components/assist_ranker/binary_classifier_predictor_unittest.cc
+++ b/components/assist_ranker/binary_classifier_predictor_unittest.cc
@@ -31,6 +31,7 @@
   GenericLogisticRegressionModel GetSimpleLogisticRegressionModel();
 
   PredictorConfig GetConfig();
+  PredictorConfig GetConfig(float predictor_threshold_replacement);
 
  protected:
   const std::string feature_ = "feature";
@@ -66,9 +67,14 @@
     &kTestRankerQuery, "url-param-name", "https://default.model.url"};
 
 PredictorConfig BinaryClassifierPredictorTest::GetConfig() {
+  return GetConfig(kNoPredictThresholdReplacement);
+}
+
+PredictorConfig BinaryClassifierPredictorTest::GetConfig(
+    float predictor_threshold_replacement) {
   PredictorConfig config("model_name", "logging_name", "uma_prefix", LOG_NONE,
                          GetEmptyWhitelist(), &kTestRankerQuery,
-                         &kTestRankerUrl);
+                         &kTestRankerUrl, predictor_threshold_replacement);
 
   return config;
 }
@@ -171,4 +177,30 @@
   EXPECT_LT(float_response, threshold_);
 }
 
+TEST_F(BinaryClassifierPredictorTest,
+       GenericLogisticRegressionPreprocessedModelReplacedThreshold) {
+  auto ranker_model = std::make_unique<RankerModel>();
+  auto& glr = *ranker_model->mutable_proto()->mutable_logistic_regression();
+  glr = GetSimpleLogisticRegressionModel();
+  glr.clear_weights();
+  glr.set_is_preprocessed_model(true);
+  (*glr.mutable_fullname_weights())[feature_] = weight_;
+
+  float high_threshold = 0.9;  // Some high threshold.
+  auto predictor =
+      InitPredictor(std::move(ranker_model), GetConfig(high_threshold));
+  EXPECT_TRUE(predictor->IsReady());
+
+  RankerExample ranker_example;
+  auto& features = *ranker_example.mutable_features();
+  features[feature_].set_bool_value(true);
+  bool bool_response;
+  EXPECT_TRUE(predictor->Predict(ranker_example, &bool_response));
+  EXPECT_FALSE(bool_response);
+  float float_response;
+  EXPECT_TRUE(predictor->PredictScore(ranker_example, &float_response));
+  EXPECT_GT(float_response, threshold_);
+  EXPECT_LT(float_response, high_threshold);
+}
+
 }  // namespace assist_ranker
diff --git a/components/assist_ranker/generic_logistic_regression_inference.h b/components/assist_ranker/generic_logistic_regression_inference.h
index ad4b1dbf..eb34804 100644
--- a/components/assist_ranker/generic_logistic_regression_inference.h
+++ b/components/assist_ranker/generic_logistic_regression_inference.h
@@ -23,7 +23,7 @@
   // Returns a boolean decision given a RankerExample. Uses the same logic as
   // PredictScore, and then applies the model decision threshold.
   bool Predict(const RankerExample& example);
-  // Returns a score between 0 and 1 give a RankerExample.
+  // Returns a score between 0 and 1 given a RankerExample.
   float PredictScore(const RankerExample& example);
 
  private:
diff --git a/components/assist_ranker/predictor_config.h b/components/assist_ranker/predictor_config.h
index 6a54588..944164b4 100644
--- a/components/assist_ranker/predictor_config.h
+++ b/components/assist_ranker/predictor_config.h
@@ -30,21 +30,25 @@
                   const LogType log_type,
                   const base::flat_set<std::string>* feature_whitelist,
                   const base::Feature* field_trial,
-                  const base::FeatureParam<std::string>* field_trial_url_param)
+                  const base::FeatureParam<std::string>* field_trial_url_param,
+                  float field_trial_threshold_replacement_param)
       : model_name(model_name),
         logging_name(logging_name),
         uma_prefix(uma_prefix),
         log_type(log_type),
         feature_whitelist(feature_whitelist),
         field_trial(field_trial),
-        field_trial_url_param(field_trial_url_param) {}
-  const char* model_name;
-  const char* logging_name;
-  const char* uma_prefix;
+        field_trial_url_param(field_trial_url_param),
+        field_trial_threshold_replacement_param(
+            field_trial_threshold_replacement_param) {}
+  const char* const model_name;
+  const char* const logging_name;
+  const char* const uma_prefix;
   const LogType log_type;
   const base::flat_set<std::string>* feature_whitelist;
   const base::Feature* field_trial;
   const base::FeatureParam<std::string>* field_trial_url_param;
+  const float field_trial_threshold_replacement_param;
 };
 
 }  // namespace assist_ranker
diff --git a/components/assist_ranker/predictor_config_definitions.cc b/components/assist_ranker/predictor_config_definitions.cc
index e967f68..7bcfc641 100644
--- a/components/assist_ranker/predictor_config_definitions.cc
+++ b/components/assist_ranker/predictor_config_definitions.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/assist_ranker/predictor_config_definitions.h"
+#include "components/assist_ranker/base_predictor.h"
 
 namespace assist_ranker {
 
@@ -28,6 +29,15 @@
   return kContextualSearchRankerUrl;
 }
 
+float GetContextualSearchRankerThresholdFeatureParam() {
+  static auto* kContextualSearchRankerThreshold =
+      new base::FeatureParam<double>(
+          &kContextualSearchRankerQuery,
+          "contextual-search-ranker-predict-threshold",
+          kNoPredictThresholdReplacement);
+  return static_cast<float>(kContextualSearchRankerThreshold->Get());
+}
+
 // NOTE: This list needs to be kept in sync with tools/metrics/ukm/ukm.xml!
 // Only features within this list will be logged to UKM.
 // TODO(chrome-ranker-team) Deprecate the whitelist once it is available through
@@ -77,7 +87,8 @@
       kContextualSearchModelName, kContextualSearchLoggingName,
       kContextualSearchUmaPrefixName, LOG_UKM,
       GetContextualSearchFeatureWhitelist(), &kContextualSearchRankerQuery,
-      GetContextualSearchRankerUrlFeatureParam()));
+      GetContextualSearchRankerUrlFeatureParam(),
+      GetContextualSearchRankerThresholdFeatureParam()));
   return kContextualSearchPredictorConfig;
 }
 #endif  // OS_ANDROID
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 0cb575f8..76068b4 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -673,6 +673,20 @@
   password_generation_agent_ = generation_agent;
 }
 
+PasswordAutofillAgent::FormStructureInfo::FormStructureInfo() = default;
+
+PasswordAutofillAgent::FormStructureInfo::FormStructureInfo(
+    const FormStructureInfo& other) = default;
+
+PasswordAutofillAgent::FormStructureInfo::FormStructureInfo(
+    FormStructureInfo&& other) = default;
+
+PasswordAutofillAgent::FormStructureInfo::~FormStructureInfo() = default;
+
+PasswordAutofillAgent::FormStructureInfo&
+PasswordAutofillAgent::FormStructureInfo::operator=(
+    PasswordAutofillAgent::FormStructureInfo&& other) = default;
+
 PasswordAutofillAgent::FocusStateNotifier::FocusStateNotifier(
     PasswordAutofillAgent* agent)
     : was_fillable_(false), was_password_field_(false), agent_(agent) {}
@@ -1183,9 +1197,11 @@
     if (logger)
       logger->LogPasswordForm(Logger::STRING_FORM_IS_PASSWORD, *password_form);
 
-    const FormData& form_data = password_form->form_data;
-    if (only_visible || WasFormStructureChanged(form_data)) {
-      forms_structure_cache_[form_data.unique_renderer_id] = form_data;
+    FormStructureInfo form_structure_info =
+        ExtractFormStructureInfo(password_form->form_data);
+    if (only_visible || WasFormStructureChanged(form_structure_info)) {
+      forms_structure_cache_[form_structure_info.unique_renderer_id] =
+          std::move(form_structure_info);
 
       password_forms.push_back(std::move(*password_form));
       continue;
@@ -2030,23 +2046,42 @@
   last_supplied_password_info_iter_ = web_input_to_password_info_.begin();
 }
 
-bool PasswordAutofillAgent::WasFormStructureChanged(
-    const FormData& form_data) const {
-  if (form_data.unique_renderer_id == FormData::kNotSetFormRendererId)
-    return true;
-
-  auto cached_form = forms_structure_cache_.find(form_data.unique_renderer_id);
-  if (cached_form == forms_structure_cache_.end())
-    return true;
-
-  const FormData& cached_form_data = cached_form->second;
-
-  if (form_data.fields.size() != cached_form_data.fields.size())
-    return true;
+PasswordAutofillAgent::FormStructureInfo
+PasswordAutofillAgent::ExtractFormStructureInfo(const FormData& form_data) {
+  FormStructureInfo result;
+  result.unique_renderer_id = form_data.unique_renderer_id;
+  result.fields.resize(form_data.fields.size());
 
   for (size_t i = 0; i < form_data.fields.size(); ++i) {
     const FormFieldData& form_field = form_data.fields[i];
-    const FormFieldData& cached_form_field = cached_form_data.fields[i];
+
+    FormFieldInfo& field_info = result.fields[i];
+    field_info.unique_renderer_id = form_field.unique_renderer_id;
+    field_info.form_control_type = form_field.form_control_type;
+    field_info.autocomplete_attribute = form_field.autocomplete_attribute;
+    field_info.is_focusable = form_field.is_focusable;
+  }
+
+  return result;
+}
+
+bool PasswordAutofillAgent::WasFormStructureChanged(
+    const FormStructureInfo& form_info) const {
+  if (form_info.unique_renderer_id == FormData::kNotSetFormRendererId)
+    return true;
+
+  auto cached_form = forms_structure_cache_.find(form_info.unique_renderer_id);
+  if (cached_form == forms_structure_cache_.end())
+    return true;
+
+  const FormStructureInfo& cached_form_info = cached_form->second;
+
+  if (form_info.fields.size() != cached_form_info.fields.size())
+    return true;
+
+  for (size_t i = 0; i < form_info.fields.size(); ++i) {
+    const FormFieldInfo& form_field = form_info.fields[i];
+    const FormFieldInfo& cached_form_field = cached_form_info.fields[i];
 
     if (form_field.unique_renderer_id != cached_form_field.unique_renderer_id)
       return true;
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 668a37f3..f4f300a9 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -221,6 +221,27 @@
   using PasswordToLoginMap =
       std::map<blink::WebInputElement, blink::WebInputElement>;
 
+  // Stores information about form field structure.
+  struct FormFieldInfo {
+    uint32_t unique_renderer_id = FormFieldData::kNotSetFormControlRendererId;
+    std::string form_control_type;
+    std::string autocomplete_attribute;
+    bool is_focusable = false;
+  };
+
+  // Stores information about form structure.
+  struct FormStructureInfo {
+    FormStructureInfo();
+    FormStructureInfo(const FormStructureInfo& other);
+    FormStructureInfo(FormStructureInfo&& other);
+    ~FormStructureInfo();
+
+    FormStructureInfo& operator=(FormStructureInfo&& other);
+
+    uint32_t unique_renderer_id = FormData::kNotSetFormRendererId;
+    std::vector<FormFieldInfo> fields;
+  };
+
   // This class ensures that the driver will only receive relevant signals by
   // caching the parameters of the last message sent to the driver.
   class FocusStateNotifier {
@@ -386,9 +407,11 @@
   // |form_data|.
   void MaybeStoreFallbackData(const PasswordFormFillData& form_data);
 
+  // Extracts information about form structure.
+  static FormStructureInfo ExtractFormStructureInfo(const FormData& form_data);
   // Checks whether the form structure (amount of elements, element types etc)
   // was changed.
-  bool WasFormStructureChanged(const FormData& form_data) const;
+  bool WasFormStructureChanged(const FormStructureInfo& form_data) const;
   // Tries to restore |control_elements| values with cached values.
   void TryFixAutofilledForm(
       std::vector<blink::WebFormControlElement>* control_elements) const;
@@ -469,7 +492,7 @@
   // Keeps forms structure (amount of elements, element types etc).
   // TODO(crbug/898109): It's too expensive to keep the whole FormData
   // structure. Replace FormData with a smaller structure.
-  std::map<unsigned /*unique renderer element id*/, FormData>
+  std::map<unsigned /*unique renderer element id*/, FormStructureInfo>
       forms_structure_cache_;
   DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgent);
 };
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index 54dc7c5..0d85d9a 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -85,6 +85,8 @@
 const char kDefaultAutofillServerURL[] =
     "https://clients1.google.com/tbproxy/af/";
 
+// Header for API key.
+constexpr char kGoogApiKey[] = "X-Goog-Api-Key";
 // Header to get base64 encoded serialized proto from API for safety.
 constexpr char kGoogEncodeResponseIfExecutable[] =
     "X-Goog-Encode-Response-If-Executable";
@@ -612,9 +614,6 @@
 
   // Add the query parameter to set the response format to a serialized proto.
   url = net::AppendQueryParameter(url, "alt", "proto");
-  // Add the API key query parameter.
-  if (!api_key_.empty())
-    url = net::AppendQueryParameter(url, "key", api_key_);
 
   // Determine the HTTP method that should be used.
   std::string method =
@@ -654,6 +653,9 @@
     // Encode response serialized proto in base64 for safety.
     resource_request->headers.SetHeader(kGoogEncodeResponseIfExecutable,
                                         "base64");
+    // Put API key in request's header if there is.
+    if (!api_key_.empty())
+      resource_request->headers.SetHeader(kGoogApiKey, api_key_);
   }
 
   auto simple_loader = network::SimpleURLLoader::Create(
diff --git a/components/autofill/core/browser/autofill_download_manager_unittest.cc b/components/autofill/core/browser/autofill_download_manager_unittest.cc
index 61ef55d..9f2ce54 100644
--- a/components/autofill/core/browser/autofill_download_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_download_manager_unittest.cc
@@ -495,8 +495,12 @@
   const std::string expected_url = {
       "https://clients1.google.com/v1/pages/"
       "Chc2LjEuMTcxNS4xNDQyL2VuIChHR0xMKRIWEgkNeuFP4BIAGgASCQ2cTkrQEgAaAA==?"
-      "alt=proto&key=dummykey"};
-  EXPECT_THAT(request->request.url, expected_url);
+      "alt=proto"};
+  EXPECT_EQ(request->request.url, expected_url);
+  std::string api_key_header_value;
+  EXPECT_TRUE(request->request.headers.GetHeader("X-Goog-Api-Key",
+                                                 &api_key_header_value));
+  EXPECT_EQ(api_key_header_value, "dummykey");
 
   test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
       request, "dummy response");
@@ -558,8 +562,12 @@
   // default one used by the download manager. Request upload data is in the
   // payload when uploading.
   const std::string expected_url =
-      "https://clients1.google.com/v1/forms:vote?alt=proto&key=dummykey";
-  EXPECT_THAT(request->request.url, expected_url);
+      "https://clients1.google.com/v1/forms:vote?alt=proto";
+  EXPECT_EQ(request->request.url, expected_url);
+  std::string api_key_header_value;
+  EXPECT_TRUE(request->request.headers.GetHeader("X-Goog-Api-Key",
+                                                 &api_key_header_value));
+  EXPECT_EQ(api_key_header_value, "dummykey");
 
   // Assert some of the fields within the uploaded proto to make sure it was
   // filled with something else than default data.
diff --git a/components/autofill_assistant/browser/actions/set_form_field_value_action.cc b/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
index a1b0089..9f3e961c 100644
--- a/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
+++ b/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
@@ -75,7 +75,7 @@
       // DEPRECATED: the field `keycode' used to contain a single character to
       // input as text. Since there is no easy way to convert keycodes to text,
       // this field is now deprecated and only works for US-ASCII characters.
-      // You should use the `key' field instead.
+      // You should use the `keyboard_input' field instead.
       if (key_field.keycode() < 128) {  // US-ASCII
         delegate->SendKeyboardInput(
             selector, std::string(1, char(key_field.keycode())),
diff --git a/components/autofill_assistant/browser/batch_element_checker.h b/components/autofill_assistant/browser/batch_element_checker.h
index 58afd03..aaa91ed 100644
--- a/components/autofill_assistant/browser/batch_element_checker.h
+++ b/components/autofill_assistant/browser/batch_element_checker.h
@@ -93,6 +93,10 @@
   // |try_done| or |all_done| can delete their calling BatchElementChecker to
   // interrupt any future checks. If |try_done| deletes BatchElementChecker,
   // |all_done| will never be called.
+  //
+  // Sync |try_done| and |all_done| can be called after the element checker has
+  // been deleted, they should always be called with callbacks that use a weak
+  // ptr instead of Unretained(this), even from objects that own the checker.
   void Run(const base::TimeDelta& duration,
            base::RepeatingCallback<void()> try_done,
            base::OnceCallback<void()> all_done);
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 3bc8872..cc0b185 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -115,11 +115,11 @@
       client_(std::move(client)),
       web_controller_(std::move(web_controller)),
       service_(std::move(service)),
-      script_tracker_(std::make_unique<ScriptTracker>(/* delegate= */ this,
-                                                      /* listener= */ this)),
       parameters_(std::move(parameters)),
       memory_(std::make_unique<ClientMemory>()),
       touchable_element_area_(web_controller_.get()),
+      script_tracker_(std::make_unique<ScriptTracker>(/* delegate= */ this,
+                                                      /* listener= */ this)),
       weak_ptr_factory_(this) {
   DCHECK(parameters_);
 
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 57379f9..9f7a982 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -132,12 +132,11 @@
   std::unique_ptr<Client> client_;
   std::unique_ptr<WebController> web_controller_;
   std::unique_ptr<Service> service_;
-  std::unique_ptr<ScriptTracker> script_tracker_;
   std::unique_ptr<std::map<std::string, std::string>> parameters_;
+  std::unique_ptr<ClientMemory> memory_;
 
   // Domain of the last URL the controller requested scripts from.
   std::string script_domain_;
-  std::unique_ptr<ClientMemory> memory_;
   bool allow_autostart_ = true;
 
   // Whether a task for periodic checks is scheduled.
@@ -162,6 +161,11 @@
   // Flag indicates whether it is ready to fetch and execute scripts.
   bool started_ = false;
 
+  // Tracks scripts and script execution. It's kept at the end, as it tend to
+  // depend on everything the controller support, through script and script
+  // actions.
+  std::unique_ptr<ScriptTracker> script_tracker_;
+
   base::WeakPtrFactory<Controller> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Controller);
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 580641c..ab9abd2c 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -100,7 +100,7 @@
   wait_with_interrupts_ = std::make_unique<WaitWithInterrupts>(
       this, max_wait_time, kVisibilityCheck, selector,
       base::BindOnce(&ScriptExecutor::OnWaitForElementVisible,
-                     base::Unretained(this), std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   wait_with_interrupts_->Run();
 }
 
@@ -479,7 +479,8 @@
       check_type_(check_type),
       selector_(selector),
       callback_(std::move(callback)),
-      element_found_(false) {}
+      element_found_(false),
+      weak_ptr_factory_(this) {}
 
 ScriptExecutor::WaitWithInterrupts::~WaitWithInterrupts() = default;
 
@@ -500,17 +501,18 @@
         batch_element_checker_.get(), main_script_->delegate_->GetParameters(),
         *main_script_->scripts_state_,
         base::BindOnce(&WaitWithInterrupts::OnPreconditionCheckDone,
-                       base::Unretained(this), base::Unretained(interrupt)));
+                       weak_ptr_factory_.GetWeakPtr(),
+                       base::Unretained(interrupt)));
   }
+  // The base::Unretained(this) above are safe, since the pointers belong to the
+  // main script, which own this instance.
 
   batch_element_checker_->Run(
       max_wait_time_,
       base::BindRepeating(&WaitWithInterrupts::OnTryDone,
-                          base::Unretained(this)),
-      base::BindOnce(&WaitWithInterrupts::OnAllDone, base::Unretained(this)));
-
-  // base::Unretained(this) above is safe because batch_element_checker_ belongs
-  // to this.
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&WaitWithInterrupts::OnAllDone,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ScriptExecutor::WaitWithInterrupts::OnServerPayloadChanged(
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index d36cda8..ed79fc2 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -230,6 +230,8 @@
     // The status message that was displayed when the interrupt started.
     std::string pre_interrupt_status_;
 
+    base::WeakPtrFactory<WaitWithInterrupts> weak_ptr_factory_;
+
     DISALLOW_COPY_AND_ASSIGN(WaitWithInterrupts);
   };
   friend class WaitWithInterrupts;
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 2c31f30..2d1656b 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -579,10 +579,9 @@
       string text = 1;
       // DEPRECATED: A single US-ASCII character (e.g., 13 for carriage return).
       int32 keycode = 2;
-      // One or multiple UTF-8 characters to input in the form element. In
-      // contrast to |text|, |keyboard_input| is evaluated as if the user had
-      // tapped the keyboard. This can also be used for control sequences such
-      // as "\r".
+      // Text as generated by processing a virtual key code with a keyboard
+      // layout. This can also be used for keyboard control sequences such
+      // as "\r" or "\t".
       string keyboard_input = 3;
     }
   }
diff --git a/components/chrome_cleaner/test/BUILD.gn b/components/chrome_cleaner/test/BUILD.gn
new file mode 100644
index 0000000..593b2bb
--- /dev/null
+++ b/components/chrome_cleaner/test/BUILD.gn
@@ -0,0 +1,16 @@
+# 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.
+
+source_set("test_name_helper") {
+  testonly = true
+
+  sources = [
+    "test_name_helper.h",
+  ]
+
+  public_deps = [
+    "//base:base",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/chrome_cleaner/test/DEPS b/components/chrome_cleaner/test/DEPS
new file mode 100644
index 0000000..a372d9b3
--- /dev/null
+++ b/components/chrome_cleaner/test/DEPS
@@ -0,0 +1,5 @@
+# Allow the typemaps to access their dependencies.
+include_rules = [
+  '+base/strings/string_util.h',
+  '+testing/gtest/include/gtest/gtest.h',
+]
diff --git a/chrome/chrome_cleaner/test/test_name_helper.h b/components/chrome_cleaner/test/test_name_helper.h
similarity index 89%
rename from chrome/chrome_cleaner/test/test_name_helper.h
rename to components/chrome_cleaner/test/test_name_helper.h
index a831d448..080e7c8 100644
--- a/chrome/chrome_cleaner/test/test_name_helper.h
+++ b/components/chrome_cleaner/test/test_name_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_CHROME_CLEANER_TEST_TEST_NAME_HELPER_H_
-#define CHROME_CHROME_CLEANER_TEST_TEST_NAME_HELPER_H_
+#ifndef COMPONENTS_CHROME_CLEANER_TEST_TEST_NAME_HELPER_H_
+#define COMPONENTS_CHROME_CLEANER_TEST_TEST_NAME_HELPER_H_
 
 #include <string>
 
@@ -45,4 +45,4 @@
 
 }  // namespace chrome_cleaner
 
-#endif  // CHROME_CHROME_CLEANER_TEST_TEST_NAME_HELPER_H_
+#endif  // COMPONENTS_CHROME_CLEANER_TEST_TEST_NAME_HELPER_H_
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
index 09c3091..d62d00b 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
@@ -10,6 +10,8 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h"
+#include "components/data_reduction_proxy/core/common/uma_util.h"
+#include "net/base/load_flags.h"
 
 namespace net {
 class HttpRequestHeaders;
@@ -34,6 +36,8 @@
   url_chain_.clear();
   url_chain_.push_back(request->url);
   request_method_ = request->method;
+  is_main_frame_ = request->resource_type == content::RESOURCE_TYPE_MAIN_FRAME;
+  final_load_flags_ = request->load_flags;
 
   MaybeSetAcceptTransformHeader(
       request->url, static_cast<content::ResourceType>(request->resource_type),
@@ -115,6 +119,19 @@
   }
 }
 
+void DataReductionProxyURLLoaderThrottle::WillProcessResponse(
+    const GURL& response_url,
+    network::ResourceResponseHead* response_head,
+    bool* defer) {
+  base::Optional<DataReductionProxyTypeInfo> proxy_info =
+      manager_->FindConfiguredDataReductionProxy(response_head->proxy_server);
+  if (!proxy_info || (final_load_flags_ & net::LOAD_BYPASS_PROXY) != 0)
+    return;
+
+  LogSuccessfulProxyUMAs(proxy_info.value(), response_head->proxy_server,
+                         is_main_frame_);
+}
+
 void DataReductionProxyURLLoaderThrottle::MarkProxiesAsBad(
     const std::vector<net::ProxyServer>& bad_proxies,
     base::TimeDelta bypass_duration) {
@@ -156,6 +173,7 @@
 
   pending_restart_ = false;
   pending_restart_load_flags_ = 0;
+  final_load_flags_ |= load_flags;
 
   delegate_->RestartWithFlags(load_flags);
 }
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
index d9f9576..4b6032ed 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
@@ -42,6 +42,9 @@
       const GURL& response_url,
       const network::ResourceResponseHead& response_head,
       bool* defer) override;
+  void WillProcessResponse(const GURL& response_url,
+                           network::ResourceResponseHead* response_head,
+                           bool* defer) override;
 
  private:
   // Marks |bad_proxies| to be bypassed for |bypass_duration|. Once that action
@@ -69,6 +72,12 @@
 
   // Set to true while waiting for OnMarkProxiesAsBadComplete to run.
   bool waiting_for_mark_proxies_ = false;
+
+  // Whether this throttle is intercepting a main frame request.
+  bool is_main_frame_ = false;
+
+  // The final load flags used to complete the request.
+  int final_load_flags_ = 0;
 };
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
index ad596bd..e15155f1 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.cc
@@ -12,6 +12,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
+#include "components/data_reduction_proxy/core/common/uma_util.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/base/proxy_server.h"
@@ -138,23 +139,9 @@
   successful_requests_through_proxy_count_++;
   NotifyUnavailabilityIfChanged();
 
-  // Report the success counts.
-  UMA_HISTOGRAM_COUNTS_100(
-      "DataReductionProxy.SuccessfulRequestCompletionCounts",
-      proxy_info->proxy_index);
-
-  // It is possible that the scheme of request->proxy_server() is different
-  // from the scheme of proxy_info.proxy_servers.front(). The former may be set
-  // to QUIC by the network stack, while the latter may be set to HTTPS.
-  UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.ProxySchemeUsed",
-                            util::ConvertNetProxySchemeToProxyScheme(
-                                request->proxy_server().scheme()),
-                            PROXY_SCHEME_MAX);
-  if (request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) {
-    UMA_HISTOGRAM_COUNTS_100(
-        "DataReductionProxy.SuccessfulRequestCompletionCounts.MainFrame",
-        proxy_info->proxy_index);
-  }
+  LogSuccessfulProxyUMAs(
+      proxy_info.value(), request->proxy_server(),
+      request->load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED);
 }
 
 void DataReductionProxyBypassStats::SetBypassType(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
index 9b44186..acfc90d6 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
@@ -271,23 +271,6 @@
          EstimateOriginalBodySize(request, lofi_decider);
 }
 
-ProxyScheme ConvertNetProxySchemeToProxyScheme(
-    net::ProxyServer::Scheme scheme) {
-  switch (scheme) {
-    case net::ProxyServer::SCHEME_HTTP:
-      return PROXY_SCHEME_HTTP;
-    case net::ProxyServer::SCHEME_HTTPS:
-      return PROXY_SCHEME_HTTPS;
-    case net::ProxyServer::SCHEME_QUIC:
-      return PROXY_SCHEME_QUIC;
-    case net::ProxyServer::SCHEME_DIRECT:
-      return PROXY_SCHEME_DIRECT;
-    default:
-      NOTREACHED() << scheme;
-      return PROXY_SCHEME_UNKNOWN;
-  }
-}
-
 const char* GetSiteBreakdownOtherHostName() {
   return kOtherHostName;
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
index c4addb5..130cce1 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
@@ -48,16 +48,6 @@
   CHROME_QNX,
 };
 
-// Scheme of the proxy used.
-enum ProxyScheme {
-  PROXY_SCHEME_UNKNOWN = 0,
-  PROXY_SCHEME_HTTP,
-  PROXY_SCHEME_HTTPS,
-  PROXY_SCHEME_QUIC,
-  PROXY_SCHEME_DIRECT,
-  PROXY_SCHEME_MAX
-};
-
 namespace util {
 
 // Returns the version of Chromium that is being used, e.g. "1.2.3.4".
@@ -119,9 +109,6 @@
 int64_t EstimateOriginalReceivedBytes(const net::URLRequest& request,
                                       const LoFiDecider* lofi_decider);
 
-// Converts net::ProxyServer::Scheme to type ProxyScheme.
-ProxyScheme ConvertNetProxySchemeToProxyScheme(net::ProxyServer::Scheme scheme);
-
 // Returns the hostname used for the other bucket to record datause not scoped
 // to a page load such as chrome-services traffic, service worker, Downloads.
 const char* GetSiteBreakdownOtherHostName();
diff --git a/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc b/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
index 7c9612ae..00b58f5 100644
--- a/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
+++ b/components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc
@@ -14,6 +14,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/core/common/uma_util.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "content/public/browser/network_service_instance.h"
 #include "net/base/load_flags.h"
@@ -246,7 +247,7 @@
                                        nullptr /* has_intermediary */));
     UMA_HISTOGRAM_ENUMERATION(
         "DataReductionProxy.WarmupURL.ProxySchemeUsed",
-        util::ConvertNetProxySchemeToProxyScheme(proxy_server_.scheme()),
+        ConvertNetProxySchemeToProxyScheme(proxy_server_.scheme()),
         PROXY_SCHEME_MAX);
   }
 
diff --git a/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc b/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
index c66e433..9ad4bc7 100644
--- a/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/warmup_url_fetcher_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
+#include "components/data_reduction_proxy/core/common/uma_util.h"
 #include "net/base/proxy_server.h"
 #include "net/http/http_status_code.h"
 #include "net/socket/socket_test_util.h"
@@ -214,8 +215,7 @@
       "DataReductionProxy.WarmupURL.HasViaHeader", 0, 1);
   histogram_tester.ExpectUniqueSample(
       "DataReductionProxy.WarmupURL.ProxySchemeUsed",
-      util::ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT),
-      1);
+      ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT), 1);
 
   EXPECT_EQ(1u, warmup_url_fetcher.callback_received_count());
   EXPECT_EQ(net::ProxyServer::SCHEME_DIRECT,
@@ -268,8 +268,7 @@
       "DataReductionProxy.WarmupURL.HasViaHeader", 1, 1);
   histogram_tester.ExpectUniqueSample(
       "DataReductionProxy.WarmupURL.ProxySchemeUsed",
-      util::ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT),
-      1);
+      ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT), 1);
 
   EXPECT_EQ(1u, warmup_url_fetcher.callback_received_count());
   EXPECT_EQ(net::ProxyServer::SCHEME_DIRECT,
@@ -321,8 +320,7 @@
       "DataReductionProxy.WarmupURL.HasViaHeader", 1, 1);
   histogram_tester.ExpectUniqueSample(
       "DataReductionProxy.WarmupURL.ProxySchemeUsed",
-      util::ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT),
-      1);
+      ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT), 1);
 
   // The callback should be run.
   EXPECT_EQ(1u, warmup_url_fetcher.callback_received_count());
@@ -458,8 +456,7 @@
       "DataReductionProxy.WarmupURL.HasViaHeader", 1, 1);
   histogram_tester.ExpectUniqueSample(
       "DataReductionProxy.WarmupURL.ProxySchemeUsed",
-      util::ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT),
-      1);
+      ConvertNetProxySchemeToProxyScheme(net::ProxyServer::SCHEME_DIRECT), 1);
 
   EXPECT_EQ(1u, warmup_url_fetcher.callback_received_count());
   EXPECT_EQ(net::ProxyServer::SCHEME_DIRECT,
diff --git a/components/data_reduction_proxy/core/common/BUILD.gn b/components/data_reduction_proxy/core/common/BUILD.gn
index 637d56a..6e5f685 100644
--- a/components/data_reduction_proxy/core/common/BUILD.gn
+++ b/components/data_reduction_proxy/core/common/BUILD.gn
@@ -35,6 +35,8 @@
       "lofi_decider.h",
       "lofi_ui_service.h",
       "resource_type_provider.h",
+      "uma_util.cc",
+      "uma_util.h",
     ]
 
     public_deps = [
diff --git a/components/data_reduction_proxy/core/common/uma_util.cc b/components/data_reduction_proxy/core/common/uma_util.cc
new file mode 100644
index 0000000..8911d39
--- /dev/null
+++ b/components/data_reduction_proxy/core/common/uma_util.cc
@@ -0,0 +1,51 @@
+// 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/data_reduction_proxy/core/common/uma_util.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
+
+namespace data_reduction_proxy {
+
+ProxyScheme ConvertNetProxySchemeToProxyScheme(
+    net::ProxyServer::Scheme scheme) {
+  switch (scheme) {
+    case net::ProxyServer::SCHEME_HTTP:
+      return PROXY_SCHEME_HTTP;
+    case net::ProxyServer::SCHEME_HTTPS:
+      return PROXY_SCHEME_HTTPS;
+    case net::ProxyServer::SCHEME_QUIC:
+      return PROXY_SCHEME_QUIC;
+    case net::ProxyServer::SCHEME_DIRECT:
+      return PROXY_SCHEME_DIRECT;
+    default:
+      NOTREACHED() << scheme;
+      return PROXY_SCHEME_UNKNOWN;
+  }
+}
+
+void LogSuccessfulProxyUMAs(const DataReductionProxyTypeInfo& proxy_info,
+                            const net::ProxyServer& proxy_server,
+                            bool is_main_frame) {
+  // Report the success counts.
+  UMA_HISTOGRAM_COUNTS_100(
+      "DataReductionProxy.SuccessfulRequestCompletionCounts",
+      proxy_info.proxy_index);
+
+  // It is possible that the scheme of request->proxy_server() is different
+  // from the scheme of proxy_info.proxy_servers.front(). The former may be set
+  // to QUIC by the network stack, while the latter may be set to HTTPS.
+  UMA_HISTOGRAM_ENUMERATION(
+      "DataReductionProxy.ProxySchemeUsed",
+      ConvertNetProxySchemeToProxyScheme(proxy_server.scheme()),
+      PROXY_SCHEME_MAX);
+  if (is_main_frame) {
+    UMA_HISTOGRAM_COUNTS_100(
+        "DataReductionProxy.SuccessfulRequestCompletionCounts.MainFrame",
+        proxy_info.proxy_index);
+  }
+}
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/uma_util.h b/components/data_reduction_proxy/core/common/uma_util.h
new file mode 100644
index 0000000..879d4ad
--- /dev/null
+++ b/components/data_reduction_proxy/core/common/uma_util.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_UMA_UTIL_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_UMA_UTIL_H_
+
+#include "net/base/proxy_server.h"
+
+namespace data_reduction_proxy {
+struct DataReductionProxyTypeInfo;
+
+// Scheme of the proxy used.
+enum ProxyScheme {
+  PROXY_SCHEME_UNKNOWN = 0,
+  PROXY_SCHEME_HTTP,
+  PROXY_SCHEME_HTTPS,
+  PROXY_SCHEME_QUIC,
+  PROXY_SCHEME_DIRECT,
+  PROXY_SCHEME_MAX
+};
+
+// Converts net::ProxyServer::Scheme to type ProxyScheme.
+ProxyScheme ConvertNetProxySchemeToProxyScheme(net::ProxyServer::Scheme scheme);
+
+// Logs UMAs for a successful request through a data reduction proxy.
+void LogSuccessfulProxyUMAs(const DataReductionProxyTypeInfo& proxy_info,
+                            const net::ProxyServer& proxy_server,
+                            bool is_main_frame);
+
+}  // namespace data_reduction_proxy
+
+#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_UMA_UTIL_H_
diff --git a/components/dom_distiller/core/distilled_content_store_unittest.cc b/components/dom_distiller/core/distilled_content_store_unittest.cc
index 96583b4..f4713932 100644
--- a/components/dom_distiller/core/distilled_content_store_unittest.cc
+++ b/components/dom_distiller/core/distilled_content_store_unittest.cc
@@ -7,8 +7,8 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -78,7 +78,7 @@
 
 // Tests whether saving and then loading a single article works as expected.
 TEST_F(InMemoryContentStoreTest, SaveAndLoadSingleArticle) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment task_environment;
   const ArticleEntry entry = CreateEntry("test-id", "url1", "url2", "url3");
   const DistilledArticleProto stored_proto =
       CreateDistilledArticleForEntry(entry);
@@ -102,7 +102,7 @@
 // Tests that loading articles which have never been stored, yields a callback
 // where success is false.
 TEST_F(InMemoryContentStoreTest, LoadNonExistentArticle) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment task_environment;
   const ArticleEntry entry = CreateEntry("bogus-id", "url1", "url2", "url3");
   store_->LoadContent(entry,
                       base::Bind(&InMemoryContentStoreTest::OnLoadCallback,
@@ -115,7 +115,7 @@
 // of save and store does not matter when the total number of articles does not
 // exceed |kDefaultMaxNumCachedEntries|.
 TEST_F(InMemoryContentStoreTest, SaveAndLoadMultipleArticles) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment task_environment;
   // Store first article.
   const ArticleEntry first_entry = CreateEntry("first", "url1", "url2", "url3");
   const DistilledArticleProto first_stored_proto =
@@ -165,7 +165,7 @@
 // Verifies that the content store does not store unlimited number of articles,
 // but expires the oldest ones when the limit for number of articles is reached.
 TEST_F(InMemoryContentStoreTest, SaveAndLoadMoreThanMaxArticles) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment task_environment;
 
   // Create a new store with only |kMaxNumArticles| articles as the limit.
   const int kMaxNumArticles = 3;
@@ -246,7 +246,7 @@
 
 // Tests whether saving and then loading a single article works as expected.
 TEST_F(InMemoryContentStoreTest, LookupArticleByURL) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment task_environment;
   const ArticleEntry entry = CreateEntry("test-id", "url1", "url2", "url3");
   const DistilledArticleProto stored_proto =
       CreateDistilledArticleForEntry(entry);
@@ -283,7 +283,7 @@
 // Verifies that the content store does not store unlimited number of articles,
 // but expires the oldest ones when the limit for number of articles is reached.
 TEST_F(InMemoryContentStoreTest, LoadArticleByURLAfterExpungedFromCache) {
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment task_environment;
 
   // Create a new store with only |kMaxNumArticles| articles as the limit.
   const int kMaxNumArticles = 1;
diff --git a/components/dom_distiller/core/distiller_unittest.cc b/components/dom_distiller/core/distiller_unittest.cc
index de51505..1de5eb92 100644
--- a/components/dom_distiller/core/distiller_unittest.cc
+++ b/components/dom_distiller/core/distiller_unittest.cc
@@ -17,11 +17,11 @@
 #include "base/bind_helpers.h"
 #include "base/location.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "components/dom_distiller/core/article_distillation_update.h"
@@ -36,11 +36,11 @@
 #include "third_party/dom_distiller_js/dom_distiller.pb.h"
 #include "third_party/dom_distiller_js/dom_distiller_json_converter.h"
 
-using std::vector;
 using std::string;
+using std::vector;
+using ::testing::_;
 using ::testing::Invoke;
 using ::testing::Return;
-using ::testing::_;
 
 using dom_distiller::proto::DomDistillerOptions;
 using dom_distiller::proto::DomDistillerResult;
@@ -54,9 +54,8 @@
 const size_t kTotalGoodImages = 2;
 const size_t kTotalImages = 3;
 // Good images need to be in the front.
-const char* kImageURLs[kTotalImages] = {"http://a.com/img1.jpg",
-                                        "http://a.com/img2.jpg",
-                                        "./bad_url_should_fail"};
+const char* kImageURLs[kTotalImages] = {
+    "http://a.com/img1.jpg", "http://a.com/img2.jpg", "./bad_url_should_fail"};
 const char* kImageData[kTotalImages] = {"abcde", "12345", "VWXYZ"};
 const char kDebugLog[] = "Debug Log";
 
@@ -103,7 +102,7 @@
   ~MultipageDistillerData() {}
   vector<string> page_urls;
   vector<string> content;
-  vector<vector<int> > image_ids;
+  vector<vector<int>> image_ids;
   // The Javascript values returned by mock distiller.
   std::vector<std::unique_ptr<base::Value>> distilled_values;
 
@@ -153,8 +152,9 @@
   }
 }
 
-string GenerateNextPageUrl(const std::string& url_prefix, size_t page_num,
-    size_t pages_size) {
+string GenerateNextPageUrl(const std::string& url_prefix,
+                           size_t page_num,
+                           size_t pages_size) {
   return page_num + 1 < pages_size
              ? url_prefix + base::NumberToString(page_num + 1)
              : "";
@@ -176,8 +176,7 @@
                               base::NumberToString(page_num));
     string next_page_url =
         GenerateNextPageUrl(url_prefix, page_num, pages_size);
-    string prev_page_url =
-        GeneratePrevPageUrl(url_prefix, page_num);
+    string prev_page_url = GeneratePrevPageUrl(url_prefix, page_num);
     std::unique_ptr<base::Value> distilled_value =
         CreateDistilledValueReturnedFromJS(kTitle, result->content[page_num],
                                            image_ids[page_num], next_page_url,
@@ -309,6 +308,8 @@
   }
 
  protected:
+  base::test::ScopedTaskEnvironment task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
   std::unique_ptr<DistillerImpl> distiller_;
   std::unique_ptr<DistilledArticleProto> article_proto_;
   std::vector<ArticleDistillationUpdate> in_sequence_updates_;
@@ -359,7 +360,6 @@
 }
 
 TEST_F(DistillerTest, DistillPage) {
-  base::MessageLoopForUI loop;
   std::unique_ptr<base::Value> result =
       CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), "");
   distiller_.reset(
@@ -374,7 +374,6 @@
 }
 
 TEST_F(DistillerTest, DistillPageWithDebugInfo) {
-  base::MessageLoopForUI loop;
   DomDistillerResult dd_result;
   dd_result.mutable_debug_info()->set_log(kDebugLog);
   std::unique_ptr<base::Value> result =
@@ -393,17 +392,16 @@
 }
 
 TEST_F(DistillerTest, DistillPageWithTimingInfo) {
-  base::MessageLoopForUI loop;
   DomDistillerResult dd_result;
   dd_result.mutable_timing_info()->set_total_time(1.0);
   dd_result.mutable_timing_info()->set_markup_parsing_time(2.0);
   dd_result.mutable_timing_info()->set_document_construction_time(3.0);
   dd_result.mutable_timing_info()->set_article_processing_time(4.0);
   dd_result.mutable_timing_info()->set_formatting_time(5.0);
-  SetTimingEntry(
-      dd_result.mutable_timing_info()->add_other_times(), "time0", 6.0);
-  SetTimingEntry(
-      dd_result.mutable_timing_info()->add_other_times(), "time1", 7.0);
+  SetTimingEntry(dd_result.mutable_timing_info()->add_other_times(), "time0",
+                 6.0);
+  SetTimingEntry(dd_result.mutable_timing_info()->add_other_times(), "time1",
+                 7.0);
   std::unique_ptr<base::Value> result =
       dom_distiller::proto::json::DomDistillerResult::WriteToValue(dd_result);
   distiller_.reset(
@@ -427,7 +425,6 @@
 }
 
 TEST_F(DistillerTest, DistillPageWithImages) {
-  base::MessageLoopForUI loop;
   vector<int> image_indices;
   image_indices.push_back(0);
   image_indices.push_back(1);
@@ -463,7 +460,6 @@
 }
 
 TEST_F(DistillerTest, DistillMultiplePages) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
 
   // Add images.
@@ -493,7 +489,6 @@
 }
 
 TEST_F(DistillerTest, DistillLinkLoop) {
-  base::MessageLoopForUI loop;
   // Create a loop, the next page is same as the current page. This could
   // happen if javascript misparses a next page link.
   std::unique_ptr<base::Value> result =
@@ -507,7 +502,6 @@
 }
 
 TEST_F(DistillerTest, CheckMaxPageLimitExtraPage) {
-  base::MessageLoopForUI loop;
   const size_t kMaxPagesInArticle = 10;
   std::unique_ptr<MultipageDistillerData> distiller_data =
       CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle);
@@ -538,7 +532,6 @@
 }
 
 TEST_F(DistillerTest, CheckMaxPageLimitExactLimit) {
-  base::MessageLoopForUI loop;
   const size_t kMaxPagesInArticle = 10;
   std::unique_ptr<MultipageDistillerData> distiller_data =
       CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle);
@@ -559,7 +552,6 @@
 }
 
 TEST_F(DistillerTest, SinglePageDistillationFailure) {
-  base::MessageLoopForUI loop;
   // To simulate failure return a null value.
   auto null_value = std::make_unique<base::Value>();
   distiller_.reset(
@@ -571,7 +563,6 @@
 }
 
 TEST_F(DistillerTest, MultiplePagesDistillationFailure) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
   std::unique_ptr<MultipageDistillerData> distiller_data =
       CreateMultipageDistillerDataWithoutImages(kNumPages);
@@ -597,7 +588,6 @@
 }
 
 TEST_F(DistillerTest, DistillMultiplePagesFirstEmpty) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
   std::unique_ptr<MultipageDistillerData> distiller_data =
       CreateMultipageDistillerDataWithoutImages(kNumPages);
@@ -606,9 +596,10 @@
   const size_t empty_page_num = 0;
   distiller_data->content[empty_page_num] = "";
   std::unique_ptr<base::Value> distilled_value =
-      CreateDistilledValueReturnedFromJS(kTitle, "", vector<int>(),
-      GenerateNextPageUrl(kURL, empty_page_num, kNumPages),
-      GeneratePrevPageUrl(kURL, empty_page_num));
+      CreateDistilledValueReturnedFromJS(
+          kTitle, "", vector<int>(),
+          GenerateNextPageUrl(kURL, empty_page_num, kNumPages),
+          GeneratePrevPageUrl(kURL, empty_page_num));
   // Reset distilled data of the first page.
   distiller_data->distilled_values.erase(
       distiller_data->distilled_values.begin() + empty_page_num);
@@ -623,12 +614,11 @@
   base::RunLoop().RunUntilIdle();
   // If the first page has no content, stop fetching the next page.
   EXPECT_EQ(1, article_proto_->pages_size());
-  VerifyArticleProtoMatchesMultipageData(
-      article_proto_.get(), distiller_data.get(), 1, 1);
+  VerifyArticleProtoMatchesMultipageData(article_proto_.get(),
+                                         distiller_data.get(), 1, 1);
 }
 
 TEST_F(DistillerTest, DistillMultiplePagesSecondEmpty) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
   std::unique_ptr<MultipageDistillerData> distiller_data =
       CreateMultipageDistillerDataWithoutImages(kNumPages);
@@ -637,9 +627,10 @@
   const size_t empty_page_num = 1;
   distiller_data->content[empty_page_num] = "";
   std::unique_ptr<base::Value> distilled_value =
-      CreateDistilledValueReturnedFromJS(kTitle, "", vector<int>(),
-      GenerateNextPageUrl(kURL, empty_page_num, kNumPages),
-      GeneratePrevPageUrl(kURL, empty_page_num));
+      CreateDistilledValueReturnedFromJS(
+          kTitle, "", vector<int>(),
+          GenerateNextPageUrl(kURL, empty_page_num, kNumPages),
+          GeneratePrevPageUrl(kURL, empty_page_num));
   // Reset distilled data of the second page.
   distiller_data->distilled_values.erase(
       distiller_data->distilled_values.begin() + empty_page_num);
@@ -658,7 +649,6 @@
 }
 
 TEST_F(DistillerTest, DistillPreviousPage) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
 
   // The page number of the article on which distillation starts.
@@ -677,7 +667,6 @@
 }
 
 TEST_F(DistillerTest, IncrementalUpdates) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
 
   // The page number of the article on which distillation starts.
@@ -695,12 +684,11 @@
   ASSERT_EQ(kNumPages, static_cast<size_t>(article_proto_->pages_size()));
   EXPECT_EQ(kNumPages, in_sequence_updates_.size());
 
-  VerifyIncrementalUpdatesMatch(
-      distiller_data.get(), kNumPages, in_sequence_updates_, start_page_num);
+  VerifyIncrementalUpdatesMatch(distiller_data.get(), kNumPages,
+                                in_sequence_updates_, start_page_num);
 }
 
 TEST_F(DistillerTest, IncrementalUpdatesDoNotDeleteFinalArticle) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
   int start_page_num = 3;
   std::unique_ptr<MultipageDistillerData> distiller_data =
@@ -722,7 +710,6 @@
 }
 
 TEST_F(DistillerTest, DeletingArticleDoesNotInterfereWithUpdates) {
-  base::MessageLoopForUI loop;
   const size_t kNumPages = 8;
   std::unique_ptr<MultipageDistillerData> distiller_data =
       CreateMultipageDistillerDataWithoutImages(kNumPages);
@@ -741,14 +728,14 @@
 
   // Delete the article.
   article_proto_.reset();
-  VerifyIncrementalUpdatesMatch(
-      distiller_data.get(), kNumPages, in_sequence_updates_, start_page_num);
+  VerifyIncrementalUpdatesMatch(distiller_data.get(), kNumPages,
+                                in_sequence_updates_, start_page_num);
 }
 
 TEST_F(DistillerTest, CancelWithDelayedImageFetchCallback) {
   if (!DistillerImpl::DoesFetchImages())
     return;
-  base::MessageLoopForUI loop;
+
   vector<int> image_indices;
   image_indices.push_back(0);
   std::unique_ptr<base::Value> distilled_value =
@@ -770,15 +757,13 @@
 }
 
 TEST_F(DistillerTest, CancelWithDelayedJSCallback) {
-  base::MessageLoopForUI loop;
   std::unique_ptr<base::Value> distilled_value =
       CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), "");
   MockDistillerPage* distiller_page = nullptr;
   distiller_.reset(
       new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
-  DistillPage(kURL,
-              CreateMockDistillerPageWithPendingJSCallback(&distiller_page,
-                                                           GURL(kURL)));
+  DistillPage(kURL, CreateMockDistillerPageWithPendingJSCallback(
+                        &distiller_page, GURL(kURL)));
   base::RunLoop().RunUntilIdle();
 
   ASSERT_TRUE(distiller_page);
diff --git a/components/dom_distiller/core/distiller_url_fetcher_unittest.cc b/components/dom_distiller/core/distiller_url_fetcher_unittest.cc
index a19e6a0..0d5317409 100644
--- a/components/dom_distiller/core/distiller_url_fetcher_unittest.cc
+++ b/components/dom_distiller/core/distiller_url_fetcher_unittest.cc
@@ -5,8 +5,8 @@
 #include "components/dom_distiller/core/distiller_url_fetcher.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "services/network/test/test_utils.h"
@@ -14,10 +14,9 @@
 #include "url/gurl.h"
 
 const char kTestPageA[] = "http://www.a.com/";
-const char kTestPageAResponse[] = { 1, 2, 3, 4, 5, 6, 7 };
+const char kTestPageAResponse[] = {1, 2, 3, 4, 5, 6, 7};
 const char kTestPageB[] = "http://www.b.com/";
-const char kTestPageBResponse[] = { 'a', 'b', 'c' };
-
+const char kTestPageBResponse[] = {'a', 'b', 'c'};
 
 class DistillerURLFetcherTest : public testing::Test {
  public:
@@ -26,9 +25,7 @@
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)) {}
 
-  void FetcherCallback(const std::string& response) {
-     response_ = response;
-  }
+  void FetcherCallback(const std::string& response) { response_ = response; }
 
  protected:
   // testing::Test implementation:
@@ -45,17 +42,16 @@
         network::URLLoaderCompletionStatus(net::OK));
   }
 
-  void Fetch(const std::string& url,
-             const std::string& expected_response) {
-    base::MessageLoopForUI loop;
-    url_fetcher_->FetchURL(
-        url,
-        base::Bind(&DistillerURLFetcherTest::FetcherCallback,
-                   base::Unretained(this)));
+  void Fetch(const std::string& url, const std::string& expected_response) {
+    url_fetcher_->FetchURL(url,
+                           base::Bind(&DistillerURLFetcherTest::FetcherCallback,
+                                      base::Unretained(this)));
     base::RunLoop().RunUntilIdle();
     CHECK_EQ(expected_response, response_);
   }
 
+  base::test::ScopedTaskEnvironment task_environment_{
+      base::test::ScopedTaskEnvironment::MainThreadType::UI};
   std::unique_ptr<dom_distiller::DistillerURLFetcher> url_fetcher_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory>
diff --git a/components/dom_distiller/core/dom_distiller_service_unittest.cc b/components/dom_distiller/core/dom_distiller_service_unittest.cc
index 2e2271d4..a61a60e 100644
--- a/components/dom_distiller/core/dom_distiller_service_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_service_unittest.cc
@@ -9,9 +9,9 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/hash_tables.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/dom_distiller/core/distilled_page_prefs.h"
 #include "components/dom_distiller/core/dom_distiller_model.h"
@@ -82,7 +82,6 @@
 class DomDistillerServiceTest : public testing::Test {
  public:
   void SetUp() override {
-    main_loop_.reset(new base::MessageLoop());
     FakeDB<ArticleEntry>* fake_db = new FakeDB<ArticleEntry>(&db_model_);
     FakeDB<ArticleEntry>::EntryMap store_model;
     store_ =
@@ -106,12 +105,12 @@
   }
 
  protected:
+  base::test::ScopedTaskEnvironment task_environment_;
   // store is owned by service_.
   DomDistillerStoreInterface* store_;
   MockDistillerFactory* distiller_factory_;
   MockDistillerPageFactory* distiller_page_factory_;
   std::unique_ptr<DomDistillerService> service_;
-  std::unique_ptr<base::MessageLoop> main_loop_;
   FakeDB<ArticleEntry>::EntryMap db_model_;
 };
 
diff --git a/components/dom_distiller/core/dom_distiller_store_unittest.cc b/components/dom_distiller/core/dom_distiller_store_unittest.cc
index ce188a3..92b35c70 100644
--- a/components/dom_distiller/core/dom_distiller_store_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_store_unittest.cc
@@ -13,8 +13,8 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/time/time.h"
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/dom_distiller/core/dom_distiller_test_util.h"
@@ -159,7 +159,7 @@
     return data;
   }
 
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment task_environment_;
 
   EntryMap db_model_;
   EntryMap sync_model_;
diff --git a/components/dom_distiller/core/task_tracker_unittest.cc b/components/dom_distiller/core/task_tracker_unittest.cc
index 5759cfa..da98901e 100644
--- a/components/dom_distiller/core/task_tracker_unittest.cc
+++ b/components/dom_distiller/core/task_tracker_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <utility>
 
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/dom_distiller/core/article_distillation_update.h"
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/dom_distiller/core/distilled_content_store.h"
@@ -61,7 +61,6 @@
 class DomDistillerTaskTrackerTest : public testing::Test {
  public:
   void SetUp() override {
-    message_loop_.reset(new base::MessageLoop());
     entry_id_ = "id0";
     page_0_url_ = GURL("http://www.example.com/1");
     page_1_url_ = GURL("http://www.example.com/2");
@@ -78,7 +77,7 @@
   }
 
  protected:
-  std::unique_ptr<base::MessageLoop> message_loop_;
+  base::test::ScopedTaskEnvironment task_environment_;
   std::string entry_id_;
   GURL page_0_url_;
   GURL page_1_url_;
diff --git a/components/exo/display.cc b/components/exo/display.cc
index 393a75c..6c5243a 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -7,23 +7,18 @@
 #include <iterator>
 #include <utility>
 
-#include "ash/public/cpp/shell_window_ids.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
-#include "components/exo/client_controlled_shell_surface.h"
 #include "components/exo/data_device.h"
 #include "components/exo/file_helper.h"
-#include "components/exo/input_method_surface.h"
 #include "components/exo/input_method_surface_manager.h"
 #include "components/exo/notification_surface.h"
 #include "components/exo/notification_surface_manager.h"
 #include "components/exo/shared_memory.h"
-#include "components/exo/shell_surface.h"
 #include "components/exo/sub_surface.h"
 #include "components/exo/surface.h"
-#include "components/exo/xdg_shell_surface.h"
 #include "ui/gfx/linux/client_native_pixmap_factory_dmabuf.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -37,26 +32,36 @@
 #include "ui/ozone/public/ozone_switches.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "ash/public/cpp/shell_window_ids.h"
+#include "components/exo/client_controlled_shell_surface.h"
+#include "components/exo/input_method_surface.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/xdg_shell_surface.h"
+#endif
+
 namespace exo {
 
 ////////////////////////////////////////////////////////////////////////////////
 // Display, public:
 
-Display::Display() : Display(nullptr, nullptr, std::unique_ptr<FileHelper>()) {}
+Display::Display() : file_helper_(std::unique_ptr<FileHelper>()) {}
 
+#if defined(OS_CHROMEOS)
 Display::Display(NotificationSurfaceManager* notification_surface_manager,
                  InputMethodSurfaceManager* input_method_surface_manager,
                  std::unique_ptr<FileHelper> file_helper)
-    : notification_surface_manager_(notification_surface_manager),
-      input_method_surface_manager_(input_method_surface_manager),
-      file_helper_(std::move(file_helper))
+    : file_helper_(std::move(file_helper))
 #if defined(USE_OZONE)
       ,
       client_native_pixmap_factory_(
           gfx::CreateClientNativePixmapFactoryDmabuf())
-#endif
+#endif  // defined(USE_OZONE)
 {
+  notification_surface_manager_ = notification_surface_manager;
+  input_method_surface_manager_ = input_method_surface_manager;
 }
+#endif  // defined(OS_CHROMEOS)
 
 Display::~Display() {}
 
@@ -118,8 +123,9 @@
       GL_COMMANDS_COMPLETED_CHROMIUM, use_zero_copy, is_overlay_candidate,
       y_invert);
 }
-#endif
+#endif  // defined(USE_OZONE)
 
+#if defined(OS_CHROMEOS)
 std::unique_ptr<ShellSurface> Display::CreateShellSurface(Surface* surface) {
   TRACE_EVENT1("exo", "Display::CreateShellSurface", "surface",
                surface->AsTracedValue());
@@ -171,24 +177,6 @@
   return shell_surface;
 }
 
-std::unique_ptr<SubSurface> Display::CreateSubSurface(Surface* surface,
-                                                      Surface* parent) {
-  TRACE_EVENT2("exo", "Display::CreateSubSurface", "surface",
-               surface->AsTracedValue(), "parent", parent->AsTracedValue());
-
-  if (surface->window()->Contains(parent->window())) {
-    DLOG(ERROR) << "Parent is contained within surface's hierarchy";
-    return nullptr;
-  }
-
-  if (surface->HasSurfaceDelegate()) {
-    DLOG(ERROR) << "Surface has already been assigned a role";
-    return nullptr;
-  }
-
-  return std::make_unique<SubSurface>(surface, parent);
-}
-
 std::unique_ptr<NotificationSurface> Display::CreateNotificationSurface(
     Surface* surface,
     const std::string& notification_key) {
@@ -205,11 +193,6 @@
                                                surface, notification_key);
 }
 
-std::unique_ptr<DataDevice> Display::CreateDataDevice(
-    DataDeviceDelegate* delegate) {
-  return std::make_unique<DataDevice>(delegate, &seat_, file_helper_.get());
-}
-
 std::unique_ptr<InputMethodSurface> Display::CreateInputMethodSurface(
     Surface* surface,
     double default_device_scale_factor) {
@@ -229,5 +212,29 @@
   return std::make_unique<InputMethodSurface>(
       input_method_surface_manager_, surface, default_device_scale_factor);
 }
+#endif  // defined(OS_CHROMEOS)
+
+std::unique_ptr<SubSurface> Display::CreateSubSurface(Surface* surface,
+                                                      Surface* parent) {
+  TRACE_EVENT2("exo", "Display::CreateSubSurface", "surface",
+               surface->AsTracedValue(), "parent", parent->AsTracedValue());
+
+  if (surface->window()->Contains(parent->window())) {
+    DLOG(ERROR) << "Parent is contained within surface's hierarchy";
+    return nullptr;
+  }
+
+  if (surface->HasSurfaceDelegate()) {
+    DLOG(ERROR) << "Surface has already been assigned a role";
+    return nullptr;
+  }
+
+  return std::make_unique<SubSurface>(surface, parent);
+}
+
+std::unique_ptr<DataDevice> Display::CreateDataDevice(
+    DataDeviceDelegate* delegate) {
+  return std::make_unique<DataDevice>(delegate, &seat_, file_helper_.get());
+}
 
 }  // namespace exo
diff --git a/components/exo/display.h b/components/exo/display.h
index bb749ab5..a1fa2d5 100644
--- a/components/exo/display.h
+++ b/components/exo/display.h
@@ -30,15 +30,18 @@
 class DataDevice;
 class DataDeviceDelegate;
 class FileHelper;
-class InputMethodSurface;
 class InputMethodSurfaceManager;
 class NotificationSurface;
 class NotificationSurfaceManager;
 class SharedMemory;
-class ShellSurface;
 class SubSurface;
 class Surface;
+
+#if defined(OS_CHROMEOS)
+class InputMethodSurface;
+class ShellSurface;
 class XdgShellSurface;
+#endif
 
 #if defined(USE_OZONE)
 class Buffer;
@@ -50,9 +53,13 @@
 class Display {
  public:
   Display();
+
+#if defined(OS_CHROMEOS)
   Display(NotificationSurfaceManager* notification_surface_manager,
           InputMethodSurfaceManager* input_method_surface_manager,
           std::unique_ptr<FileHelper> file_helper);
+#endif  // defined(OS_CHROMEOS)
+
   ~Display();
 
   // Creates a new surface.
@@ -71,8 +78,9 @@
       const std::vector<gfx::NativePixmapPlane>& planes,
       bool y_invert,
       std::vector<base::ScopedFD>&& fds);
-#endif
+#endif  // defined(USE_OZONE)
 
+#if defined(OS_CHROMEOS)
   // Creates a shell surface for an existing surface.
   std::unique_ptr<ShellSurface> CreateShellSurface(Surface* surface);
 
@@ -86,36 +94,40 @@
                                      int container,
                                      double default_device_scale_factor);
 
-  // Creates a sub-surface for an existing surface. The sub-surface will be
-  // a child of |parent|.
-  std::unique_ptr<SubSurface> CreateSubSurface(Surface* surface,
-                                               Surface* parent);
-
   // Creates a notification surface for a surface and notification id.
   std::unique_ptr<NotificationSurface> CreateNotificationSurface(
       Surface* surface,
       const std::string& notification_key);
 
-  // Creates a data device for a |delegate|.
-  std::unique_ptr<DataDevice> CreateDataDevice(DataDeviceDelegate* delegate);
-
   // Creates a input method surface for a surface.
   std::unique_ptr<InputMethodSurface> CreateInputMethodSurface(
       Surface* surface,
       double default_device_scale_factor);
+#endif  // defined(OS_CHROMEOS)
+
+  // Creates a sub-surface for an existing surface. The sub-surface will be
+  // a child of |parent|.
+  std::unique_ptr<SubSurface> CreateSubSurface(Surface* surface,
+                                               Surface* parent);
+
+  // Creates a data device for a |delegate|.
+  std::unique_ptr<DataDevice> CreateDataDevice(DataDeviceDelegate* delegate);
 
   // Obtains seat instance.
   Seat* seat() { return &seat_; }
 
  private:
-  NotificationSurfaceManager* const notification_surface_manager_;
-  InputMethodSurfaceManager* const input_method_surface_manager_;
+#if defined(OS_CHROMEOS)
+  NotificationSurfaceManager* notification_surface_manager_ = nullptr;
+  InputMethodSurfaceManager* input_method_surface_manager_ = nullptr;
+#endif  // defined(OS_CHROMEOS)
+
   std::unique_ptr<FileHelper> file_helper_;
   Seat seat_;
 
 #if defined(USE_OZONE)
   std::unique_ptr<gfx::ClientNativePixmapFactory> client_native_pixmap_factory_;
-#endif
+#endif  // defined(USE_OZONE)
 
   DISALLOW_COPY_AND_ASSIGN(Display);
 };
diff --git a/components/feed/core/feed_logging_metrics.cc b/components/feed/core/feed_logging_metrics.cc
index 3d5ba4b..63263a7 100644
--- a/components/feed/core/feed_logging_metrics.cc
+++ b/components/feed/core/feed_logging_metrics.cc
@@ -181,6 +181,10 @@
   base::RecordAction(base::UserMetricsAction("Suggestions.Content.Dismissed"));
 }
 
+void FeedLoggingMetrics::OnSuggestionSwiped() {
+  base::RecordAction(base::UserMetricsAction("Suggestions.Card.SwipedAway"));
+}
+
 void FeedLoggingMetrics::OnSuggestionArticleVisited(base::TimeDelta visit_time,
                                                     bool return_to_ntp) {
   base::UmaHistogramLongTimes(
@@ -212,6 +216,15 @@
       kMaxSuggestionsForArticle + 1);
 }
 
+void FeedLoggingMetrics::OnSpinnerShown(base::TimeDelta shown_time) {
+  base::UmaHistogramLongTimes(
+      "ContentSuggestions.FetchPendingSpinner.VisibleDuration", shown_time);
+}
+
+void FeedLoggingMetrics::ReportScrolledAfterOpen() {
+  base::RecordAction(base::UserMetricsAction("Suggestions.ScrolledAfterOpen"));
+}
+
 void FeedLoggingMetrics::CheckURLVisitedDone(int position, bool visited) {
   if (visited) {
     UMA_HISTOGRAM_EXACT_LINEAR("NewTabPage.ContentSuggestions.DismissedVisited",
diff --git a/components/feed/core/feed_logging_metrics.h b/components/feed/core/feed_logging_metrics.h
index ae4397d..abb42d1 100644
--- a/components/feed/core/feed_logging_metrics.h
+++ b/components/feed/core/feed_logging_metrics.h
@@ -58,6 +58,8 @@
 
   void OnSuggestionDismissed(int position, const GURL& url);
 
+  void OnSuggestionSwiped();
+
   void OnSuggestionArticleVisited(base::TimeDelta visit_time,
                                   bool return_to_ntp);
 
@@ -69,6 +71,10 @@
 
   void OnMoreButtonClicked(int position);
 
+  void OnSpinnerShown(base::TimeDelta shown_time);
+
+  void ReportScrolledAfterOpen();
+
  private:
   void CheckURLVisitedDone(int position, bool visited);
 
diff --git a/components/offline_pages/core/background/add_request_task_unittest.cc b/components/offline_pages/core/background/add_request_task_unittest.cc
index a2dc2ef..2892dde7 100644
--- a/components/offline_pages/core/background/add_request_task_unittest.cc
+++ b/components/offline_pages/core/background/add_request_task_unittest.cc
@@ -9,9 +9,11 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -86,7 +88,7 @@
 
 TEST_F(AddRequestTaskTest, AddSingleRequest) {
   InitializeStore(&store_);
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
                             true);
   AddRequestTask task(&store_, request_1,
@@ -110,7 +112,7 @@
 
 TEST_F(AddRequestTaskTest, AddMultipleRequests) {
   InitializeStore(&store_);
-  base::Time creation_time_1 = base::Time::Now();
+  base::Time creation_time_1 = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
                             true);
   AddRequestTask task(&store_, request_1,
@@ -122,7 +124,7 @@
   EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
 
   ClearResults();
-  base::Time creation_time_2 = base::Time::Now();
+  base::Time creation_time_2 = OfflineClock()->Now();
   SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time_2,
                             true);
   AddRequestTask task_2(&store_, request_2,
@@ -149,7 +151,7 @@
 
 TEST_F(AddRequestTaskTest, AddDuplicateRequest) {
   InitializeStore(&store_);
-  base::Time creation_time_1 = base::Time::Now();
+  base::Time creation_time_1 = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time_1,
                             true);
   AddRequestTask task(&store_, request_1,
@@ -161,7 +163,7 @@
   EXPECT_EQ(ItemActionStatus::SUCCESS, last_status());
 
   ClearResults();
-  base::Time creation_time_2 = base::Time::Now();
+  base::Time creation_time_2 = OfflineClock()->Now();
   // This was has the same request ID.
   SavePageRequest request_2(kRequestId1, kUrl2, kClientId2, creation_time_2,
                             true);
diff --git a/components/offline_pages/core/background/change_requests_state_task_unittest.cc b/components/offline_pages/core/background/change_requests_state_task_unittest.cc
index c81d93a..cb68eb5a 100644
--- a/components/offline_pages/core/background/change_requests_state_task_unittest.cc
+++ b/components/offline_pages/core/background/change_requests_state_task_unittest.cc
@@ -9,9 +9,11 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -41,7 +43,7 @@
 };
 
 void ChangeRequestsStateTaskTest::AddItemsToStore() {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
                             true);
   store_.AddRequest(request_1,
diff --git a/components/offline_pages/core/background/cleanup_task_unittest.cc b/components/offline_pages/core/background/cleanup_task_unittest.cc
index 3c6b494..c6576b1 100644
--- a/components/offline_pages/core/background/cleanup_task_unittest.cc
+++ b/components/offline_pages/core/background/cleanup_task_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
 #include "components/offline_pages/core/background/request_coordinator_event_logger.h"
@@ -18,6 +19,7 @@
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -159,7 +161,7 @@
 }
 
 TEST_F(CleanupTaskTest, CleanupExpiredRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   base::Time expired_time =
       creation_time - base::TimeDelta::FromSeconds(
                           policy()->GetRequestExpirationTimeInSeconds() + 10);
@@ -183,7 +185,7 @@
 }
 
 TEST_F(CleanupTaskTest, CleanupStartCountExceededRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   // Request2 will have an exceeded start count.
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
@@ -205,7 +207,7 @@
 }
 
 TEST_F(CleanupTaskTest, CleanupCompletionCountExceededRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   // Request2 will have an exceeded completion count.
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
@@ -227,7 +229,7 @@
 }
 
 TEST_F(CleanupTaskTest, IgnoreRequestInProgress) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   // Both requests will have an exceeded completion count.
   // The first request will be marked as started.
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
diff --git a/components/offline_pages/core/background/get_requests_task_unittest.cc b/components/offline_pages/core/background/get_requests_task_unittest.cc
index 0f78a3d2..83f1d53 100644
--- a/components/offline_pages/core/background/get_requests_task_unittest.cc
+++ b/components/offline_pages/core/background/get_requests_task_unittest.cc
@@ -9,9 +9,11 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -50,13 +52,13 @@
 };
 
 void GetRequestsTaskTest::AddItemsToStore(RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
                             true);
   store->AddRequest(request_1,
                     base::BindOnce(&GetRequestsTaskTest::AddRequestDone,
                                    base::Unretained(this)));
-  creation_time = base::Time::Now();
+  creation_time = OfflineClock()->Now();
   SavePageRequest request_2(kRequestId2, kUrl2, kClientId2, creation_time,
                             true);
   store->AddRequest(request_2,
diff --git a/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
index 37db9a4..1688862b 100644
--- a/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
+++ b/components/offline_pages/core/background/mark_attempt_aborted_task_unittest.cc
@@ -9,11 +9,13 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/change_requests_state_task.h"
 #include "components/offline_pages/core/background/mark_attempt_started_task.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -44,7 +46,7 @@
 };
 
 void MarkAttemptAbortedTaskTest::AddItemToStore(RequestQueueStore* store) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
                             true);
   store->AddRequest(request_1,
diff --git a/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
index e458554..a75923e 100644
--- a/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
+++ b/components/offline_pages/core/background/mark_attempt_completed_task_unittest.cc
@@ -8,9 +8,11 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -37,10 +39,10 @@
 };
 
 void MarkAttemptCompletedTaskTest::AddStartedItemToStore() {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
                             true);
-  request_1.MarkAttemptStarted(base::Time::Now());
+  request_1.MarkAttemptStarted(OfflineClock()->Now());
   store_.AddRequest(
       request_1, base::BindOnce(&MarkAttemptCompletedTaskTest::AddRequestDone,
                                 base::Unretained(this)));
diff --git a/components/offline_pages/core/background/mark_attempt_deferred_task.cc b/components/offline_pages/core/background/mark_attempt_deferred_task.cc
index 3023d3a..ca2786e 100644
--- a/components/offline_pages/core/background/mark_attempt_deferred_task.cc
+++ b/components/offline_pages/core/background/mark_attempt_deferred_task.cc
@@ -7,7 +7,9 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/time/clock.h"
 #include "base/time/time.h"
+#include "components/offline_pages/core/offline_clock.h"
 
 namespace offline_pages {
 
@@ -28,7 +30,7 @@
 
   // It is perfectly fine to reuse the read_result.updated_items collection, as
   // it is owned by this callback and will be destroyed when out of scope.
-  read_result.updated_items[0].MarkAttemptDeferred(base::Time::Now());
+  read_result.updated_items[0].MarkAttemptDeferred(OfflineClock()->Now());
   store()->UpdateRequests(
       read_result.updated_items,
       base::BindOnce(&MarkAttemptDeferredTask::CompleteWithResult,
diff --git a/components/offline_pages/core/background/mark_attempt_started_task.cc b/components/offline_pages/core/background/mark_attempt_started_task.cc
index 5067348..3d702fe5 100644
--- a/components/offline_pages/core/background/mark_attempt_started_task.cc
+++ b/components/offline_pages/core/background/mark_attempt_started_task.cc
@@ -7,7 +7,9 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/time/clock.h"
 #include "base/time/time.h"
+#include "components/offline_pages/core/offline_clock.h"
 
 namespace offline_pages {
 
@@ -28,7 +30,7 @@
 
   // It is perfectly fine to reuse the read_result.updated_items collection, as
   // it is owned by this callback and will be destroyed when out of scope.
-  read_result.updated_items[0].MarkAttemptStarted(base::Time::Now());
+  read_result.updated_items[0].MarkAttemptStarted(OfflineClock()->Now());
   store()->UpdateRequests(
       read_result.updated_items,
       base::BindOnce(&MarkAttemptStartedTask::CompleteWithResult,
diff --git a/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc b/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
index 92fab85b..12b3ab7 100644
--- a/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
+++ b/components/offline_pages/core/background/mark_attempt_started_task_unittest.cc
@@ -9,9 +9,11 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -38,7 +40,7 @@
 };
 
 void MarkAttemptStartedTaskTest::AddItemToStore() {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
                             true);
   store_.AddRequest(request_1,
@@ -83,7 +85,7 @@
                      base::Unretained(this)));
 
   // Current time for verification.
-  base::Time before_time = base::Time::Now();
+  base::Time before_time = OfflineClock()->Now();
   task.Run();
   PumpLoop();
   ASSERT_TRUE(last_result());
@@ -94,7 +96,7 @@
   EXPECT_EQ(1UL, last_result()->updated_items.size());
   EXPECT_LE(before_time,
             last_result()->updated_items.at(0).last_attempt_time());
-  EXPECT_GE(base::Time::Now(),
+  EXPECT_GE(OfflineClock()->Now(),
             last_result()->updated_items.at(0).last_attempt_time());
   EXPECT_EQ(1, last_result()->updated_items.at(0).started_attempt_count());
   EXPECT_EQ(SavePageRequest::RequestState::OFFLINING,
diff --git a/components/offline_pages/core/background/offliner_policy_utils.cc b/components/offline_pages/core/background/offliner_policy_utils.cc
index a9383f24..2ba3e608 100644
--- a/components/offline_pages/core/background/offliner_policy_utils.cc
+++ b/components/offline_pages/core/background/offliner_policy_utils.cc
@@ -4,8 +4,10 @@
 
 #include "components/offline_pages/core/background/offliner_policy_utils.h"
 
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
 #include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_clock.h"
 
 namespace offline_pages {
 
@@ -17,7 +19,7 @@
   DCHECK(request);
   DCHECK(policy);
 
-  if (base::Time::Now() - request->creation_time() >=
+  if (OfflineClock()->Now() - request->creation_time() >=
       base::TimeDelta::FromSeconds(
           policy->GetRequestExpirationTimeInSeconds())) {
     return RequestExpirationStatus::EXPIRED;
diff --git a/components/offline_pages/core/background/pick_request_task.cc b/components/offline_pages/core/background/pick_request_task.cc
index cfda513..02f0dde 100644
--- a/components/offline_pages/core/background/pick_request_task.cc
+++ b/components/offline_pages/core/background/pick_request_task.cc
@@ -11,6 +11,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/offline_pages/core/background/device_conditions.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
@@ -20,6 +21,7 @@
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_clock.h"
 
 namespace {
 template <typename T>
@@ -137,7 +139,8 @@
     if (policy_controller_->GetPolicy(request->client_id().name_space)
             .defer_background_fetch_while_page_is_active) {
       if (!request->last_attempt_time().is_null() &&
-          base::Time::Now() - request->last_attempt_time() < kDeferInterval) {
+          OfflineClock()->Now() - request->last_attempt_time() <
+              kDeferInterval) {
         defer_available_time = request->last_attempt_time() + kDeferInterval;
         continue;
       }
diff --git a/components/offline_pages/core/background/pick_request_task_unittest.cc b/components/offline_pages/core/background/pick_request_task_unittest.cc
index 16ae3d5..945498b 100644
--- a/components/offline_pages/core/background/pick_request_task_unittest.cc
+++ b/components/offline_pages/core/background/pick_request_task_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/containers/circular_deque.h"
+#include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/offline_pages/core/background/device_conditions.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
@@ -21,6 +22,7 @@
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
 #include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -234,7 +236,7 @@
       kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
   MakePickRequestTask();
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
@@ -255,8 +257,8 @@
 
 TEST_F(PickRequestTaskTest, ChooseRequestWithSameRetryCountButEarlier) {
   base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(10);
-  base::Time creation_time2 = base::Time::Now();
+      OfflineClock()->Now() - base::TimeDelta::FromSeconds(10);
+  base::Time creation_time2 = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
@@ -280,8 +282,8 @@
   MakePickRequestTask();
 
   base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(10);
-  base::Time creation_time2 = base::Time::Now();
+      OfflineClock()->Now() - base::TimeDelta::FromSeconds(10);
+  base::Time creation_time2 = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
@@ -305,7 +307,7 @@
       kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
   MakePickRequestTask();
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
@@ -329,7 +331,7 @@
       kMaxCompletedTries + 1, kBackgroundProcessingTimeBudgetSeconds));
   MakePickRequestTask();
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
@@ -354,8 +356,8 @@
   MakePickRequestTask();
 
   base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(10);
-  base::Time creation_time2 = base::Time::Now();
+      OfflineClock()->Now() - base::TimeDelta::FromSeconds(10);
+  base::Time creation_time2 = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
@@ -372,7 +374,7 @@
 }
 
 TEST_F(PickRequestTaskTest, ChooseNonExpiredRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   base::Time expired_time =
       creation_time - base::TimeDelta::FromSeconds(
                           policy_->GetRequestExpirationTimeInSeconds() + 60);
@@ -396,8 +398,8 @@
 
 TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededStartLimit) {
   base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(1);
-  base::Time creation_time2 = base::Time::Now();
+      OfflineClock()->Now() - base::TimeDelta::FromSeconds(1);
+  base::Time creation_time2 = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
@@ -422,8 +424,8 @@
 
 TEST_F(PickRequestTaskTest, ChooseRequestThatHasNotExceededCompletionLimit) {
   base::Time creation_time1 =
-      base::Time::Now() - base::TimeDelta::FromSeconds(1);
-  base::Time creation_time2 = base::Time::Now();
+      OfflineClock()->Now() - base::TimeDelta::FromSeconds(1);
+  base::Time creation_time2 = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time1,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time2,
@@ -454,7 +456,7 @@
   disabled_requests_.insert(kRequestId2);
   MakePickRequestTask();
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
@@ -483,7 +485,7 @@
   prioritized_requests_.push_back(kRequestId2);
   MakePickRequestTask();
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
   SavePageRequest request2(kRequestId2, kUrl2, kClientId2, creation_time,
@@ -522,7 +524,7 @@
   // Making request 1 more attractive to be picked not considering the
   // prioritizing issues with older creation time, fewer attempt count and it's
   // earlier in the request queue.
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   base::Time older_creation_time =
       creation_time - base::TimeDelta::FromMinutes(10);
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, older_creation_time,
diff --git a/components/offline_pages/core/background/reconcile_task_unittest.cc b/components/offline_pages/core/background/reconcile_task_unittest.cc
index e92b7c3d..240eebe 100644
--- a/components/offline_pages/core/background/reconcile_task_unittest.cc
+++ b/components/offline_pages/core/background/reconcile_task_unittest.cc
@@ -10,11 +10,13 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -116,7 +118,7 @@
 }
 
 TEST_F(ReconcileTaskTest, Reconcile) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   // Request2 will be expired, request1 will be current.
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
@@ -151,7 +153,7 @@
 }
 
 TEST_F(ReconcileTaskTest, NothingToReconcile) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   // Request2 will be expired, request1 will be current.
   SavePageRequest request1(kRequestId1, kUrl1, kClientId1, creation_time,
                            kUserRequested);
diff --git a/components/offline_pages/core/background/remove_requests_task_unittest.cc b/components/offline_pages/core/background/remove_requests_task_unittest.cc
index b93eae05..be0af62 100644
--- a/components/offline_pages/core/background/remove_requests_task_unittest.cc
+++ b/components/offline_pages/core/background/remove_requests_task_unittest.cc
@@ -9,9 +9,11 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/request_queue_task_test_base.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -47,7 +49,7 @@
 }
 
 void RemoveRequestsTaskTest::AddRequestsToStore() {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request_1(kRequestId1, kUrl1, kClientId1, creation_time,
                             true);
   store_.AddRequest(request_1,
diff --git a/components/offline_pages/core/background/request_coordinator.cc b/components/offline_pages/core/background/request_coordinator.cc
index 5dcae9f..086ae2d2 100644
--- a/components/offline_pages/core/background/request_coordinator.cc
+++ b/components/offline_pages/core/background/request_coordinator.cc
@@ -15,11 +15,13 @@
 #include "base/rand_util.h"
 #include "base/stl_util.h"
 #include "base/system/sys_info.h"
+#include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/offline_pages/core/background/offliner.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/client_policy_controller.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/offline_page_model.h"
@@ -63,7 +65,7 @@
   // For successful requests also record time from request to save.
   if (request_status == Offliner::RequestStatus::SAVED ||
       request_status == Offliner::RequestStatus::SAVED_ON_LAST_RETRY) {
-    base::TimeDelta duration = base::Time::Now() - request_creation_time;
+    base::TimeDelta duration = OfflineClock()->Now() - request_creation_time;
     base::UmaHistogramCustomCounts(
         AddHistogramSuffix(client_id, "OfflinePages.Background.TimeToSaved"),
         duration.InSeconds(), kMinDuration.InSeconds(),
@@ -98,7 +100,7 @@
     histogram_name += ".Svelte";
   }
 
-  base::TimeDelta duration = base::Time::Now() - request.creation_time();
+  base::TimeDelta duration = OfflineClock()->Now() - request.creation_time();
   base::UmaHistogramCustomTimes(
       AddHistogramSuffix(request.client_id(), histogram_name.c_str()), duration,
       base::TimeDelta::FromMilliseconds(100), base::TimeDelta::FromDays(7), 50);
@@ -108,7 +110,7 @@
   // Using regular histogram (with dynamic suffix) rather than time-oriented
   // one to record samples in seconds rather than milliseconds.
   base::TimeDelta duration =
-      base::Time::Now() - canceled_request.creation_time();
+      OfflineClock()->Now() - canceled_request.creation_time();
   base::UmaHistogramCustomCounts(
       AddHistogramSuffix(canceled_request.client_id(),
                          "OfflinePages.Background.TimeToCanceled"),
@@ -259,7 +261,7 @@
   // Build a SavePageRequest.
   offline_pages::SavePageRequest request(
       id, save_page_later_params.url, save_page_later_params.client_id,
-      base::Time::Now(), save_page_later_params.user_requested);
+      OfflineClock()->Now(), save_page_later_params.user_requested);
   request.set_original_url(save_page_later_params.original_url);
   request.set_request_origin(save_page_later_params.request_origin);
   pending_state_updater_.SetPendingState(request);
@@ -661,7 +663,7 @@
 
   // Mark the time at which we started processing so we can check our time
   // budget.
-  operation_start_time_ = base::Time::Now();
+  operation_start_time_ = OfflineClock()->Now();
 
   TryNextRequest(kStartOfProcessing);
 
@@ -775,7 +777,8 @@
   // will return to us at the next opportunity to run background tasks.
   if (connection_type ==
           net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE ||
-      (base::Time::Now() - operation_start_time_) > processing_time_budget) {
+      (OfflineClock()->Now() - operation_start_time_) >
+          processing_time_budget) {
     state_ = RequestCoordinatorState::IDLE;
 
     // If we were doing immediate processing, try to start it again
@@ -847,7 +850,7 @@
   } else if (!available_time.is_null()) {
     scheduler_->BackupSchedule(
         GetTriggerConditions(kUserRequest),
-        (available_time - base::Time::Now()).InSeconds() +
+        (available_time - OfflineClock()->Now()).InSeconds() +
             1 /*Add an extra second to avoid rounding down.*/);
   }
 
diff --git a/components/offline_pages/core/background/request_coordinator_unittest.cc b/components/offline_pages/core/background/request_coordinator_unittest.cc
index 1abf67dd..28ea3e22 100644
--- a/components/offline_pages/core/background/request_coordinator_unittest.cc
+++ b/components/offline_pages/core/background/request_coordinator_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/offline_items_collection/core/pending_state.h"
 #include "components/offline_pages/core/background/device_conditions.h"
@@ -32,6 +33,7 @@
 #include "components/offline_pages/core/background/scheduler.h"
 #include "components/offline_pages/core/background/scheduler_stub.h"
 #include "components/offline_pages/core/client_namespace_constants.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "services/network/test/test_network_quality_tracker.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -470,7 +472,7 @@
     offline_pages::SavePageRequest* request) {
   // Mark request as started and add it to the queue,
   // then wait for callback to finish.
-  request->MarkAttemptStarted(base::Time::Now());
+  request->MarkAttemptStarted(OfflineClock()->Now());
   queue()->AddRequest(*request,
                       base::BindOnce(&RequestCoordinatorTest::AddRequestDone,
                                      base::Unretained(this)));
@@ -485,7 +487,7 @@
   // Mock that coordinator is in actively processing state starting now.
   SetProcessingStateForTest(
       RequestCoordinator::ProcessingWindowState::IMMEDIATE_WINDOW);
-  SetOperationStartTimeForTest(base::Time::Now());
+  SetOperationStartTimeForTest(OfflineClock()->Now());
 }
 
 void RequestCoordinatorTest::SendOfflinerDoneCallback(
@@ -496,8 +498,8 @@
 }
 
 SavePageRequest RequestCoordinatorTest::AddRequest1() {
-  offline_pages::SavePageRequest request1(kRequestId1, kUrl1, kClientId1,
-                                          base::Time::Now(), kUserRequested);
+  offline_pages::SavePageRequest request1(
+      kRequestId1, kUrl1, kClientId1, OfflineClock()->Now(), kUserRequested);
   queue()->AddRequest(request1,
                       base::BindOnce(&RequestCoordinatorTest::AddRequestDone,
                                      base::Unretained(this)));
@@ -505,8 +507,8 @@
 }
 
 SavePageRequest RequestCoordinatorTest::AddRequest2() {
-  offline_pages::SavePageRequest request2(kRequestId2, kUrl2, kClientId2,
-                                          base::Time::Now(), kUserRequested);
+  offline_pages::SavePageRequest request2(
+      kRequestId2, kUrl2, kClientId2, OfflineClock()->Now(), kUserRequested);
   queue()->AddRequest(request2,
                       base::BindOnce(&RequestCoordinatorTest::AddRequestDone,
                                      base::Unretained(this)));
@@ -713,7 +715,7 @@
 TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceeded) {
   // Add a request to the queue, wait for callbacks to finish.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   SetupForOfflinerDoneCallbackTest(&request);
 
   // Call the OfflinerDoneCallback to simulate the page being completed, wait
@@ -744,7 +746,7 @@
 TEST_F(RequestCoordinatorTest, OfflinerDoneRequestSucceededButLostNetwork) {
   // Add a request to the queue and set offliner done callback for it.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   SetupForOfflinerDoneCallbackTest(&request);
   EnableOfflinerCallback(false);
 
@@ -773,7 +775,7 @@
 TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailed) {
   // Add a request to the queue, wait for callbacks to finish.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   request.set_completed_attempt_count(kMaxCompletedTries - 1);
   SetupForOfflinerDoneCallbackTest(&request);
   // Stop processing before completing the second request on the queue.
@@ -816,7 +818,7 @@
 TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoRetryFailure) {
   // Add a request to the queue, wait for callbacks to finish.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   SetupForOfflinerDoneCallbackTest(&request);
   EnableOfflinerCallback(false);
 
@@ -859,7 +861,7 @@
 TEST_F(RequestCoordinatorTest, OfflinerDoneRequestFailedNoNextFailure) {
   // Add a request to the queue, wait for callbacks to finish.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   SetupForOfflinerDoneCallbackTest(&request);
   EnableOfflinerCallback(false);
 
@@ -891,7 +893,7 @@
 TEST_F(RequestCoordinatorTest, OfflinerDoneForegroundCancel) {
   // Add a request to the queue, wait for callbacks to finish.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   SetupForOfflinerDoneCallbackTest(&request);
 
   // Call the OfflinerDoneCallback to simulate the request failed, wait
@@ -1020,8 +1022,8 @@
   // Put two requests on the queue - The first is user requested, and
   // the second is not user requested.
   AddRequest1();
-  offline_pages::SavePageRequest request2(kRequestId2, kUrl2, kClientId2,
-                                          base::Time::Now(), !kUserRequested);
+  offline_pages::SavePageRequest request2(
+      kRequestId2, kUrl2, kClientId2, OfflineClock()->Now(), !kUserRequested);
   queue()->AddRequest(request2,
                       base::BindOnce(&RequestCoordinatorTest::AddRequestDone,
                                      base::Unretained(this)));
@@ -1237,7 +1239,7 @@
        WatchdogTimeoutForScheduledProcessingNoLastSnapshot) {
   // Build a request to use with the pre-renderer, and put it on the queue.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   // Set request to allow one more completed attempt.
   int max_tries = coordinator()->policy()->GetMaxCompletedTries();
   request.set_completed_attempt_count(max_tries - 1);
@@ -1312,7 +1314,8 @@
   AddRequest1();
   // The second request will have a larger completed attempt count.
   offline_pages::SavePageRequest request2(kRequestId1 + 1, kUrl1, kClientId1,
-                                          base::Time::Now(), kUserRequested);
+                                          OfflineClock()->Now(),
+                                          kUserRequested);
   request2.set_completed_attempt_count(kAttemptCount);
   queue()->AddRequest(request2,
                       base::BindOnce(&RequestCoordinatorTest::AddRequestDone,
@@ -1584,7 +1587,7 @@
 TEST_F(RequestCoordinatorTest, SnapshotOnLastTryForScheduledProcessing) {
   // Build a request to use with the pre-renderer, and put it on the queue.
   offline_pages::SavePageRequest request(kRequestId1, kUrl1, kClientId1,
-                                         base::Time::Now(), kUserRequested);
+                                         OfflineClock()->Now(), kUserRequested);
   // Set request to allow one more completed attempt. So that the next try would
   // be the last retry.
   int max_tries = coordinator()->policy()->GetMaxCompletedTries();
diff --git a/components/offline_pages/core/background/request_queue_store_unittest.cc b/components/offline_pages/core/background/request_queue_store_unittest.cc
index 3f5d328..c448a6d 100644
--- a/components/offline_pages/core/background/request_queue_store_unittest.cc
+++ b/components/offline_pages/core/background/request_queue_store_unittest.cc
@@ -13,8 +13,10 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/request_queue.h"
 #include "components/offline_pages/core/background/save_page_request.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "sql/database.h"
 #include "sql/statement.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -409,7 +411,7 @@
   std::unique_ptr<RequestQueueStore> store(this->BuildStore());
   this->InitializeStore(store.get());
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
                            kUserRequested);
   store->AddRequest(request1,
@@ -470,7 +472,7 @@
   std::unique_ptr<RequestQueueStore> store(this->BuildStore());
   this->InitializeStore(store.get());
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   request.set_original_url(kUrl2);
@@ -532,7 +534,7 @@
   std::unique_ptr<RequestQueueStore> store(this->BuildStore());
   this->InitializeStore(store.get());
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
                                    kUserRequested);
   store->AddRequest(original_request,
@@ -588,7 +590,7 @@
   std::unique_ptr<RequestQueueStore> store(this->BuildStore());
   this->InitializeStore(store.get());
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
                            kUserRequested);
   store->AddRequest(request1,
@@ -652,7 +654,7 @@
   std::unique_ptr<RequestQueueStore> store(this->BuildStore());
   this->InitializeStore(store.get());
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
                                    kUserRequested);
   store->AddRequest(original_request,
@@ -682,7 +684,7 @@
   std::unique_ptr<RequestQueueStore> store(BuildStore());
   this->InitializeStore(store.get());
 
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
                                    kUserRequested);
   store->AddRequest(original_request,
diff --git a/components/offline_pages/core/background/request_queue_unittest.cc b/components/offline_pages/core/background/request_queue_unittest.cc
index 0ea44f9..c07dd3d 100644
--- a/components/offline_pages/core/background/request_queue_unittest.cc
+++ b/components/offline_pages/core/background/request_queue_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "components/offline_pages/core/background/device_conditions.h"
 #include "components/offline_pages/core/background/offliner_policy.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
@@ -18,6 +19,7 @@
 #include "components/offline_pages/core/background/request_queue_store.h"
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/background/test_request_queue_store.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -208,7 +210,7 @@
 }
 
 TEST_F(RequestQueueTest, AddRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   queue()->AddRequest(request, base::BindOnce(&RequestQueueTest::AddRequestDone,
@@ -226,7 +228,7 @@
 }
 
 TEST_F(RequestQueueTest, RemoveRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   queue()->AddRequest(request, base::BindOnce(&RequestQueueTest::AddRequestDone,
@@ -254,7 +256,7 @@
 }
 
 TEST_F(RequestQueueTest, RemoveSeveralRequests) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   queue()->AddRequest(request, base::BindOnce(&RequestQueueTest::AddRequestDone,
@@ -302,7 +304,7 @@
 }
 
 TEST_F(RequestQueueTest, PauseAndResume) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   queue()->AddRequest(request, base::BindOnce(&RequestQueueTest::AddRequestDone,
@@ -371,7 +373,7 @@
 // A longer test populating the request queue with more than one item, properly
 // listing multiple items and removing the right item.
 TEST_F(RequestQueueTest, MultipleRequestsAddGetRemove) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
                            kUserRequested);
   queue()->AddRequest(request1,
@@ -414,14 +416,14 @@
 
 TEST_F(RequestQueueTest, MarkAttemptStarted) {
   // First add a request.  Retry count will be set to 0.
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   queue()->AddRequest(request, base::BindOnce(&RequestQueueTest::AddRequestDone,
                                               base::Unretained(this)));
   PumpLoop();
 
-  base::Time before_time = base::Time::Now();
+  base::Time before_time = OfflineClock()->Now();
   // Update the request, ensure it succeeded.
   queue()->MarkAttemptStarted(
       kRequestId, base::BindOnce(&RequestQueueTest::UpdateRequestsDone,
@@ -434,7 +436,7 @@
   EXPECT_EQ(1UL, update_requests_result()->updated_items.size());
   EXPECT_LE(before_time,
             update_requests_result()->updated_items.at(0).last_attempt_time());
-  EXPECT_GE(base::Time::Now(),
+  EXPECT_GE(OfflineClock()->Now(),
             update_requests_result()->updated_items.at(0).last_attempt_time());
   EXPECT_EQ(
       1, update_requests_result()->updated_items.at(0).started_attempt_count());
@@ -452,7 +454,7 @@
 
 TEST_F(RequestQueueTest, MarkAttempStartedRequestNotPresent) {
   // First add a request.  Retry count will be set to 0.
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   // This request is never put into the queue.
   SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
                            kUserRequested);
@@ -469,7 +471,7 @@
 }
 
 TEST_F(RequestQueueTest, MarkAttemptAborted) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   queue()->AddRequest(request, base::BindOnce(&RequestQueueTest::AddRequestDone,
@@ -500,7 +502,7 @@
 
 TEST_F(RequestQueueTest, MarkAttemptAbortedRequestNotPresent) {
   // First add a request.  Retry count will be set to 0.
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   // This request is never put into the queue.
   SavePageRequest request1(kRequestId, kUrl, kClientId, creation_time,
                            kUserRequested);
@@ -517,7 +519,7 @@
 }
 
 TEST_F(RequestQueueTest, MarkAttemptCompleted) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   queue()->AddRequest(request, base::BindOnce(&RequestQueueTest::AddRequestDone,
@@ -550,7 +552,8 @@
 TEST_F(RequestQueueTest, CleanStaleRequests) {
   // Create a request that is already expired.
   base::Time creation_time =
-      base::Time::Now() - base::TimeDelta::FromSeconds(2 * kOneWeekInSeconds);
+      OfflineClock()->Now() -
+      base::TimeDelta::FromSeconds(2 * kOneWeekInSeconds);
 
   SavePageRequest original_request(kRequestId, kUrl, kClientId, creation_time,
                                    kUserRequested);
diff --git a/components/offline_pages/core/background/save_page_request_unittest.cc b/components/offline_pages/core/background/save_page_request_unittest.cc
index ab7e722..e285317 100644
--- a/components/offline_pages/core/background/save_page_request_unittest.cc
+++ b/components/offline_pages/core/background/save_page_request_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/offline_pages/core/background/save_page_request.h"
 
+#include "base/time/clock.h"
+#include "components/offline_pages/core/offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -25,7 +27,7 @@
 SavePageRequestTest::~SavePageRequestTest() {}
 
 TEST_F(SavePageRequestTest, CreatePendingReqeust) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   request.set_original_url(kUrl2);
@@ -43,7 +45,7 @@
 }
 
 TEST_F(SavePageRequestTest, StartAndCompleteRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
   request.set_request_origin(kRequestOrigin);
@@ -77,7 +79,7 @@
 }
 
 TEST_F(SavePageRequestTest, StartAndAbortRequest) {
-  base::Time creation_time = base::Time::Now();
+  base::Time creation_time = OfflineClock()->Now();
   SavePageRequest request(kRequestId, kUrl, kClientId, creation_time,
                           kUserRequested);
 
diff --git a/components/offline_pages/core/prefetch/prefetch_item.h b/components/offline_pages/core/prefetch/prefetch_item.h
index 27e244b..b5ae3dba 100644
--- a/components/offline_pages/core/prefetch/prefetch_item.h
+++ b/components/offline_pages/core/prefetch/prefetch_item.h
@@ -103,8 +103,10 @@
   // item. It holds a negative value otherwise.
   int64_t archive_body_length = -1;
 
-  // Time when this item was inserted into the store with the URL to be
-  // prefetched.
+  // The last time the URL was attempted to be added to the store. Normally this
+  // is just the time the item was added. If the same URL is added multiple
+  // times, this is the timestamp of the last time. creation_time
+  // is used as a proxy for priority.
   base::Time creation_time;
 
   // Time used for the expiration of the item depending on the applicable policy
diff --git a/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.cc b/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.cc
index ef51030..942183a 100644
--- a/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.cc
+++ b/components/offline_pages/core/prefetch/tasks/add_unique_urls_task.cc
@@ -31,22 +31,26 @@
 using Result = AddUniqueUrlsTask::Result;
 
 namespace {
+struct ItemInfo {
+  int64_t offline_id;
+  PrefetchItemState state;
+};
 
-std::map<std::string, std::pair<int64_t, PrefetchItemState>>
-FindExistingPrefetchItemsInNamespaceSync(sql::Database* db,
-                                         const std::string& name_space) {
+std::map<std::string, ItemInfo> FindExistingPrefetchItemsInNamespaceSync(
+    sql::Database* db,
+    const std::string& name_space) {
   static const char kSql[] =
       "SELECT offline_id, state, requested_url FROM prefetch_items"
       " WHERE client_namespace = ?";
   sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
   statement.BindString(0, name_space);
 
-  std::map<std::string, std::pair<int64_t, PrefetchItemState>> result;
+  std::map<std::string, ItemInfo> result;
   while (statement.Step()) {
-    result.emplace(
-        statement.ColumnString(2),
-        std::make_pair(statement.ColumnInt64(0),
-                       static_cast<PrefetchItemState>(statement.ColumnInt(1))));
+    result.emplace(statement.ColumnString(2),
+                   ItemInfo{.offline_id = statement.ColumnInt64(0),
+                            .state = static_cast<PrefetchItemState>(
+                                statement.ColumnInt(1))});
   }
 
   return result;
@@ -75,6 +79,22 @@
   return statement.Run();
 }
 
+bool UpdateItemTimeSync(sql::Database* db,
+                        int64_t offline_id,
+                        base::Time now_db_time) {
+  static const char kSql[] =
+      "UPDATE prefetch_items SET"
+      " freshness_time=?,creation_time=?"
+      " WHERE offline_id=?";
+
+  sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
+  statement.BindInt64(0, store_utils::ToDatabaseTime(now_db_time));
+  statement.BindInt64(1, store_utils::ToDatabaseTime(now_db_time));
+  statement.BindInt64(2, offline_id);
+
+  return statement.Run();
+}
+
 // Adds new prefetch item entries to the store using the URLs and client IDs
 // from |candidate_prefetch_urls| and the client's |name_space|. Also cleans up
 // entries in the Zombie state from the client's |name_space| except for the
@@ -88,7 +108,7 @@
   if (!transaction.Begin())
     return Result::STORE_ERROR;
 
-  std::map<std::string, std::pair<int64_t, PrefetchItemState>> existing_items =
+  std::map<std::string, ItemInfo> existing_items =
       FindExistingPrefetchItemsInNamespaceSync(db, name_space);
 
   int added_row_count = 0;
@@ -104,23 +124,26 @@
                                   store_utils::ToDatabaseTime(now)))
         return Result::STORE_ERROR;  // Transaction rollback.
       added_row_count++;
-
-      // We artificially add a microsecond to ensure that the timestamp is
-      // different (and guarantee a particular order when sorting by timestamp).
-      now += base::TimeDelta::FromMicroseconds(1);
     } else {
+      // The existing item is still being suggested, update its timestamp (and
+      // therefore priority).
+      if (!UpdateItemTimeSync(db, iter->second.offline_id, now))
+        return Result::STORE_ERROR;  // Transaction rollback.
       // Removing from the list of existing items if it was requested again, to
       // prevent it from being removed in the next step.
       existing_items.erase(iter);
     }
+    // We artificially add a microsecond to ensure that the timestamp is
+    // different (and guarantee a particular order when sorting by timestamp).
+    now += base::TimeDelta::FromMicroseconds(1);
   }
 
   // Purge remaining zombie IDs.
   for (const auto& existing_item : existing_items) {
-    if (existing_item.second.second != PrefetchItemState::ZOMBIE)
+    if (existing_item.second.state != PrefetchItemState::ZOMBIE)
       continue;
     if (!PrefetchStoreUtils::DeletePrefetchItemByOfflineIdSync(
-            db, existing_item.second.first)) {
+            db, existing_item.second.offline_id)) {
       return Result::STORE_ERROR;  // Transaction rollback.
     }
   }
diff --git a/components/offline_pages/core/prefetch/tasks/add_unique_urls_task_unittest.cc b/components/offline_pages/core/prefetch/tasks/add_unique_urls_task_unittest.cc
index 750814e8..17fb480 100644
--- a/components/offline_pages/core/prefetch/tasks/add_unique_urls_task_unittest.cc
+++ b/components/offline_pages/core/prefetch/tasks/add_unique_urls_task_unittest.cc
@@ -12,12 +12,14 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/offline_pages/core/offline_store_utils.h"
 #include "components/offline_pages/core/prefetch/prefetch_item.h"
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
 #include "components/offline_pages/core/prefetch/store/prefetch_store.h"
 #include "components/offline_pages/core/prefetch/store/prefetch_store_test_util.h"
 #include "components/offline_pages/core/prefetch/tasks/prefetch_task_test_base.h"
 #include "components/offline_pages/core/prefetch/test_prefetch_dispatcher.h"
+#include "components/offline_pages/core/test_scoped_offline_clock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace offline_pages {
@@ -39,11 +41,19 @@
 
 class AddUniqueUrlsTaskTest : public PrefetchTaskTestBase {
  public:
-  AddUniqueUrlsTaskTest();
+  AddUniqueUrlsTaskTest() = default;
   ~AddUniqueUrlsTaskTest() override = default;
 
   // Returns all items stored in a map keyed with client id.
-  std::map<std::string, PrefetchItem> GetAllItems();
+  std::map<std::string, PrefetchItem> GetAllItems() {
+    std::set<PrefetchItem> set;
+    store_util()->GetAllItems(&set);
+
+    std::map<std::string, PrefetchItem> map;
+    for (const auto& item : set)
+      map[item.client_id.id] = item;
+    return map;
+  }
 
   TestPrefetchDispatcher* dispatcher() { return &dispatcher_; }
 
@@ -51,17 +61,6 @@
   TestPrefetchDispatcher dispatcher_;
 };
 
-AddUniqueUrlsTaskTest::AddUniqueUrlsTaskTest() {}
-
-std::map<std::string, PrefetchItem> AddUniqueUrlsTaskTest::GetAllItems() {
-  std::set<PrefetchItem> set;
-  store_util()->GetAllItems(&set);
-
-  std::map<std::string, PrefetchItem> map;
-  for (const auto& item : set)
-    map[item.client_id.id] = item;
-  return map;
-}
 
 TEST_F(AddUniqueUrlsTaskTest, StoreFailure) {
   store_util()->SimulateInitializationError();
@@ -113,17 +112,24 @@
 }
 
 TEST_F(AddUniqueUrlsTaskTest, DontAddURLIfItExists) {
+  TestScopedOfflineClock clock;
   std::vector<PrefetchURL> urls;
   urls.push_back(PrefetchURL{kClientId1, kTestURL1, kTestTitle1});
   urls.push_back(PrefetchURL{kClientId2, kTestURL2, kTestTitle2});
   RunTask(std::make_unique<AddUniqueUrlsTask>(dispatcher(), store(),
                                               kTestNamespace, urls));
   EXPECT_EQ(1, dispatcher()->task_schedule_count);
+  std::map<std::string, PrefetchItem> items_before = GetAllItems();
 
-  urls.clear();
-  // This PrefetchURL has a duplicate URL, should not be added.
-  urls.push_back(PrefetchURL{kClientId4, kTestURL1, kTestTitle4});
-  urls.push_back(PrefetchURL{kClientId3, kTestURL3, kTestTitle3});
+  // Advance time to verify that timestamp of kClientId1 is updated on the next
+  // task execution.
+  clock.Advance(base::TimeDelta::FromSeconds(1));
+
+  urls = {
+      // This PrefetchURL has a duplicate URL, should not be added.
+      {kClientId4, kTestURL1, kTestTitle4},
+      {kClientId3, kTestURL3, kTestTitle3},
+  };
 
   RunTask(std::make_unique<AddUniqueUrlsTask>(dispatcher(), store(),
                                               kTestNamespace, urls));
@@ -143,6 +149,12 @@
   EXPECT_EQ(kTestURL3, items[kClientId3].url);
   EXPECT_EQ(kTestNamespace, items[kClientId3].client_id.name_space);
   EXPECT_EQ(kTestTitle3, items[kClientId3].title);
+
+  // Although kClientId4 was not inserted, it should have resulted in updating
+  // kClientId1's timestamp.
+  EXPECT_GT(items[kClientId1].creation_time,
+            items_before[kClientId1].creation_time);
+  EXPECT_EQ(items[kClientId1].creation_time, items[kClientId1].freshness_time);
 }
 
 TEST_F(AddUniqueUrlsTaskTest, HandleZombiePrefetchItems) {
@@ -158,10 +170,11 @@
   EXPECT_EQ(1, store_util()->ZombifyPrefetchItems(kTestNamespace, urls[0].url));
   EXPECT_EQ(1, store_util()->ZombifyPrefetchItems(kTestNamespace, urls[1].url));
 
-  urls.clear();
-  urls.push_back(PrefetchURL{kClientId1, kTestURL1, kTestTitle1});
-  urls.push_back(PrefetchURL{kClientId3, kTestURL3, kTestTitle3});
-  urls.push_back(PrefetchURL{kClientId4, kTestURL4, kTestTitle4});
+  urls = {
+      {kClientId1, kTestURL1, kTestTitle1},
+      {kClientId3, kTestURL3, kTestTitle3},
+      {kClientId4, kTestURL4, kTestTitle4},
+  };
   // ID-1 is expected to stay in zombie state.
   // ID-2 is expected to be removed, because it is in zombie state.
   // ID-3 is still requested, so it is ignored.
diff --git a/components/previews/content/previews_decider_impl.cc b/components/previews/content/previews_decider_impl.cc
index 936f4cfc..6332c7bb 100644
--- a/components/previews/content/previews_decider_impl.cc
+++ b/components/previews/content/previews_decider_impl.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
@@ -30,6 +31,8 @@
 
 void LogPreviewsEligibilityReason(PreviewsEligibilityReason status,
                                   PreviewsType type) {
+  UMA_HISTOGRAM_ENUMERATION("Previews.EligibilityReason", status,
+                            PreviewsEligibilityReason::LAST);
   int32_t max_limit = static_cast<int32_t>(PreviewsEligibilityReason::LAST);
   base::LinearHistogram::FactoryGet(
       base::StringPrintf("Previews.EligibilityReason.%s",
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc
index 5d25b9d..caff62a 100644
--- a/components/previews/content/previews_decider_impl_unittest.cc
+++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -445,6 +445,10 @@
       &user_data, GURL("https://www.google.com"), false,
       PreviewsType::OFFLINE));
   histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason",
+      static_cast<int>(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED),
+      1);
+  histogram_tester.ExpectBucketCount(
       "Previews.EligibilityReason.Offline",
       static_cast<int>(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED),
       1);
@@ -465,6 +469,10 @@
       "Previews.EligibilityReason.Offline",
       static_cast<int>(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED),
       2);
+  histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason",
+      static_cast<int>(PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED),
+      2);
   histogram_tester.ExpectTotalCount("Previews.EligibilityReason.NoScript", 0);
 
   variations::testing::ClearAllVariationParams();
@@ -507,6 +515,10 @@
       "Previews.EligibilityReason.Offline",
       static_cast<int>(PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE),
       1);
+  histogram_tester.ExpectUniqueSample(
+      "Previews.EligibilityReason",
+      static_cast<int>(PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE),
+      1);
 }
 
 TEST_F(PreviewsDeciderImplTest, TestAllowLitePageWhenNetworkQualityFast) {
@@ -557,6 +569,9 @@
   EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
       &user_data, GURL("https://www.google.com"), true, PreviewsType::OFFLINE));
   histogram_tester.ExpectUniqueSample(
+      "Previews.EligibilityReason",
+      static_cast<int>(PreviewsEligibilityReason::RELOAD_DISALLOWED), 1);
+  histogram_tester.ExpectUniqueSample(
       "Previews.EligibilityReason.Offline",
       static_cast<int>(PreviewsEligibilityReason::RELOAD_DISALLOWED), 1);
 }
@@ -901,6 +916,9 @@
       PreviewsType::LITE_PAGE_REDIRECT));
 
   histogram_tester.ExpectUniqueSample(
+      "Previews.EligibilityReason",
+      static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
+  histogram_tester.ExpectUniqueSample(
       "Previews.EligibilityReason.LitePageRedirect",
       static_cast<int>(PreviewsEligibilityReason::ALLOWED), 1);
 }
@@ -931,6 +949,10 @@
       PreviewsType::LITE_PAGE_REDIRECT));
 
   histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason",
+      static_cast<int>(PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER),
+      1);
+  histogram_tester.ExpectBucketCount(
       "Previews.EligibilityReason.LitePageRedirect",
       static_cast<int>(PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER),
       1);
@@ -963,6 +985,11 @@
       &user_data, GURL("https://whitelisted.example.com"), false,
       PreviewsType::RESOURCE_LOADING_HINTS));
   histogram_tester.ExpectUniqueSample(
+      "Previews.EligibilityReason",
+      static_cast<int>(
+          PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
+      1);
+  histogram_tester.ExpectUniqueSample(
       "Previews.EligibilityReason.ResourceLoadingHints",
       static_cast<int>(
           PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER),
diff --git a/components/previews/core/previews_black_list.cc b/components/previews/core/previews_black_list.cc
index d718e84..5fbc7a0 100644
--- a/components/previews/core/previews_black_list.cc
+++ b/components/previews/core/previews_black_list.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/clock.h"
@@ -95,7 +96,7 @@
                                                    bool opt_out,
                                                    PreviewsType type) {
   DCHECK(url.has_host());
-
+  UMA_HISTOGRAM_BOOLEAN("Previews.OptOut.UserOptedOut", opt_out);
   base::BooleanHistogram::FactoryGet(
       base::StringPrintf("Previews.OptOut.UserOptedOut.%s",
                          GetStringNameForType(type).c_str()),
diff --git a/components/previews/core/previews_black_list_unittest.cc b/components/previews/core/previews_black_list_unittest.cc
index 53909cf..28265e4f 100644
--- a/components/previews/core/previews_black_list_unittest.cc
+++ b/components/previews/core/previews_black_list_unittest.cc
@@ -177,9 +177,11 @@
   black_list_->AddPreviewNavigation(url, false, PreviewsType::OFFLINE);
   histogram_tester.ExpectUniqueSample("Previews.OptOut.UserOptedOut.Offline", 0,
                                       1);
+  histogram_tester.ExpectUniqueSample("Previews.OptOut.UserOptedOut", 0, 1);
   black_list_->AddPreviewNavigation(url, true, PreviewsType::OFFLINE);
   histogram_tester.ExpectBucketCount("Previews.OptOut.UserOptedOut.Offline", 1,
                                      1);
+  histogram_tester.ExpectBucketCount("Previews.OptOut.UserOptedOut", 1, 1);
 }
 
 TEST_F(PreviewsBlackListTest, SessionParams) {
diff --git a/components/renderer_context_menu/context_menu_content_type.h b/components/renderer_context_menu/context_menu_content_type.h
index 0cce18f..aef21b4 100644
--- a/components/renderer_context_menu/context_menu_content_type.h
+++ b/components/renderer_context_menu/context_menu_content_type.h
@@ -67,7 +67,7 @@
  protected:
   const content::ContextMenuParams& params() const { return params_; }
 
-  const content::WebContents* source_web_contents() const {
+  content::WebContents* source_web_contents() const {
     return source_web_contents_;
   }
 
diff --git a/components/safe_browsing/base_ui_manager.cc b/components/safe_browsing/base_ui_manager.cc
index 07506ea9..6c2d4f8 100644
--- a/components/safe_browsing/base_ui_manager.cc
+++ b/components/safe_browsing/base_ui_manager.cc
@@ -258,7 +258,7 @@
 // users who are not in incognito mode.
 void BaseUIManager::MaybeReportSafeBrowsingHit(
     const HitReport& hit_report,
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return;
 }
diff --git a/components/safe_browsing/base_ui_manager.h b/components/safe_browsing/base_ui_manager.h
index b349f00..2321da8a 100644
--- a/components/safe_browsing/base_ui_manager.h
+++ b/components/safe_browsing/base_ui_manager.h
@@ -52,7 +52,7 @@
   // report if the user has enabled SBER and is not currently in incognito mode.
   virtual void MaybeReportSafeBrowsingHit(
       const safe_browsing::HitReport& hit_report,
-      const content::WebContents* web_contents);
+      content::WebContents* web_contents);
 
   // A convenience wrapper method for IsUrlWhitelistedOrPendingForWebContents.
   virtual bool IsWhitelisted(const UnsafeResource& resource);
diff --git a/components/safe_browsing/triggers/ad_sampler_trigger.cc b/components/safe_browsing/triggers/ad_sampler_trigger.cc
index 0524324..90cba93 100644
--- a/components/safe_browsing/triggers/ad_sampler_trigger.cc
+++ b/components/safe_browsing/triggers/ad_sampler_trigger.cc
@@ -166,7 +166,7 @@
 
 void AdSamplerTrigger::CreateAdSampleReport() {
   SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, *web_contents());
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
 
   security_interstitials::UnsafeResource resource;
   resource.threat_type = SB_THREAT_TYPE_AD_SAMPLE;
diff --git a/components/safe_browsing/triggers/suspicious_site_trigger.cc b/components/safe_browsing/triggers/suspicious_site_trigger.cc
index 0f225c8..5b5cd23d 100644
--- a/components/safe_browsing/triggers/suspicious_site_trigger.cc
+++ b/components/safe_browsing/triggers/suspicious_site_trigger.cc
@@ -89,7 +89,7 @@
 
 bool SuspiciousSiteTrigger::MaybeStartReport() {
   SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, *web_contents());
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
 
   security_interstitials::UnsafeResource resource;
   resource.threat_type = SB_THREAT_TYPE_SUSPICIOUS_SITE;
@@ -124,7 +124,7 @@
 
 void SuspiciousSiteTrigger::FinishReport() {
   SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, *web_contents());
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
   if (trigger_manager_->FinishCollectingThreatDetails(
           TriggerType::SUSPICIOUS_SITE, web_contents(), base::TimeDelta(),
           /*did_proceed=*/false, /*num_visits=*/0, error_options)) {
@@ -139,7 +139,7 @@
 void SuspiciousSiteTrigger::SuspiciousSiteDetectedWhenMonitoring() {
   DCHECK_EQ(TriggerState::MONITOR_MODE, current_state_);
   SBErrorOptions error_options =
-      TriggerManager::GetSBErrorDisplayOptions(*prefs_, *web_contents());
+      TriggerManager::GetSBErrorDisplayOptions(*prefs_, web_contents());
   TriggerManagerReason reason;
   if (trigger_manager_->CanStartDataCollectionWithReason(
           error_options, TriggerType::SUSPICIOUS_SITE, &reason) ||
diff --git a/components/safe_browsing/triggers/trigger_manager.cc b/components/safe_browsing/triggers/trigger_manager.cc
index 5d38ce8..d6a0bc9 100644
--- a/components/safe_browsing/triggers/trigger_manager.cc
+++ b/components/safe_browsing/triggers/trigger_manager.cc
@@ -74,10 +74,10 @@
 // static
 SBErrorOptions TriggerManager::GetSBErrorDisplayOptions(
     const PrefService& pref_service,
-    const content::WebContents& web_contents) {
+    content::WebContents* web_contents) {
   return SBErrorOptions(/*is_main_frame_load_blocked=*/false,
                         IsExtendedReportingOptInAllowed(pref_service),
-                        web_contents.GetBrowserContext()->IsOffTheRecord(),
+                        web_contents->GetBrowserContext()->IsOffTheRecord(),
                         IsExtendedReportingEnabled(pref_service),
                         IsExtendedReportingPolicyManaged(pref_service),
                         /*is_proceed_anyway_disabled=*/false,
diff --git a/components/safe_browsing/triggers/trigger_manager.h b/components/safe_browsing/triggers/trigger_manager.h
index 1a62749..db26345 100644
--- a/components/safe_browsing/triggers/trigger_manager.h
+++ b/components/safe_browsing/triggers/trigger_manager.h
@@ -96,7 +96,7 @@
   // |pref_service|. Only the fields needed by TriggerManager will be set.
   static SBErrorOptions GetSBErrorDisplayOptions(
       const PrefService& pref_service,
-      const content::WebContents& web_contents);
+      content::WebContents* web_contents);
 
   // Returns whether data collection can be started for the |trigger_type| based
   // on the settings specified in |error_display_options| as well as quota.
diff --git a/components/safe_browsing/triggers/trigger_manager_unittest.cc b/components/safe_browsing/triggers/trigger_manager_unittest.cc
index 3b03d92..abac318 100644
--- a/components/safe_browsing/triggers/trigger_manager_unittest.cc
+++ b/components/safe_browsing/triggers/trigger_manager_unittest.cc
@@ -113,7 +113,7 @@
   bool StartCollectingThreatDetails(const TriggerType trigger_type,
                                     content::WebContents* web_contents) {
     SBErrorOptions options =
-        TriggerManager::GetSBErrorDisplayOptions(pref_service_, *web_contents);
+        TriggerManager::GetSBErrorDisplayOptions(pref_service_, web_contents);
     return trigger_manager_.StartCollectingThreatDetails(
         trigger_type, web_contents, security_interstitials::UnsafeResource(),
         nullptr, nullptr, options);
@@ -129,7 +129,7 @@
       EXPECT_CALL(*threat_details, FinishCollection(_, _)).Times(1);
     }
     SBErrorOptions options =
-        TriggerManager::GetSBErrorDisplayOptions(pref_service_, *web_contents);
+        TriggerManager::GetSBErrorDisplayOptions(pref_service_, web_contents);
     bool result = trigger_manager_.FinishCollectingThreatDetails(
         trigger_type, web_contents, base::TimeDelta(), false, 0, options);
 
diff --git a/components/sessions/content/content_live_tab.cc b/components/sessions/content/content_live_tab.cc
index 12a86003..d9d566f 100644
--- a/components/sessions/content/content_live_tab.cc
+++ b/components/sessions/content/content_live_tab.cc
@@ -64,7 +64,7 @@
       web_contents());
 }
 
-const std::string& ContentLiveTab::GetUserAgentOverride() const {
+const std::string& ContentLiveTab::GetUserAgentOverride() {
   return web_contents()->GetUserAgentOverride();
 }
 
diff --git a/components/sessions/content/content_live_tab.h b/components/sessions/content/content_live_tab.h
index 5c04ba1..c4ff024 100644
--- a/components/sessions/content/content_live_tab.h
+++ b/components/sessions/content/content_live_tab.h
@@ -41,7 +41,7 @@
   int GetEntryCount() override;
   std::unique_ptr<PlatformSpecificTabData> GetPlatformSpecificTabData()
       override;
-  const std::string& GetUserAgentOverride() const override;
+  const std::string& GetUserAgentOverride() override;
 
   content::WebContents* web_contents() { return web_contents_; }
   const content::WebContents* web_contents() const { return web_contents_; }
diff --git a/components/sessions/core/live_tab.h b/components/sessions/core/live_tab.h
index 4902199..1ea71f1 100644
--- a/components/sessions/core/live_tab.h
+++ b/components/sessions/core/live_tab.h
@@ -33,7 +33,7 @@
   virtual std::unique_ptr<PlatformSpecificTabData> GetPlatformSpecificTabData();
 
   // Returns the user agent override, if any.
-  virtual const std::string& GetUserAgentOverride() const = 0;
+  virtual const std::string& GetUserAgentOverride() = 0;
 };
 
 }  // namespace sessions
diff --git a/components/sessions/ios/ios_live_tab.h b/components/sessions/ios/ios_live_tab.h
index 17b6fe54..14f3d89 100644
--- a/components/sessions/ios/ios_live_tab.h
+++ b/components/sessions/ios/ios_live_tab.h
@@ -35,7 +35,7 @@
   sessions::SerializedNavigationEntry GetEntryAtIndex(int index) override;
   sessions::SerializedNavigationEntry GetPendingEntry() override;
   int GetEntryCount() override;
-  const std::string& GetUserAgentOverride() const override;
+  const std::string& GetUserAgentOverride() override;
 
   web::WebState* web_state() { return web_state_; }
   const web::WebState* web_state() const { return web_state_; }
diff --git a/components/sessions/ios/ios_live_tab.mm b/components/sessions/ios/ios_live_tab.mm
index 0d0b1c90..9a50135 100644
--- a/components/sessions/ios/ios_live_tab.mm
+++ b/components/sessions/ios/ios_live_tab.mm
@@ -55,7 +55,7 @@
   return navigation_manager()->GetItemCount();
 }
 
-const std::string& IOSLiveTab::GetUserAgentOverride() const {
+const std::string& IOSLiveTab::GetUserAgentOverride() {
   // Dynamic user agent overrides are not supported on iOS.
   return user_agent_override_;
 }
diff --git a/components/zoom/zoom_controller.cc b/components/zoom/zoom_controller.cc
index 94fc93bd..20ce115 100644
--- a/components/zoom/zoom_controller.cc
+++ b/components/zoom/zoom_controller.cc
@@ -24,7 +24,7 @@
 namespace zoom {
 
 double ZoomController::GetZoomLevelForWebContents(
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   if (!web_contents)
     return 0.0;
 
diff --git a/components/zoom/zoom_controller.h b/components/zoom/zoom_controller.h
index 3aa98a5..7c5318f 100644
--- a/components/zoom/zoom_controller.h
+++ b/components/zoom/zoom_controller.h
@@ -91,8 +91,7 @@
   // Since it's possible for a WebContents to not have a ZoomController, provide
   // a simple, safe and reliable method to find the current zoom level for a
   // given WebContents*.
-  static double GetZoomLevelForWebContents(
-      const content::WebContents* web_contents);
+  static double GetZoomLevelForWebContents(content::WebContents* web_contents);
 
   ~ZoomController() override;
 
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 6df8fd81..bce899c 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -124,6 +124,7 @@
 #include "media/mojo/buildflags.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
+#include "mojo/public/cpp/bindings/mojo_buildflags.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
 #include "net/base/network_change_notifier.h"
 #include "net/socket/client_socket_factory.h"
@@ -244,6 +245,10 @@
 #include "base/mac/foundation_util.h"
 #endif
 
+#if BUILDFLAG(MOJO_RANDOM_DELAYS_ENABLED)
+#include "mojo/public/cpp/bindings/lib/test_random_mojo_delays.h"
+#endif
+
 // One of the linux specific headers defines this as a macro.
 #ifdef DestroyAll
 #undef DestroyAll
@@ -1595,6 +1600,10 @@
     parts_->ServiceManagerConnectionStarted(
         ServiceManagerConnection::GetForProcess());
   }
+
+#if BUILDFLAG(MOJO_RANDOM_DELAYS_ENABLED)
+  mojo::BeginRandomMojoDelays();
+#endif
 }
 
 base::FilePath BrowserMainLoop::GetStartupTraceFileName() const {
diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc
index babcf10..a97648e 100644
--- a/content/browser/frame_host/interstitial_page_impl.cc
+++ b/content/browser/frame_host/interstitial_page_impl.cc
@@ -425,7 +425,7 @@
   return this;
 }
 
-ui::AXMode InterstitialPageImpl::GetAccessibilityMode() const {
+ui::AXMode InterstitialPageImpl::GetAccessibilityMode() {
   if (web_contents_)
     return static_cast<WebContentsImpl*>(web_contents_)->GetAccessibilityMode();
   else
@@ -488,7 +488,7 @@
   return web_contents();
 }
 
-const GURL& InterstitialPageImpl::GetMainFrameLastCommittedURL() const {
+const GURL& InterstitialPageImpl::GetMainFrameLastCommittedURL() {
   return url_;
 }
 
@@ -548,7 +548,7 @@
   return nullptr;
 }
 
-const std::string& InterstitialPageImpl::GetUserAgentOverride() const {
+const std::string& InterstitialPageImpl::GetUserAgentOverride() {
   return base::EmptyString();
 }
 
@@ -556,7 +556,7 @@
   return false;
 }
 
-bool InterstitialPageImpl::ShowingInterstitialPage() const {
+bool InterstitialPageImpl::ShowingInterstitialPage() {
   // An interstitial page never shows a second interstitial.
   return false;
 }
@@ -796,7 +796,7 @@
   }
 }
 
-Visibility InterstitialPageImpl::GetVisibility() const {
+Visibility InterstitialPageImpl::GetVisibility() {
   // Interstitials always occlude the underlying web content.
   return Visibility::OCCLUDED;
 }
diff --git a/content/browser/frame_host/interstitial_page_impl.h b/content/browser/frame_host/interstitial_page_impl.h
index 486a40e..c3c81b8 100644
--- a/content/browser/frame_host/interstitial_page_impl.h
+++ b/content/browser/frame_host/interstitial_page_impl.h
@@ -99,9 +99,9 @@
 
   // NavigatorDelegate implementation.
   WebContents* OpenURL(const OpenURLParams& params) override;
-  const std::string& GetUserAgentOverride() const override;
+  const std::string& GetUserAgentOverride() override;
   bool ShouldOverrideUserAgentInNewTabs() override;
-  bool ShowingInterstitialPage() const override;
+  bool ShowingInterstitialPage() override;
 
  protected:
   // NotificationObserver method:
@@ -117,7 +117,7 @@
                    const base::string16& title,
                    base::i18n::TextDirection title_direction) override;
   InterstitialPage* GetAsInterstitialPage() override;
-  ui::AXMode GetAccessibilityMode() const override;
+  ui::AXMode GetAccessibilityMode() override;
   void ExecuteEditCommand(const std::string& command,
                           const base::Optional<base::string16>& value) override;
   void Cut() override;
@@ -137,7 +137,7 @@
                          const gfx::Rect& initial_rect,
                          bool user_gesture) override;
   void SetFocusedFrame(FrameTreeNode* node, SiteInstance* source) override;
-  Visibility GetVisibility() const override;
+  Visibility GetVisibility() override;
   void AudioContextPlaybackStarted(RenderFrameHost* host,
                                    int context_id) override;
   void AudioContextPlaybackStopped(RenderFrameHost* host,
@@ -147,7 +147,7 @@
   RenderViewHostDelegateView* GetDelegateView() override;
   bool OnMessageReceived(RenderViewHostImpl* render_view_host,
                          const IPC::Message& message) override;
-  const GURL& GetMainFrameLastCommittedURL() const override;
+  const GURL& GetMainFrameLastCommittedURL() override;
   void RenderViewTerminated(RenderViewHost* render_view_host,
                             base::TerminationStatus status,
                             int error_code) override;
diff --git a/content/browser/frame_host/navigation_controller_delegate.h b/content/browser/frame_host/navigation_controller_delegate.h
index 8a54d98..3a1e77dc 100644
--- a/content/browser/frame_host/navigation_controller_delegate.h
+++ b/content/browser/frame_host/navigation_controller_delegate.h
@@ -32,12 +32,12 @@
   virtual ~NavigationControllerDelegate() {}
 
   // Duplicates of WebContents methods.
-  virtual RenderViewHost* GetRenderViewHost() const = 0;
-  virtual InterstitialPage* GetInterstitialPage() const = 0;
-  virtual const std::string& GetContentsMimeType() const = 0;
+  virtual RenderViewHost* GetRenderViewHost() = 0;
+  virtual InterstitialPage* GetInterstitialPage() = 0;
+  virtual const std::string& GetContentsMimeType() = 0;
   virtual void NotifyNavigationStateChanged(InvalidateTypes changed_flags) = 0;
   virtual void Stop() = 0;
-  virtual bool IsBeingDestroyed() const = 0;
+  virtual bool IsBeingDestroyed() = 0;
   virtual bool CanOverscrollContent() const = 0;
 
   // Methods from WebContentsImpl that NavigationControllerImpl needs to
diff --git a/content/browser/frame_host/navigator_delegate.h b/content/browser/frame_host/navigator_delegate.h
index cc58d99..909f3dfd 100644
--- a/content/browser/frame_host/navigator_delegate.h
+++ b/content/browser/frame_host/navigator_delegate.h
@@ -96,7 +96,7 @@
   virtual bool ShouldPreserveAbortedURLs();
 
   // Returns the overriden user agent string if it's set.
-  virtual const std::string& GetUserAgentOverride() const = 0;
+  virtual const std::string& GetUserAgentOverride() = 0;
 
   // Returns whether we should override the user agent in new tabs, e.g., for
   // Android Webview's popup window when current entry.
@@ -132,7 +132,7 @@
 
   // Whether the delegate is displaying an interstitial page over the current
   // page.
-  virtual bool ShowingInterstitialPage() const = 0;
+  virtual bool ShowingInterstitialPage() = 0;
 };
 
 }  // namspace content
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index 61ceec5..a925637d 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -22,7 +22,7 @@
   return false;
 }
 
-const GURL& RenderFrameHostDelegate::GetMainFrameLastCommittedURL() const {
+const GURL& RenderFrameHostDelegate::GetMainFrameLastCommittedURL() {
   return GURL::EmptyGURL();
 }
 
@@ -79,7 +79,7 @@
   return std::string();
 }
 
-ui::AXMode RenderFrameHostDelegate::GetAccessibilityMode() const {
+ui::AXMode RenderFrameHostDelegate::GetAccessibilityMode() {
   return ui::AXMode();
 }
 
@@ -133,11 +133,11 @@
 }
 #endif
 
-bool RenderFrameHostDelegate::IsBeingDestroyed() const {
+bool RenderFrameHostDelegate::IsBeingDestroyed() {
   return false;
 }
 
-Visibility RenderFrameHostDelegate::GetVisibility() const {
+Visibility RenderFrameHostDelegate::GetVisibility() {
   return Visibility::HIDDEN;
 }
 
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index b417e7b..e54342a 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -107,7 +107,7 @@
 
   // Gets the last committed URL. See WebContents::GetLastCommittedURL for a
   // description of the semantics.
-  virtual const GURL& GetMainFrameLastCommittedURL() const;
+  virtual const GURL& GetMainFrameLastCommittedURL();
 
   // A message was added to to the console.
   virtual bool DidAddMessageToConsole(int32_t level,
@@ -215,7 +215,7 @@
   virtual std::string GetDefaultMediaDeviceID(MediaStreamType type);
 
   // Get the accessibility mode for the WebContents that owns this frame.
-  virtual ui::AXMode GetAccessibilityMode() const;
+  virtual ui::AXMode GetAccessibilityMode();
 
   // Called when accessibility events or location changes are received
   // from a render frame, when the accessibility mode has the
@@ -383,7 +383,7 @@
 
   // Whether the delegate is being destroyed, in which case the RenderFrameHost
   // should not be asked to create a RenderFrame.
-  virtual bool IsBeingDestroyed() const;
+  virtual bool IsBeingDestroyed();
 
   // Notifies that the render frame started loading a subresource.
   virtual void SubresourceResponseStarted(const GURL& url,
@@ -407,7 +407,7 @@
                                                const gfx::Size& natural_size) {}
 
   // Returns the visibility of the delegate.
-  virtual Visibility GetVisibility() const;
+  virtual Visibility GetVisibility();
 
   // Get the UKM source ID for current content. This is used for providing
   // data about the content to the URL-keyed metrics service.
diff --git a/content/browser/host_zoom_map_impl.cc b/content/browser/host_zoom_map_impl.cc
index b97855d..6c900b0 100644
--- a/content/browser/host_zoom_map_impl.cc
+++ b/content/browser/host_zoom_map_impl.cc
@@ -79,7 +79,7 @@
   return partition->GetHostZoomMap();
 }
 
-HostZoomMap* HostZoomMap::GetForWebContents(const WebContents* contents) {
+HostZoomMap* HostZoomMap::GetForWebContents(WebContents* contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // TODO(wjmaclean): Update this behaviour to work with OOPIF.
   // See crbug.com/528407.
@@ -92,32 +92,31 @@
 
 // Helper function for setting/getting zoom levels for WebContents without
 // having to import HostZoomMapImpl everywhere.
-double HostZoomMap::GetZoomLevel(const WebContents* web_contents) {
+double HostZoomMap::GetZoomLevel(WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
       HostZoomMap::GetForWebContents(web_contents));
   return host_zoom_map->GetZoomLevelForWebContents(
-      *static_cast<const WebContentsImpl*>(web_contents));
+      static_cast<WebContentsImpl*>(web_contents));
 }
 
-bool HostZoomMap::PageScaleFactorIsOne(const WebContents* web_contents) {
+bool HostZoomMap::PageScaleFactorIsOne(WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
       HostZoomMap::GetForWebContents(web_contents));
   return host_zoom_map->PageScaleFactorIsOneForWebContents(
-      *static_cast<const WebContentsImpl*>(web_contents));
+      static_cast<WebContentsImpl*>(web_contents));
 }
 
-void HostZoomMap::SetZoomLevel(const WebContents* web_contents, double level) {
+void HostZoomMap::SetZoomLevel(WebContents* web_contents, double level) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   HostZoomMapImpl* host_zoom_map = static_cast<HostZoomMapImpl*>(
       HostZoomMap::GetForWebContents(web_contents));
   host_zoom_map->SetZoomLevelForWebContents(
-      *static_cast<const WebContentsImpl*>(web_contents), level);
+      static_cast<WebContentsImpl*>(web_contents), level);
 }
 
-void HostZoomMap::SendErrorPageZoomLevelRefresh(
-    const WebContents* web_contents) {
+void HostZoomMap::SendErrorPageZoomLevelRefresh(WebContents* web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   HostZoomMapImpl* host_zoom_map =
       static_cast<HostZoomMapImpl*>(HostZoomMap::GetDefaultForBrowserContext(
@@ -342,11 +341,11 @@
 }
 
 double HostZoomMapImpl::GetZoomLevelForWebContents(
-    const WebContentsImpl& web_contents_impl) const {
+    WebContentsImpl* web_contents_impl) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   int render_process_id =
-      web_contents_impl.GetRenderViewHost()->GetProcess()->GetID();
-  int routing_id = web_contents_impl.GetRenderViewHost()->GetRoutingID();
+      web_contents_impl->GetRenderViewHost()->GetProcess()->GetID();
+  int routing_id = web_contents_impl->GetRenderViewHost()->GetRoutingID();
 
   if (UsesTemporaryZoomLevel(render_process_id, routing_id))
     return GetTemporaryZoomLevel(render_process_id, routing_id);
@@ -356,7 +355,7 @@
   // is different than is stored in the map.
   GURL url;
   NavigationEntry* entry =
-      web_contents_impl.GetController().GetLastCommittedEntry();
+      web_contents_impl->GetController().GetLastCommittedEntry();
   // It is possible for a WebContent's zoom level to be queried before
   // a navigation has occurred.
   if (entry)
@@ -366,12 +365,12 @@
 }
 
 void HostZoomMapImpl::SetZoomLevelForWebContents(
-    const WebContentsImpl& web_contents_impl,
+    WebContentsImpl* web_contents_impl,
     double level) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   int render_process_id =
-      web_contents_impl.GetRenderViewHost()->GetProcess()->GetID();
-  int render_view_id = web_contents_impl.GetRenderViewHost()->GetRoutingID();
+      web_contents_impl->GetRenderViewHost()->GetProcess()->GetID();
+  int render_view_id = web_contents_impl->GetRenderViewHost()->GetRoutingID();
   if (UsesTemporaryZoomLevel(render_process_id, render_view_id)) {
     SetTemporaryZoomLevel(render_process_id, render_view_id, level);
   } else {
@@ -380,7 +379,7 @@
     // is different than what the render view is using. If the two don't match,
     // the attempt to set the zoom will fail.
     NavigationEntry* entry =
-        web_contents_impl.GetController().GetLastCommittedEntry();
+        web_contents_impl->GetController().GetLastCommittedEntry();
     // Tests may invoke this function with a null entry, but we don't
     // want to save zoom levels in this case.
     if (!entry)
@@ -414,14 +413,14 @@
 }
 
 bool HostZoomMapImpl::PageScaleFactorIsOneForWebContents(
-    const WebContentsImpl& web_contents_impl) const {
+    WebContentsImpl* web_contents_impl) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!web_contents_impl.GetRenderViewHost()->GetProcess())
+  if (!web_contents_impl->GetRenderViewHost()->GetProcess())
     return true;
 
   const auto it = view_page_scale_factors_are_one_.find(RenderViewKey(
-      web_contents_impl.GetRenderViewHost()->GetProcess()->GetID(),
-      web_contents_impl.GetRenderViewHost()->GetRoutingID()));
+      web_contents_impl->GetRenderViewHost()->GetProcess()->GetID(),
+      web_contents_impl->GetRenderViewHost()->GetRoutingID()));
   return it != view_page_scale_factors_are_one_.end() ? it->second : true;
 }
 
diff --git a/content/browser/host_zoom_map_impl.h b/content/browser/host_zoom_map_impl.h
index 51a26a52..3bf890a6d 100644
--- a/content/browser/host_zoom_map_impl.h
+++ b/content/browser/host_zoom_map_impl.h
@@ -59,16 +59,15 @@
 
   // Returns the current zoom level for the specified WebContents. This may
   // be a temporary zoom level, depending on UsesTemporaryZoomLevel().
-  double GetZoomLevelForWebContents(
-      const WebContentsImpl& web_contents_impl) const;
+  double GetZoomLevelForWebContents(WebContentsImpl* web_contents_impl) const;
 
   bool PageScaleFactorIsOneForWebContents(
-      const WebContentsImpl& web_contents_impl) const;
+      WebContentsImpl* web_contents_impl) const;
 
   // Sets the zoom level for this WebContents. If this WebContents is using
   // a temporary zoom level, then level is only applied to this WebContents.
   // Otherwise, the level will be applied on a host level.
-  void SetZoomLevelForWebContents(const WebContentsImpl& web_contents_impl,
+  void SetZoomLevelForWebContents(WebContentsImpl* web_contents_impl,
                                   double level);
 
   // Sets the zoom level for the specified view. The level may be set for only
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index fc73b531..f626c60 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -284,7 +284,7 @@
     return;
   }
 
-  const WebContents* web_contents = WebContents::FromRenderFrameHost(
+  WebContents* web_contents = WebContents::FromRenderFrameHost(
       RenderFrameHost::FromID(render_process_id, render_frame_id));
   if (!web_contents)
     return;
diff --git a/content/browser/net/reporting_service_proxy.cc b/content/browser/net/reporting_service_proxy.cc
index 90176c2..f668a839 100644
--- a/content/browser/net/reporting_service_proxy.cc
+++ b/content/browser/net/reporting_service_proxy.cc
@@ -11,6 +11,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/values.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -19,6 +21,7 @@
 #include "net/url_request/http_user_agent_settings.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "services/network/public/mojom/network_context.mojom.h"
 #include "third_party/blink/public/platform/reporting.mojom.h"
 #include "url/gurl.h"
 
@@ -28,9 +31,8 @@
 
 class ReportingServiceProxyImpl : public blink::mojom::ReportingServiceProxy {
  public:
-  ReportingServiceProxyImpl(
-      scoped_refptr<net::URLRequestContextGetter> request_context_getter)
-      : request_context_getter_(std::move(request_context_getter)) {}
+  explicit ReportingServiceProxyImpl(int render_process_id)
+      : render_process_id_(render_process_id) {}
 
   // blink::mojom::ReportingServiceProxy:
 
@@ -132,55 +134,29 @@
                    const std::string& group,
                    const std::string& type,
                    std::unique_ptr<base::Value> body) {
-    net::URLRequestContext* request_context =
-        request_context_getter_->GetURLRequestContext();
-    if (!request_context) {
-      net::ReportingReport::RecordReportDiscardedForNoURLRequestContext();
+    auto* rph = RenderProcessHost::FromID(render_process_id_);
+    if (!rph)
       return;
-    }
 
-    net::ReportingService* reporting_service =
-        request_context->reporting_service();
-    if (!reporting_service) {
-      net::ReportingReport::RecordReportDiscardedForNoReportingService();
-      return;
-    }
-
-    // Depth is only non-zero for NEL reports, and those can't come from the
-    // renderer.
-    std::string user_agent;
-    if (request_context->http_user_agent_settings() != nullptr)
-      user_agent = request_context->http_user_agent_settings()->GetUserAgent();
-    reporting_service->QueueReport(url, user_agent, group, type,
-                                   std::move(body),
-                                   /* depth= */ 0);
+    rph->GetStoragePartition()->GetNetworkContext()->QueueReport(
+        type, group, url, /*user_agent=*/base::nullopt,
+        base::Value::FromUniquePtrValue(std::move(body)));
   }
 
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+  int render_process_id_;
 };
 
-void CreateReportingServiceProxyOnNetworkTaskRunner(
-    blink::mojom::ReportingServiceProxyRequest request,
-    scoped_refptr<net::URLRequestContextGetter> request_context_getter) {
-  mojo::MakeStrongBinding(std::make_unique<ReportingServiceProxyImpl>(
-                              std::move(request_context_getter)),
-                          std::move(request));
-}
-
 }  // namespace
 
 // static
 void CreateReportingServiceProxy(
-    StoragePartition* storage_partition,
+    int render_process_id,
     blink::mojom::ReportingServiceProxyRequest request) {
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter(
-      storage_partition->GetURLRequestContext());
-  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner(
-      request_context_getter->GetNetworkTaskRunner());
-  network_task_runner->PostTask(
-      FROM_HERE,
-      base::BindOnce(&CreateReportingServiceProxyOnNetworkTaskRunner,
-                     std::move(request), std::move(request_context_getter)));
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  mojo::MakeStrongBinding(
+      std::make_unique<ReportingServiceProxyImpl>(render_process_id),
+      std::move(request));
 }
 
 }  // namespace content
diff --git a/content/browser/net/reporting_service_proxy.h b/content/browser/net/reporting_service_proxy.h
index cd0f42ea..cbd9a363 100644
--- a/content/browser/net/reporting_service_proxy.h
+++ b/content/browser/net/reporting_service_proxy.h
@@ -9,10 +9,10 @@
 
 namespace content {
 
-class StoragePartition;
-
+// Binds a mojom::ReportingServiceProxy to |request| that queues reports using
+// |render_process_id|'s NetworkContext. This must be called on the UI thread.
 void CreateReportingServiceProxy(
-    StoragePartition* storage_partition,
+    int render_process_id,
     blink::mojom::ReportingServiceProxyRequest request);
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 079dcad1..d1823eb 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2223,8 +2223,9 @@
           storage_partition_impl_->GetGeneratedCodeCacheContext())));
 
 #if BUILDFLAG(ENABLE_REPORTING)
-  registry->AddInterface(
-      base::Bind(&CreateReportingServiceProxy, storage_partition_impl_));
+  AddUIThreadInterface(
+      registry.get(),
+      base::BindRepeating(&CreateReportingServiceProxy, GetID()));
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
   registry->AddInterface(base::BindRepeating(
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index 419a683..efd2a7a 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -49,7 +49,7 @@
   return false;
 }
 
-double RenderWidgetHostDelegate::GetPendingPageZoomLevel() const {
+double RenderWidgetHostDelegate::GetPendingPageZoomLevel() {
   return 0.0;
 }
 
@@ -82,7 +82,7 @@
   return nullptr;
 }
 
-bool RenderWidgetHostDelegate::IsFullscreenForCurrentTab() const {
+bool RenderWidgetHostDelegate::IsFullscreenForCurrentTab() {
   return false;
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index 79ac33c..83878e9 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -78,7 +78,7 @@
   // for the pending page. Otherwise, this returns the zoom level for the
   // current page. Note that subframe navigations do not affect the zoom level,
   // which is tracked at the level of the page.
-  virtual double GetPendingPageZoomLevel() const;
+  virtual double GetPendingPageZoomLevel();
 
   // The RenderWidgetHost lost the focus.
   virtual void RenderWidgetLostFocus(
@@ -199,7 +199,7 @@
                                   bool privileged) {}
 
   // Returns whether the associated tab is in fullscreen mode.
-  virtual bool IsFullscreenForCurrentTab() const;
+  virtual bool IsFullscreenForCurrentTab();
 
   // Returns the display mode for the view.
   virtual blink::WebDisplayMode GetDisplayMode(
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index d8bad409..f0b0c4cf 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -569,7 +569,7 @@
 
   void SetZoomLevel(double zoom_level) { zoom_level_ = zoom_level; }
 
-  double GetPendingPageZoomLevel() const override { return zoom_level_; }
+  double GetPendingPageZoomLevel() override { return zoom_level_; }
 
   void FocusOwningWebContents(
       RenderWidgetHostImpl* render_widget_host) override {
diff --git a/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc b/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc
index 8ccba9c..bdaf4c8 100644
--- a/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc
+++ b/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc
@@ -106,7 +106,7 @@
     return fake_contents_window_.get();
   }
 
-  bool IsBeingDestroyed() const override { return is_being_destroyed_; }
+  bool IsBeingDestroyed() override { return is_being_destroyed_; }
 
  private:
   std::unique_ptr<aura::Window> fake_native_view_;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index f5853dce..8d1eaa3 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -917,27 +917,23 @@
   return controller_;
 }
 
-const NavigationControllerImpl& WebContentsImpl::GetController() const {
-  return controller_;
-}
-
-BrowserContext* WebContentsImpl::GetBrowserContext() const {
+BrowserContext* WebContentsImpl::GetBrowserContext() {
   return controller_.GetBrowserContext();
 }
 
-const GURL& WebContentsImpl::GetURL() const {
+const GURL& WebContentsImpl::GetURL() {
   // We may not have a navigation entry yet.
   NavigationEntry* entry = controller_.GetVisibleEntry();
   return entry ? entry->GetVirtualURL() : GURL::EmptyGURL();
 }
 
-const GURL& WebContentsImpl::GetVisibleURL() const {
+const GURL& WebContentsImpl::GetVisibleURL() {
   // We may not have a navigation entry yet.
   NavigationEntry* entry = controller_.GetVisibleEntry();
   return entry ? entry->GetVirtualURL() : GURL::EmptyGURL();
 }
 
-const GURL& WebContentsImpl::GetLastCommittedURL() const {
+const GURL& WebContentsImpl::GetLastCommittedURL() {
   // We may not have a navigation entry yet.
   NavigationEntry* entry = controller_.GetLastCommittedEntry();
   return entry ? entry->GetVirtualURL() : GURL::EmptyGURL();
@@ -964,7 +960,7 @@
   }
 }
 
-RenderFrameHostImpl* WebContentsImpl::GetMainFrame() const {
+RenderFrameHostImpl* WebContentsImpl::GetMainFrame() {
   return frame_tree_.root()->current_frame_host();
 }
 
@@ -1031,7 +1027,7 @@
   frame_tree_.root()->render_manager()->SendPageMessage(msg, nullptr);
 }
 
-RenderViewHostImpl* WebContentsImpl::GetRenderViewHost() const {
+RenderViewHostImpl* WebContentsImpl::GetRenderViewHost() {
   return GetRenderManager()->current_host();
 }
 
@@ -1047,7 +1043,7 @@
   GetRenderViewHost()->ClosePage();
 }
 
-RenderWidgetHostView* WebContentsImpl::GetRenderWidgetHostView() const {
+RenderWidgetHostView* WebContentsImpl::GetRenderWidgetHostView() {
   return GetRenderManager()->GetRenderWidgetHostView();
 }
 
@@ -1057,8 +1053,7 @@
   return GetRenderManager()->GetRenderWidgetHostView();
 }
 
-RenderWidgetHostView* WebContentsImpl::GetFullscreenRenderWidgetHostView()
-    const {
+RenderWidgetHostView* WebContentsImpl::GetFullscreenRenderWidgetHostView() {
   if (auto* widget_host = GetFullscreenRenderWidgetHost())
     return widget_host->GetView();
   return nullptr;
@@ -1073,7 +1068,7 @@
   screen_orientation_provider_->OnOrientationChange();
 }
 
-SkColor WebContentsImpl::GetThemeColor() const {
+SkColor WebContentsImpl::GetThemeColor() {
   return theme_color_;
 }
 
@@ -1176,7 +1171,7 @@
     observer.ViewportFitChanged(value);
 }
 
-FindRequestManager* WebContentsImpl::GetFindRequestManagerForTesting() const {
+FindRequestManager* WebContentsImpl::GetFindRequestManagerForTesting() {
   return GetFindRequestManager();
 }
 
@@ -1253,13 +1248,13 @@
     observer.DidUpdateWebManifestURL(manifest_url);
 }
 
-WebUI* WebContentsImpl::GetWebUI() const {
+WebUI* WebContentsImpl::GetWebUI() {
   WebUI* commited_web_ui = GetCommittedWebUI();
   return commited_web_ui ? commited_web_ui
                          : GetRenderManager()->GetNavigatingWebUI();
 }
 
-WebUI* WebContentsImpl::GetCommittedWebUI() const {
+WebUI* WebContentsImpl::GetCommittedWebUI() {
   return frame_tree_.root()->current_frame_host()->web_ui();
 }
 
@@ -1287,7 +1282,7 @@
     observer.UserAgentOverrideSet(override);
 }
 
-const std::string& WebContentsImpl::GetUserAgentOverride() const {
+const std::string& WebContentsImpl::GetUserAgentOverride() {
   return renderer_preferences_.user_agent_override;
 }
 
@@ -1304,15 +1299,15 @@
   }
 }
 
-bool WebContentsImpl::IsWebContentsOnlyAccessibilityModeForTesting() const {
+bool WebContentsImpl::IsWebContentsOnlyAccessibilityModeForTesting() {
   return accessibility_mode_ == ui::kAXModeWebContentsOnly;
 }
 
-bool WebContentsImpl::IsFullAccessibilityModeForTesting() const {
+bool WebContentsImpl::IsFullAccessibilityModeForTesting() {
   return accessibility_mode_ == ui::kAXModeComplete;
 }
 
-const PageImportanceSignals& WebContentsImpl::GetPageImportanceSignals() const {
+const PageImportanceSignals& WebContentsImpl::GetPageImportanceSignals() {
   return page_importance_signals_;
 }
 
@@ -1329,7 +1324,7 @@
 
 #endif
 
-const base::string16& WebContentsImpl::GetTitle() const {
+const base::string16& WebContentsImpl::GetTitle() {
   // Transient entries take precedence. They are used for interstitial pages
   // that are shown on top of existing pages.
   NavigationEntry* entry = controller_.GetTransientEntry();
@@ -1383,24 +1378,24 @@
   return page_title_when_no_navigation_entry_;
 }
 
-SiteInstanceImpl* WebContentsImpl::GetSiteInstance() const {
+SiteInstanceImpl* WebContentsImpl::GetSiteInstance() {
   return GetRenderManager()->current_host()->GetSiteInstance();
 }
 
-bool WebContentsImpl::IsLoading() const {
+bool WebContentsImpl::IsLoading() {
   return frame_tree_.IsLoading() &&
          !(ShowingInterstitialPage() && interstitial_page_->pause_throbber());
 }
 
-double WebContentsImpl::GetLoadProgress() const {
+double WebContentsImpl::GetLoadProgress() {
   return frame_tree_.load_progress();
 }
 
-bool WebContentsImpl::IsLoadingToDifferentDocument() const {
+bool WebContentsImpl::IsLoadingToDifferentDocument() {
   return IsLoading() && is_load_to_different_document_;
 }
 
-bool WebContentsImpl::IsWaitingForResponse() const {
+bool WebContentsImpl::IsWaitingForResponse() {
   NavigationRequest* ongoing_navigation_request =
       frame_tree_.root()->navigation_request();
 
@@ -1408,23 +1403,23 @@
   return ongoing_navigation_request != nullptr;
 }
 
-const net::LoadStateWithParam& WebContentsImpl::GetLoadState() const {
+const net::LoadStateWithParam& WebContentsImpl::GetLoadState() {
   return load_state_;
 }
 
-const base::string16& WebContentsImpl::GetLoadStateHost() const {
+const base::string16& WebContentsImpl::GetLoadStateHost() {
   return load_state_host_;
 }
 
-uint64_t WebContentsImpl::GetUploadSize() const {
+uint64_t WebContentsImpl::GetUploadSize() {
   return upload_size_;
 }
 
-uint64_t WebContentsImpl::GetUploadPosition() const {
+uint64_t WebContentsImpl::GetUploadPosition() {
   return upload_position_;
 }
 
-const std::string& WebContentsImpl::GetEncoding() const {
+const std::string& WebContentsImpl::GetEncoding() {
   return canonical_encoding_;
 }
 
@@ -1483,11 +1478,11 @@
   }
 }
 
-bool WebContentsImpl::IsBeingCaptured() const {
+bool WebContentsImpl::IsBeingCaptured() {
   return capturer_count_ > 0;
 }
 
-bool WebContentsImpl::IsAudioMuted() const {
+bool WebContentsImpl::IsAudioMuted() {
   if (base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)) {
     return audio_stream_factory_ && audio_stream_factory_->IsMuted();
   }
@@ -1525,11 +1520,11 @@
   return is_currently_audible_;
 }
 
-bool WebContentsImpl::IsConnectedToBluetoothDevice() const {
+bool WebContentsImpl::IsConnectedToBluetoothDevice() {
   return bluetooth_connected_device_count_ > 0;
 }
 
-bool WebContentsImpl::HasPictureInPictureVideo() const {
+bool WebContentsImpl::HasPictureInPictureVideo() {
   return has_picture_in_picture_video_;
 }
 
@@ -1544,7 +1539,7 @@
     observer.MediaPictureInPictureChanged(has_picture_in_picture_video_);
 }
 
-bool WebContentsImpl::IsCrashed() const {
+bool WebContentsImpl::IsCrashed() {
   switch (crashed_status_) {
     case base::TERMINATION_STATUS_PROCESS_CRASHED:
     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
@@ -1580,15 +1575,15 @@
   NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
 }
 
-base::TerminationStatus WebContentsImpl::GetCrashedStatus() const {
+base::TerminationStatus WebContentsImpl::GetCrashedStatus() {
   return crashed_status_;
 }
 
-int WebContentsImpl::GetCrashedErrorCode() const {
+int WebContentsImpl::GetCrashedErrorCode() {
   return crashed_error_code_;
 }
 
-bool WebContentsImpl::IsBeingDestroyed() const {
+bool WebContentsImpl::IsBeingDestroyed() {
   return is_being_destroyed_;
 }
 
@@ -1636,7 +1631,7 @@
     observer.OnAudioStateChanged(is_currently_audible_);
 }
 
-base::TimeTicks WebContentsImpl::GetLastActiveTime() const {
+base::TimeTicks WebContentsImpl::GetLastActiveTime() {
   return last_active_time_;
 }
 
@@ -1690,7 +1685,7 @@
   SetVisibility(Visibility::HIDDEN);
 }
 
-bool WebContentsImpl::HasRecentInteractiveInputEvent() const {
+bool WebContentsImpl::HasRecentInteractiveInputEvent() {
   static constexpr base::TimeDelta kMaxInterval =
       base::TimeDelta::FromSeconds(5);
   base::TimeDelta delta =
@@ -1728,7 +1723,7 @@
   SetVisibility(Visibility::OCCLUDED);
 }
 
-Visibility WebContentsImpl::GetVisibility() const {
+Visibility WebContentsImpl::GetVisibility() {
   return visibility_;
 }
 
@@ -2457,7 +2452,7 @@
 }
 #endif
 
-bool WebContentsImpl::IsFullscreenForCurrentTab() const {
+bool WebContentsImpl::IsFullscreenForCurrentTab() {
   return delegate_ ? delegate_->IsFullscreenForTabOrPending(this) : false;
 }
 
@@ -3043,7 +3038,7 @@
   return DevToolsAgentHost::IsDebuggerAttached(this);
 }
 
-ui::AXMode WebContentsImpl::GetAccessibilityMode() const {
+ui::AXMode WebContentsImpl::GetAccessibilityMode() {
   return accessibility_mode_;
 }
 
@@ -3663,11 +3658,11 @@
   view_->FocusThroughTabTraversal(reverse);
 }
 
-bool WebContentsImpl::ShowingInterstitialPage() const {
+bool WebContentsImpl::ShowingInterstitialPage() {
   return interstitial_page_ != nullptr;
 }
 
-InterstitialPageImpl* WebContentsImpl::GetInterstitialPage() const {
+InterstitialPageImpl* WebContentsImpl::GetInterstitialPage() {
   return interstitial_page_;
 }
 
@@ -3799,11 +3794,11 @@
                                                    std::move(callback));
 }
 
-const std::string& WebContentsImpl::GetContentsMimeType() const {
+const std::string& WebContentsImpl::GetContentsMimeType() {
   return contents_mime_type_;
 }
 
-bool WebContentsImpl::WillNotifyDisconnection() const {
+bool WebContentsImpl::WillNotifyDisconnection() {
   return notify_disconnection_;
 }
 
@@ -3895,15 +3890,15 @@
   closed_by_user_gesture_ = value;
 }
 
-bool WebContentsImpl::GetClosedByUserGesture() const {
+bool WebContentsImpl::GetClosedByUserGesture() {
   return closed_by_user_gesture_;
 }
 
-int WebContentsImpl::GetMinimumZoomPercent() const {
+int WebContentsImpl::GetMinimumZoomPercent() {
   return minimum_zoom_percent_;
 }
 
-int WebContentsImpl::GetMaximumZoomPercent() const {
+int WebContentsImpl::GetMaximumZoomPercent() {
   return maximum_zoom_percent_;
 }
 
@@ -3912,7 +3907,7 @@
       GetRenderViewHost()->GetRoutingID(), page_scale_factor));
 }
 
-gfx::Size WebContentsImpl::GetPreferredSize() const {
+gfx::Size WebContentsImpl::GetPreferredSize() {
   return IsBeingCaptured() ? preferred_size_for_capture_ : preferred_size_;
 }
 
@@ -3958,20 +3953,20 @@
   return true;
 }
 
-bool WebContentsImpl::HasOpener() const {
+bool WebContentsImpl::HasOpener() {
   return GetOpener() != nullptr;
 }
 
-RenderFrameHostImpl* WebContentsImpl::GetOpener() const {
+RenderFrameHostImpl* WebContentsImpl::GetOpener() {
   FrameTreeNode* opener_ftn = frame_tree_.root()->opener();
   return opener_ftn ? opener_ftn->current_frame_host() : nullptr;
 }
 
-bool WebContentsImpl::HasOriginalOpener() const {
+bool WebContentsImpl::HasOriginalOpener() {
   return GetOriginalOpener() != nullptr;
 }
 
-RenderFrameHostImpl* WebContentsImpl::GetOriginalOpener() const {
+RenderFrameHostImpl* WebContentsImpl::GetOriginalOpener() {
   FrameTreeNode* opener_ftn = frame_tree_.root()->original_opener();
   return opener_ftn ? opener_ftn->current_frame_host() : nullptr;
 }
@@ -5079,7 +5074,7 @@
     delegate_->OnDidBlockFramebust(this, url);
 }
 
-const GURL& WebContentsImpl::GetMainFrameLastCommittedURL() const {
+const GURL& WebContentsImpl::GetMainFrameLastCommittedURL() {
   return GetLastCommittedURL();
 }
 
@@ -5313,7 +5308,7 @@
 }
 
 #if !defined(OS_ANDROID)
-double WebContentsImpl::GetPendingPageZoomLevel() const {
+double WebContentsImpl::GetPendingPageZoomLevel() {
   NavigationEntry* pending_entry = GetController().GetPendingEntry();
   if (!pending_entry)
     return HostZoomMap::GetZoomLevel(this);
@@ -5350,7 +5345,7 @@
   return frame && frame->has_focused_editable_element();
 }
 
-bool WebContentsImpl::IsShowingContextMenu() const {
+bool WebContentsImpl::IsShowingContextMenu() {
   return showing_context_menu_;
 }
 
@@ -5390,7 +5385,7 @@
     browser_plugin_embedder_.reset();
 }
 
-WebContentsImpl* WebContentsImpl::GetOuterWebContents() const {
+WebContentsImpl* WebContentsImpl::GetOuterWebContents() {
   if (GuestMode::IsCrossProcessFrameGuest(this))
     return node_.outer_web_contents();
 
@@ -6276,7 +6271,7 @@
 
 #endif
 
-bool WebContentsImpl::CompletedFirstVisuallyNonEmptyPaint() const {
+bool WebContentsImpl::CompletedFirstVisuallyNonEmptyPaint() {
   return did_first_visually_non_empty_paint_;
 }
 
@@ -6432,8 +6427,8 @@
   return nullptr;
 }
 
-FindRequestManager* WebContentsImpl::GetFindRequestManager() const {
-  for (const auto* contents = this; contents;
+FindRequestManager* WebContentsImpl::GetFindRequestManager() {
+  for (auto* contents = this; contents;
        contents = contents->GetOuterWebContents()) {
     if (contents->find_request_manager_)
       return contents->find_request_manager_.get();
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 5a0e315..492ad274 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -295,12 +295,11 @@
   WebContentsDelegate* GetDelegate() override;
   void SetDelegate(WebContentsDelegate* delegate) override;
   NavigationControllerImpl& GetController() override;
-  const NavigationControllerImpl& GetController() const override;
-  BrowserContext* GetBrowserContext() const override;
-  const GURL& GetURL() const override;
-  const GURL& GetVisibleURL() const override;
-  const GURL& GetLastCommittedURL() const override;
-  RenderFrameHostImpl* GetMainFrame() const override;
+  BrowserContext* GetBrowserContext() override;
+  const GURL& GetURL() override;
+  const GURL& GetVisibleURL() override;
+  const GURL& GetLastCommittedURL() override;
+  RenderFrameHostImpl* GetMainFrame() override;
   RenderFrameHostImpl* GetFocusedFrame() override;
   RenderFrameHostImpl* FindFrameByFrameTreeNodeId(int frame_tree_node_id,
                                                   int process_id) override;
@@ -310,65 +309,65 @@
       const base::RepeatingCallback<void(RenderFrameHost*)>& on_frame) override;
   std::vector<RenderFrameHost*> GetAllFrames() override;
   int SendToAllFrames(IPC::Message* message) override;
-  RenderViewHostImpl* GetRenderViewHost() const override;
-  RenderWidgetHostView* GetRenderWidgetHostView() const override;
+  RenderViewHostImpl* GetRenderViewHost() override;
+  RenderWidgetHostView* GetRenderWidgetHostView() override;
   RenderWidgetHostView* GetTopLevelRenderWidgetHostView() override;
   void ClosePage() override;
-  RenderWidgetHostView* GetFullscreenRenderWidgetHostView() const override;
-  SkColor GetThemeColor() const override;
-  WebUI* GetWebUI() const override;
-  WebUI* GetCommittedWebUI() const override;
+  RenderWidgetHostView* GetFullscreenRenderWidgetHostView() override;
+  SkColor GetThemeColor() override;
+  WebUI* GetWebUI() override;
+  WebUI* GetCommittedWebUI() override;
   void SetUserAgentOverride(const std::string& override,
                             bool override_in_new_tabs) override;
-  const std::string& GetUserAgentOverride() const override;
+  const std::string& GetUserAgentOverride() override;
   bool ShouldOverrideUserAgentInNewTabs() override;
   void EnableWebContentsOnlyAccessibilityMode() override;
-  bool IsWebContentsOnlyAccessibilityModeForTesting() const override;
-  bool IsFullAccessibilityModeForTesting() const override;
-  const PageImportanceSignals& GetPageImportanceSignals() const override;
-  const base::string16& GetTitle() const override;
+  bool IsWebContentsOnlyAccessibilityModeForTesting() override;
+  bool IsFullAccessibilityModeForTesting() override;
+  const PageImportanceSignals& GetPageImportanceSignals() override;
+  const base::string16& GetTitle() override;
   void UpdateTitleForEntry(NavigationEntry* entry,
                            const base::string16& title) override;
-  SiteInstanceImpl* GetSiteInstance() const override;
-  bool IsLoading() const override;
-  double GetLoadProgress() const override;
-  bool IsLoadingToDifferentDocument() const override;
-  bool IsWaitingForResponse() const override;
-  const net::LoadStateWithParam& GetLoadState() const override;
-  const base::string16& GetLoadStateHost() const override;
+  SiteInstanceImpl* GetSiteInstance() override;
+  bool IsLoading() override;
+  double GetLoadProgress() override;
+  bool IsLoadingToDifferentDocument() override;
+  bool IsWaitingForResponse() override;
+  const net::LoadStateWithParam& GetLoadState() override;
+  const base::string16& GetLoadStateHost() override;
   void RequestAXTreeSnapshot(AXTreeSnapshotCallback callback,
                              ui::AXMode ax_mode) override;
-  uint64_t GetUploadSize() const override;
-  uint64_t GetUploadPosition() const override;
-  const std::string& GetEncoding() const override;
+  uint64_t GetUploadSize() override;
+  uint64_t GetUploadPosition() override;
+  const std::string& GetEncoding() override;
   bool WasDiscarded() override;
   void SetWasDiscarded(bool was_discarded) override;
   void IncrementCapturerCount(const gfx::Size& capture_size) override;
   void DecrementCapturerCount() override;
-  bool IsBeingCaptured() const override;
-  bool IsAudioMuted() const override;
+  bool IsBeingCaptured() override;
+  bool IsAudioMuted() override;
   void SetAudioMuted(bool mute) override;
   bool IsCurrentlyAudible() override;
-  bool IsConnectedToBluetoothDevice() const override;
-  bool HasPictureInPictureVideo() const override;
-  bool IsCrashed() const override;
+  bool IsConnectedToBluetoothDevice() override;
+  bool HasPictureInPictureVideo() override;
+  bool IsCrashed() override;
   void SetIsCrashed(base::TerminationStatus status, int error_code) override;
-  base::TerminationStatus GetCrashedStatus() const override;
-  int GetCrashedErrorCode() const override;
-  bool IsBeingDestroyed() const override;
+  base::TerminationStatus GetCrashedStatus() override;
+  int GetCrashedErrorCode() override;
+  bool IsBeingDestroyed() override;
   void NotifyNavigationStateChanged(InvalidateTypes changed_flags) override;
   void OnAudioStateChanged() override;
-  base::TimeTicks GetLastActiveTime() const override;
+  base::TimeTicks GetLastActiveTime() override;
   void WasShown() override;
   void WasHidden() override;
   void WasOccluded() override;
-  Visibility GetVisibility() const override;
+  Visibility GetVisibility() override;
   bool NeedToFireBeforeUnload() override;
   void DispatchBeforeUnload(bool auto_cancel) override;
   void AttachToOuterWebContentsFrame(
       std::unique_ptr<WebContents> current_web_contents,
       RenderFrameHost* outer_contents_frame) override;
-  WebContentsImpl* GetOuterWebContents() const override;
+  WebContentsImpl* GetOuterWebContents() override;
   WebContentsImpl* GetOutermostWebContents() override;
   void DidChangeVisibleSecurityState() override;
   void NotifyPreferencesChanged() override;
@@ -408,8 +407,8 @@
   void StoreFocus() override;
   void RestoreFocus() override;
   void FocusThroughTabTraversal(bool reverse) override;
-  bool ShowingInterstitialPage() const override;
-  InterstitialPageImpl* GetInterstitialPage() const override;
+  bool ShowingInterstitialPage() override;
+  InterstitialPageImpl* GetInterstitialPage() override;
   bool IsSavable() override;
   void OnSavePage() override;
   bool SavePage(const base::FilePath& main_file,
@@ -422,24 +421,24 @@
                             const base::string16& suggested_filename) override;
   void GenerateMHTML(const MHTMLGenerationParams& params,
                      base::OnceCallback<void(int64_t)> callback) override;
-  const std::string& GetContentsMimeType() const override;
-  bool WillNotifyDisconnection() const override;
+  const std::string& GetContentsMimeType() override;
+  bool WillNotifyDisconnection() override;
   RendererPreferences* GetMutableRendererPrefs() override;
   void Close() override;
   void SystemDragEnded(RenderWidgetHost* source_rwh) override;
   void NavigatedByUser() override;
   void SetClosedByUserGesture(bool value) override;
-  bool GetClosedByUserGesture() const override;
-  int GetMinimumZoomPercent() const override;
-  int GetMaximumZoomPercent() const override;
+  bool GetClosedByUserGesture() override;
+  int GetMinimumZoomPercent() override;
+  int GetMaximumZoomPercent() override;
   void SetPageScale(float page_scale_factor) override;
-  gfx::Size GetPreferredSize() const override;
+  gfx::Size GetPreferredSize() override;
   bool GotResponseToLockMouseRequest(bool allowed) override;
   bool GotResponseToKeyboardLockRequest(bool allowed) override;
-  bool HasOpener() const override;
-  RenderFrameHostImpl* GetOpener() const override;
-  bool HasOriginalOpener() const override;
-  RenderFrameHostImpl* GetOriginalOpener() const override;
+  bool HasOpener() override;
+  RenderFrameHostImpl* GetOpener() override;
+  bool HasOriginalOpener() override;
+  RenderFrameHostImpl* GetOriginalOpener() override;
   void DidChooseColorInColorChooser(SkColor color) override;
   void DidEndColorChooser() override;
   int DownloadImage(const GURL& url,
@@ -453,17 +452,17 @@
   void StopFinding(StopFindAction action) override;
   bool WasEverAudible() override;
   void GetManifest(GetManifestCallback callback) override;
-  bool IsFullscreenForCurrentTab() const override;
+  bool IsFullscreenForCurrentTab() override;
   void ExitFullscreen(bool will_cause_resize) override;
   void ResumeLoadingCreatedWebContents() override;
   void SetIsOverlayContent(bool is_overlay_content) override;
   bool IsFocusedElementEditable() override;
   void ClearFocusedElement() override;
-  bool IsShowingContextMenu() const override;
+  bool IsShowingContextMenu() override;
   void SetShowingContextMenu(bool showing) override;
   void PausePageScheduledTasks(bool paused) override;
   base::UnguessableToken GetAudioGroupId() override;
-  bool CompletedFirstVisuallyNonEmptyPaint() const override;
+  bool CompletedFirstVisuallyNonEmptyPaint() override;
 
 #if defined(OS_ANDROID)
   base::android::ScopedJavaLocalRef<jobject> GetJavaWebContents() override;
@@ -473,7 +472,7 @@
   service_manager::InterfaceProvider* GetJavaInterfaces() override;
 #endif
 
-  bool HasRecentInteractiveInputEvent() const override;
+  bool HasRecentInteractiveInputEvent() override;
   void SetIgnoreInputEvents(bool ignore_input_events) override;
 
   // Implementation of PageNavigator.
@@ -491,7 +490,7 @@
       const std::string& interface_name,
       mojo::ScopedMessagePipeHandle* interface_pipe) override;
   void OnDidBlockFramebust(const GURL& url) override;
-  const GURL& GetMainFrameLastCommittedURL() const override;
+  const GURL& GetMainFrameLastCommittedURL() override;
   void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
   void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
   void ShowContextMenu(RenderFrameHost* render_frame_host,
@@ -524,7 +523,7 @@
                       const std::string& encoding) override;
   WebContents* GetAsWebContents() override;
   bool IsNeverVisible() override;
-  ui::AXMode GetAccessibilityMode() const override;
+  ui::AXMode GetAccessibilityMode() override;
   // Broadcasts the mode change to all frames.
   void SetAccessibilityMode(ui::AXMode mode) override;
   void AccessibilityEventReceived(
@@ -722,7 +721,7 @@
   InputEventShim* GetInputEventShim() const override;
 
 #if !defined(OS_ANDROID)
-  double GetPendingPageZoomLevel() const override;
+  double GetPendingPageZoomLevel() override;
 #endif  // !defined(OS_ANDROID)
 
   KeyboardEventProcessingResult PreHandleKeyboardEvent(
@@ -1004,7 +1003,7 @@
 
   // Returns the current FindRequestManager associated with the WebContents;
   // this won't create one if none exists.
-  FindRequestManager* GetFindRequestManagerForTesting() const;
+  FindRequestManager* GetFindRequestManagerForTesting();
 
  private:
   friend class WebContentsObserver;
@@ -1390,7 +1389,7 @@
       JavaScriptDialogManager* dialog_manager);
 
   // Returns the FindRequestManager, which may be found in an outer WebContents.
-  FindRequestManager* GetFindRequestManager() const;
+  FindRequestManager* GetFindRequestManager();
 
   // Returns the FindRequestManager, or tries to create one if it doesn't
   //  already exist. The FindRequestManager may be found in an outer
diff --git a/content/browser/webui/web_ui_url_loader_factory.cc b/content/browser/webui/web_ui_url_loader_factory.cc
index 37bab92..63fe012 100644
--- a/content/browser/webui/web_ui_url_loader_factory.cc
+++ b/content/browser/webui/web_ui_url_loader_factory.cc
@@ -7,7 +7,6 @@
 #include <map>
 
 #include "base/bind.h"
-#include "base/debug/alias.h"
 #include "base/debug/crash_logging.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
@@ -58,12 +57,9 @@
 void ReadData(scoped_refptr<network::ResourceResponse> headers,
               const ui::TemplateReplacements* replacements,
               bool gzipped,
-              const GURL& url,
               scoped_refptr<URLDataSourceImpl> data_source,
               network::mojom::URLLoaderClientPtrInfo client_info,
               scoped_refptr<base::RefCountedMemory> bytes) {
-  // TODO(jam): remove after https://crbug.com/891074 is fixed.
-  DEBUG_ALIAS_FOR_GURL(url_buf, url);
   if (!bytes) {
     CallOnError(std::move(client_info), net::ERR_FAILED);
     return;
@@ -131,7 +127,6 @@
 void DataAvailable(scoped_refptr<network::ResourceResponse> headers,
                    const ui::TemplateReplacements* replacements,
                    bool gzipped,
-                   const GURL& url,
                    scoped_refptr<URLDataSourceImpl> source,
                    network::mojom::URLLoaderClientPtrInfo client_info,
                    scoped_refptr<base::RefCountedMemory> bytes) {
@@ -142,7 +137,7 @@
       {base::TaskPriority::USER_BLOCKING, base::MayBlock(),
        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})
       ->PostTask(FROM_HERE,
-                 base::BindOnce(ReadData, headers, replacements, gzipped, url,
+                 base::BindOnce(ReadData, headers, replacements, gzipped,
                                 source, std::move(client_info), bytes));
 }
 
@@ -198,7 +193,6 @@
   // |replacements| is owned by |source| keep a reference to it in the callback.
   auto data_available_callback =
       base::Bind(DataAvailable, resource_response, replacements, gzipped,
-                 request.url,
                  base::RetainedRef(source), base::Passed(&client_info));
 
   // TODO(jam): once we only have this code path for WebUI, and not the
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index b0635bb4..17be42a 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -141,7 +141,7 @@
 IPC_ENUM_TRAITS_MAX_VALUE(blink::UserActivationUpdateType,
                           blink::UserActivationUpdateType::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::WebMediaPlayerAction::Type,
-                          blink::WebMediaPlayerAction::Type::kTypeLast)
+                          blink::WebMediaPlayerAction::Type::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(content::WasActivatedOption,
                           content::WasActivatedOption::kMaxValue)
 IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebScrollDirection,
diff --git a/content/public/browser/host_zoom_map.h b/content/public/browser/host_zoom_map.h
index a72fc20..66b566c2 100644
--- a/content/public/browser/host_zoom_map.h
+++ b/content/public/browser/host_zoom_map.h
@@ -77,26 +77,24 @@
   // Returns the HostZoomMap associated with this WebContent's main frame. If
   // multiple WebContents share the same SiteInstance, then they share a single
   // HostZoomMap.
-  CONTENT_EXPORT static HostZoomMap* GetForWebContents(
-      const WebContents* contents);
+  CONTENT_EXPORT static HostZoomMap* GetForWebContents(WebContents* contents);
 
   // Returns the current zoom level for the specified WebContents. May be
   // temporary or host-specific.
-  CONTENT_EXPORT static double GetZoomLevel(const WebContents* web_contents);
+  CONTENT_EXPORT static double GetZoomLevel(WebContents* web_contents);
 
   // Returns true if the page scale factor for the WebContents is one.
-  CONTENT_EXPORT static bool PageScaleFactorIsOne(
-      const WebContents* web_contents);
+  CONTENT_EXPORT static bool PageScaleFactorIsOne(WebContents* web_contents);
 
   // Sets the current zoom level for the specified WebContents. The level may
   // be temporary or host-specific depending on the particular WebContents.
-  CONTENT_EXPORT static void SetZoomLevel(const WebContents* web_contents,
+  CONTENT_EXPORT static void SetZoomLevel(WebContents* web_contents,
                                           double level);
 
   // Send an IPC to refresh any displayed error page's zoom levels. Needs to
   // be called since error pages don't get loaded via the normal channel.
   CONTENT_EXPORT static void SendErrorPageZoomLevelRefresh(
-      const WebContents* web_contents);
+      WebContents* web_contents);
 
   // Set or clear whether or not the page scale factor for a view is one.
   virtual void SetPageScaleFactorIsOneForView(
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 1f1167b5..060e2194 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -264,16 +264,15 @@
 
   // Gets the controller for this WebContents.
   virtual NavigationController& GetController() = 0;
-  virtual const NavigationController& GetController() const = 0;
 
   // Returns the user browser context associated with this WebContents (via the
   // NavigationController).
-  virtual content::BrowserContext* GetBrowserContext() const = 0;
+  virtual content::BrowserContext* GetBrowserContext() = 0;
 
   // Gets the URL that is currently being displayed, if there is one.
   // This method is deprecated. DO NOT USE! Pick either |GetVisibleURL| or
   // |GetLastCommittedURL| as appropriate.
-  virtual const GURL& GetURL() const = 0;
+  virtual const GURL& GetURL() = 0;
 
   // Gets the virtual URL currently being displayed in the URL bar, if there is
   // one. This URL might be a pending navigation that hasn't committed yet, so
@@ -281,7 +280,7 @@
   // typical example of this is interstitials, which show the URL of the
   // new/loading page (active) but the security context is of the old page (last
   // committed).
-  virtual const GURL& GetVisibleURL() const = 0;
+  virtual const GURL& GetVisibleURL() = 0;
 
   // Gets the virtual URL of the last committed page in this WebContents.
   // Virtual URLs are meant to be displayed to the user (e.g., they include the
@@ -289,10 +288,10 @@
   // and NavigationHandle::GetURL). The last committed page is the current
   // security context and the content that is actually displayed within the tab.
   // See also GetVisibleURL above, which may differ from this URL.
-  virtual const GURL& GetLastCommittedURL() const = 0;
+  virtual const GURL& GetLastCommittedURL() = 0;
 
   // Returns the main frame for the currently active view.
-  virtual RenderFrameHost* GetMainFrame() const = 0;
+  virtual RenderFrameHost* GetMainFrame() = 0;
 
   // Returns the focused frame for the currently active view.
   virtual RenderFrameHost* GetFocusedFrame() = 0;
@@ -333,11 +332,11 @@
   virtual int SendToAllFrames(IPC::Message* message) = 0;
 
   // Gets the current RenderViewHost for this tab.
-  virtual RenderViewHost* GetRenderViewHost() const = 0;
+  virtual RenderViewHost* GetRenderViewHost() = 0;
 
   // Returns the currently active RenderWidgetHostView. This may change over
   // time and can be nullptr (during setup and teardown).
-  virtual RenderWidgetHostView* GetRenderWidgetHostView() const = 0;
+  virtual RenderWidgetHostView* GetRenderWidgetHostView() = 0;
 
   // Returns the outermost RenderWidgetHostView. This will return the platform
   // specific RenderWidgetHostView (as opposed to
@@ -358,22 +357,22 @@
 
   // Returns the currently active fullscreen widget. If there is none, returns
   // nullptr.
-  virtual RenderWidgetHostView* GetFullscreenRenderWidgetHostView() const = 0;
+  virtual RenderWidgetHostView* GetFullscreenRenderWidgetHostView() = 0;
 
   // Returns the theme color for the underlying content as set by the
   // theme-color meta tag.
-  virtual SkColor GetThemeColor() const = 0;
+  virtual SkColor GetThemeColor() = 0;
 
   // Returns the committed WebUI if one exists, otherwise the pending one.
-  virtual WebUI* GetWebUI() const = 0;
-  virtual WebUI* GetCommittedWebUI() const = 0;
+  virtual WebUI* GetWebUI() = 0;
+  virtual WebUI* GetCommittedWebUI() = 0;
 
   // Allows overriding the user agent used for NavigationEntries it owns.
   // |override_in_new_tabs| is set when we are overriding user agent for new
   // tabs.
   virtual void SetUserAgentOverride(const std::string& override,
                                     bool override_in_new_tabs) = 0;
-  virtual const std::string& GetUserAgentOverride() const = 0;
+  virtual const std::string& GetUserAgentOverride() = 0;
 
   // Set the accessibility mode so that accessibility events are forwarded
   // to each WebContentsObserver.
@@ -381,26 +380,26 @@
 
   // Returns true only if the WebContentsObserver accessibility mode is
   // enabled.
-  virtual bool IsWebContentsOnlyAccessibilityModeForTesting() const = 0;
+  virtual bool IsWebContentsOnlyAccessibilityModeForTesting() = 0;
 
   // Returns true only if complete accessibility mode is on, meaning there's
   // both renderer accessibility, and a native browser accessibility tree.
-  virtual bool IsFullAccessibilityModeForTesting() const = 0;
+  virtual bool IsFullAccessibilityModeForTesting() = 0;
 
-  virtual ui::AXMode GetAccessibilityMode() const = 0;
+  virtual ui::AXMode GetAccessibilityMode() = 0;
 
   virtual void SetAccessibilityMode(ui::AXMode mode) = 0;
 
   virtual base::string16 DumpAccessibilityTree(bool internal) = 0;
 
-  virtual const PageImportanceSignals& GetPageImportanceSignals() const = 0;
+  virtual const PageImportanceSignals& GetPageImportanceSignals() = 0;
 
   // Tab navigation state ------------------------------------------------------
 
   // Returns the current navigation properties, which if a navigation is
   // pending may be provisional (e.g., the navigation could result in a
   // download, in which case the URL would revert to what it was previously).
-  virtual const base::string16& GetTitle() const = 0;
+  virtual const base::string16& GetTitle() = 0;
 
   // Saves the given title to the navigation entry and does associated work. It
   // will update history and the view with the new title, and also synthesize
@@ -409,35 +408,35 @@
                                    const base::string16& title) = 0;
 
   // Returns the SiteInstance associated with the current page.
-  virtual SiteInstance* GetSiteInstance() const = 0;
+  virtual SiteInstance* GetSiteInstance() = 0;
 
   // Returns whether this WebContents is loading a resource.
-  virtual bool IsLoading() const = 0;
+  virtual bool IsLoading() = 0;
 
   // Returns the current load progress.
-  virtual double GetLoadProgress() const = 0;
+  virtual double GetLoadProgress() = 0;
 
   // Returns whether this WebContents is loading and and the load is to a
   // different top-level document (rather than being a navigation within the
   // same document) in the main frame. This being true implies that IsLoading()
   // is also true.
-  virtual bool IsLoadingToDifferentDocument() const = 0;
+  virtual bool IsLoadingToDifferentDocument() = 0;
 
   // Returns whether this WebContents is waiting for a first-response for the
   // main resource of the page.
-  virtual bool IsWaitingForResponse() const = 0;
+  virtual bool IsWaitingForResponse() = 0;
 
   // Returns the current load state and the URL associated with it.
   // The load state is only updated while IsLoading() is true.
-  virtual const net::LoadStateWithParam& GetLoadState() const = 0;
-  virtual const base::string16& GetLoadStateHost() const = 0;
+  virtual const net::LoadStateWithParam& GetLoadState() = 0;
+  virtual const base::string16& GetLoadStateHost() = 0;
 
   // Returns the upload progress.
-  virtual uint64_t GetUploadSize() const = 0;
-  virtual uint64_t GetUploadPosition() const = 0;
+  virtual uint64_t GetUploadSize() = 0;
+  virtual uint64_t GetUploadPosition() = 0;
 
   // Returns the character encoding of the page.
-  virtual const std::string& GetEncoding() const = 0;
+  virtual const std::string& GetEncoding() = 0;
 
   // Indicates that the tab was previously discarded.
   // wasDiscarded is exposed on Document after discard, see:
@@ -461,10 +460,10 @@
   // returned by GetPreferredSize() until all captures have ended.
   virtual void IncrementCapturerCount(const gfx::Size& capture_size) = 0;
   virtual void DecrementCapturerCount() = 0;
-  virtual bool IsBeingCaptured() const = 0;
+  virtual bool IsBeingCaptured() = 0;
 
   // Indicates/Sets whether all audio output from this WebContents is muted.
-  virtual bool IsAudioMuted() const = 0;
+  virtual bool IsAudioMuted() = 0;
   virtual void SetAudioMuted(bool mute) = 0;
 
   // Returns true if the audio is currently audible.
@@ -472,21 +471,21 @@
 
   // Indicates whether any frame in the WebContents is connected to a Bluetooth
   // Device.
-  virtual bool IsConnectedToBluetoothDevice() const = 0;
+  virtual bool IsConnectedToBluetoothDevice() = 0;
 
   // Indicates whether a video is in Picture-in-Picture for |this|.
-  virtual bool HasPictureInPictureVideo() const = 0;
+  virtual bool HasPictureInPictureVideo() = 0;
 
   // Indicates whether this tab should be considered crashed. The setter will
   // also notify the delegate when the flag is changed.
-  virtual bool IsCrashed() const = 0;
+  virtual bool IsCrashed() = 0;
   virtual void SetIsCrashed(base::TerminationStatus status, int error_code) = 0;
 
-  virtual base::TerminationStatus GetCrashedStatus() const = 0;
-  virtual int GetCrashedErrorCode() const = 0;
+  virtual base::TerminationStatus GetCrashedStatus() = 0;
+  virtual int GetCrashedErrorCode() = 0;
 
   // Whether the tab is in the process of being destroyed.
-  virtual bool IsBeingDestroyed() const = 0;
+  virtual bool IsBeingDestroyed() = 0;
 
   // Convenience method for notifying the delegate of a navigation state
   // change.
@@ -499,7 +498,7 @@
 
   // Get/Set the last time that the WebContents was made active (either when it
   // was created or shown with WasShown()).
-  virtual base::TimeTicks GetLastActiveTime() const = 0;
+  virtual base::TimeTicks GetLastActiveTime() = 0;
 
   // Invoked when the WebContents becomes shown/hidden. A hidden WebContents
   // isn't painted on the screen.
@@ -512,7 +511,7 @@
   virtual void WasOccluded() = 0;
 
   // Returns the visibility of the WebContents' view.
-  virtual Visibility GetVisibility() const = 0;
+  virtual Visibility GetVisibility() = 0;
 
   // Returns true if the before unload and unload listeners need to be
   // fired. The value of this changes over time. For example, if true and the
@@ -537,7 +536,7 @@
 
   // Returns the outer WebContents of this WebContents if any.
   // Otherwise, return nullptr.
-  virtual WebContents* GetOuterWebContents() const = 0;
+  virtual WebContents* GetOuterWebContents() = 0;
 
   // Returns the root WebContents of the WebContents tree. Always returns
   // non-null value.
@@ -659,7 +658,7 @@
   // Interstitials -------------------------------------------------------------
 
   // Various other systems need to know about our interstitials.
-  virtual bool ShowingInterstitialPage() const = 0;
+  virtual bool ShowingInterstitialPage() = 0;
 
   // Returns the currently visible interstitial, nullptr if no interstitial is
   // visible. Note: This returns nullptr from the time the interstitial page has
@@ -667,7 +666,7 @@
   // interstitial is displayed.
   //
   // Compare to InterstitialPage::GetInterstitialPage.
-  virtual InterstitialPage* GetInterstitialPage() const = 0;
+  virtual InterstitialPage* GetInterstitialPage() = 0;
 
   // Misc state & callbacks ----------------------------------------------------
 
@@ -709,10 +708,10 @@
       base::OnceCallback<void(int64_t /* size of the file */)> callback) = 0;
 
   // Returns the contents MIME type after a navigation.
-  virtual const std::string& GetContentsMimeType() const = 0;
+  virtual const std::string& GetContentsMimeType() = 0;
 
   // Returns true if this WebContents will notify about disconnection.
-  virtual bool WillNotifyDisconnection() const = 0;
+  virtual bool WillNotifyDisconnection() = 0;
 
   // Returns the settings which get passed to the renderer.
   virtual content::RendererPreferences* GetMutableRendererPrefs() = 0;
@@ -734,17 +733,17 @@
   // such as closing the window.  The setter is maintained by TabStripModel, and
   // the getter only useful from within TAB_CLOSED notification
   virtual void SetClosedByUserGesture(bool value) = 0;
-  virtual bool GetClosedByUserGesture() const = 0;
+  virtual bool GetClosedByUserGesture() = 0;
 
   // Gets the minimum/maximum zoom percent.
-  virtual int GetMinimumZoomPercent() const = 0;
-  virtual int GetMaximumZoomPercent() const = 0;
+  virtual int GetMinimumZoomPercent() = 0;
+  virtual int GetMaximumZoomPercent() = 0;
 
   // Set the renderer's page scale to the given factor.
   virtual void SetPageScale(float page_scale_factor) = 0;
 
   // Gets the preferred size of the contents.
-  virtual gfx::Size GetPreferredSize() const = 0;
+  virtual gfx::Size GetPreferredSize() = 0;
 
   // Called when the response to a pending mouse lock request has arrived.
   // Returns true if |allowed| is true and the mouse has been successfully
@@ -768,10 +767,10 @@
 
   // Does this have an opener (corresponding to window.opener in JavaScript)
   // associated with it?
-  virtual bool HasOpener() const = 0;
+  virtual bool HasOpener() = 0;
 
   // Returns the opener if HasOpener() is true, or nullptr otherwise.
-  virtual RenderFrameHost* GetOpener() const = 0;
+  virtual RenderFrameHost* GetOpener() = 0;
 
   // Returns true if this WebContents was opened by another WebContents, even
   // if the opener was suppressed. In contrast to HasOpener/GetOpener, the
@@ -779,11 +778,11 @@
   // updated. This traces all the way back, so if the original owner was closed,
   // but _it_ had an original owner, this will return the original owner's
   // original owner, etc.
-  virtual bool HasOriginalOpener() const = 0;
+  virtual bool HasOriginalOpener() = 0;
 
   // Returns the original opener if HasOriginalOpener() is true, or nullptr
   // otherwise.
-  virtual RenderFrameHost* GetOriginalOpener() const = 0;
+  virtual RenderFrameHost* GetOriginalOpener() = 0;
 
   // Returns the WakeLockContext accociated with this WebContents.
   virtual device::mojom::WakeLockContext* GetWakeLockContext() = 0;
@@ -839,7 +838,7 @@
   virtual void GetManifest(GetManifestCallback callback) = 0;
 
   // Returns whether the renderer is in fullscreen mode.
-  virtual bool IsFullscreenForCurrentTab() const = 0;
+  virtual bool IsFullscreenForCurrentTab() = 0;
 
   // Requests the renderer to exit fullscreen.
   // |will_cause_resize| indicates whether the fullscreen change causes a
@@ -872,7 +871,7 @@
   virtual bool IsFocusedElementEditable() = 0;
 
   // Returns true if a context menu is showing on the page.
-  virtual bool IsShowingContextMenu() const = 0;
+  virtual bool IsShowingContextMenu() = 0;
 
   // Tells the WebContents whether the context menu is showing.
   virtual void SetShowingContextMenu(bool showing) = 0;
@@ -918,14 +917,14 @@
 
   // Returns true if the WebContents has completed its first meaningful paint
   // since the last navigation.
-  virtual bool CompletedFirstVisuallyNonEmptyPaint() const = 0;
+  virtual bool CompletedFirstVisuallyNonEmptyPaint() = 0;
 
   // TODO(https://crbug.com/826293): This is a simple mitigation to validate
   // that an action that requires a user gesture actually has one in the
   // trustworthy browser process, rather than relying on the untrustworthy
   // renderer. This should be eventually merged into and accounted for in the
   // user activation work.
-  virtual bool HasRecentInteractiveInputEvent() const = 0;
+  virtual bool HasRecentInteractiveInputEvent() = 0;
 
   // Sets a flag that causes the WebContents to ignore input events.
   virtual void SetIgnoreInputEvents(bool ignore_input_events) = 0;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 84e89a6..35fe221 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1467,6 +1467,15 @@
     // pulling the device scale factor off the WebView itself.
     render_widget->UpdateWebViewWithDeviceScaleFactor();
 
+    // It may be questionable, since we create un-frozen RenderWidgets at this
+    // point for subframes, but we don't un-freeze the main frame's RenderWidget
+    // here, instead deferring until the non-provisional frame is swapped in.
+    // But we do need to start the creating compositor resources in parallel to
+    // the navigation being done with the provisional frame, so we inform the
+    // frozen RenderWidget to get prepared. We must abort this if we are no
+    // longer planning to un-freeze the RenderWidget (ie in FrameDetached).
+    render_widget->WarmupCompositor();
+
     render_frame->render_widget_ = render_widget;
   } else if (widget_params.routing_id != MSG_ROUTING_NONE) {
     // This frame is a child local root, so we require a separate RenderWidget
@@ -2254,7 +2263,7 @@
   // Now that all of the cleanup is complete and the browser side is notified,
   // start using the RenderFrameProxy.
   //
-  // The swap call deletes this RenderFrame via frameDetached.  Do not access
+  // The swap call deletes this RenderFrame via FrameDetached.  Do not access
   // any members after this call.
   //
   // TODO(creis): WebFrame::swap() can return false.  Most of those cases
@@ -2304,7 +2313,7 @@
 }
 
 void RenderFrameImpl::OnDeleteFrame() {
-  // This will result in a call to RenderFrameImpl::frameDetached, which
+  // This will result in a call to RenderFrameImpl::FrameDetached, which
   // deletes the object. Do not access |this| after detach.
   frame_->Detach();
 }
@@ -2763,46 +2772,25 @@
   if (!document_loader)
     return;
 
-  const WebURLRequest& failed_request = document_loader->GetRequest();
-
   // Notify the browser that we failed a provisional load with an error.
-  SendFailedProvisionalLoad(failed_request, error, frame_);
+  SendFailedProvisionalLoad(document_loader->GetRequest(), error, frame_);
 
   if (!ShouldDisplayErrorPageForFailedLoad(error.reason(), error.url()))
     return;
 
-  NavigationState* navigation_state =
-      NavigationState::FromDocumentLoader(document_loader);
-
-  std::unique_ptr<blink::WebNavigationParams> navigation_params;
-  std::unique_ptr<DocumentState> document_state;
-
-  // If we failed on a browser initiated request, then make sure that our error
-  // page load is regarded as the same browser initiated request.
-  if (!navigation_state->IsContentInitiated()) {
-    document_state = BuildDocumentStateFromParams(
-        navigation_state->common_params(), navigation_state->request_params(),
-        base::TimeTicks(),  // Not used for failed navigation.
-        CommitNavigationCallback(), nullptr);
-    navigation_params = BuildNavigationParams(
-        navigation_state->common_params(), navigation_state->request_params(),
-        BuildServiceWorkerNetworkProviderForNavigation(
-            nullptr /* request_params */,
-            nullptr /* controller_service_worker_info */));
-  }
-
   // If this is a failed back/forward/reload navigation, then we need to do a
   // 'replace' load. This is necessary to avoid messing up session history.
   // Otherwise, we do a normal load, which simulates a 'go' navigation as far
   // as session history is concerned.
-  bool replace = commit_type != blink::kWebStandardCommit;
+  bool replace_current_item = commit_type != blink::kWebStandardCommit;
 
-  std::string error_html;
-  GetContentClient()->renderer()->PrepareErrorPage(this, failed_request, error,
-                                                   &error_html);
-  LoadNavigationErrorPage(error_html, error.url(), replace, nullptr,
-                          std::move(navigation_params),
-                          std::move(document_state), &failed_request);
+  // If we failed on a browser initiated request, then make sure that our error
+  // page load is regarded as the same browser initiated request.
+  bool inherit_document_state =
+      !NavigationState::FromDocumentLoader(document_loader)
+           ->IsContentInitiated();
+  LoadNavigationErrorPage(document_loader, error, base::nullopt,
+                          replace_current_item, inherit_document_state);
 }
 
 void RenderFrameImpl::NotifyObserversOfFailedProvisionalLoad(
@@ -2814,41 +2802,53 @@
 }
 
 void RenderFrameImpl::LoadNavigationErrorPage(
-    const std::string& error_html,
-    const GURL& error_url,
-    bool replace,
-    HistoryEntry* history_entry,
-    std::unique_ptr<blink::WebNavigationParams> navigation_params,
-    std::unique_ptr<blink::WebDocumentLoader::ExtraData> navigation_data,
-    const WebURLRequest* failed_request) {
+    WebDocumentLoader* document_loader,
+    const WebURLError& error,
+    const base::Optional<std::string>& error_page_content,
+    bool replace_current_item,
+    bool inherit_document_state) {
+  WebURLRequest failed_request = document_loader->GetRequest();
+
+  std::string error_html;
+  if (error_page_content) {
+    error_html = error_page_content.value();
+  } else {
+    GetContentClient()->renderer()->PrepareErrorPage(this, failed_request,
+                                                     error, &error_html);
+  }
+
+  std::unique_ptr<blink::WebNavigationParams> navigation_params;
+  std::unique_ptr<DocumentState> document_state;
+
+  if (inherit_document_state) {
+    NavigationState* navigation_state =
+        NavigationState::FromDocumentLoader(document_loader);
+    document_state = BuildDocumentStateFromParams(
+        navigation_state->common_params(), navigation_state->request_params(),
+        base::TimeTicks(),  // Not used for failed navigation.
+        CommitNavigationCallback(), nullptr);
+    navigation_params = BuildNavigationParams(
+        navigation_state->common_params(), navigation_state->request_params(),
+        BuildServiceWorkerNetworkProviderForNavigation(
+            nullptr /* request_params */,
+            nullptr /* controller_service_worker_info */));
+  } else {
+    document_state = BuildDocumentState();
+  }
+
   // Make sure we never show errors in view source mode.
   frame_->EnableViewSourceMode(false);
 
-  blink::WebFrameLoadType frame_load_type =
-      history_entry ? blink::WebFrameLoadType::kBackForward
-                    : blink::WebFrameLoadType::kStandard;
-  const blink::WebHistoryItem& history_item =
-      history_entry ? history_entry->root() : blink::WebHistoryItem();
-  if (replace && frame_load_type == WebFrameLoadType::kStandard)
-    frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
+  // Locally generated error pages should not be cached.
+  failed_request.SetCacheMode(blink::mojom::FetchCacheMode::kNoStore);
+  failed_request.SetURL(GURL(kUnreachableWebDataURL));
 
-  // Failed navigations will always provide a |failed_request|.  Error induced
-  // by the client/renderer side after a commit won't have a |failed_request|.
-  bool is_client_redirect = !failed_request;
-
-  WebURLRequest new_request;
-  if (failed_request)
-    new_request = *failed_request;
-  new_request.SetURL(GURL(kUnreachableWebDataURL));
-
-  // Locally generated error pages should not be cached (in particular they
-  // should not inherit the cache mode from |failed_request|).
-  new_request.SetCacheMode(blink::mojom::FetchCacheMode::kNoStore);
-
-  frame_->CommitDataNavigation(new_request, error_html, "text/html", "UTF-8",
-                               error_url, frame_load_type, history_item,
-                               is_client_redirect, std::move(navigation_params),
-                               std::move(navigation_data));
+  frame_->CommitDataNavigation(
+      failed_request, error_html, "text/html", "UTF-8", error.url(),
+      replace_current_item ? WebFrameLoadType::kReplaceCurrentItem
+                           : WebFrameLoadType::kStandard,
+      WebHistoryItem(), false /* is_client_redirect */,
+      std::move(navigation_params), std::move(document_state));
 }
 
 void RenderFrameImpl::DidMeaningfulLayout(
@@ -2964,16 +2964,10 @@
 }
 
 void RenderFrameImpl::LoadErrorPage(int reason) {
-  WebURLError error(reason, frame_->GetDocument().Url());
-
-  std::string error_html;
-  GetContentClient()->renderer()->PrepareErrorPage(
-      this, frame_->GetDocumentLoader()->GetRequest(), error, &error_html);
-
-  LoadNavigationErrorPage(
-      error_html, error.url(), true /* replace */, nullptr /* history_entry */,
-      nullptr /* navigation_params */, nullptr /* navigation_data */,
-      nullptr /* failed_request */);
+  LoadNavigationErrorPage(frame_->GetDocumentLoader(),
+                          WebURLError(reason, frame_->GetDocument().Url()),
+                          base::nullopt, true /* replace_current_item */,
+                          false /* inherit_document_state */);
 }
 
 void RenderFrameImpl::ExecuteJavaScript(const base::string16& javascript) {
@@ -3452,21 +3446,29 @@
                                                      error, &error_html);
   }
 
+  // Make sure we never show errors in view source mode.
+  frame_->EnableViewSourceMode(false);
+
+  WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
+  if (history_entry)
+    frame_load_type = WebFrameLoadType::kBackForward;
+  else if (replace)
+    frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
+
+  failed_request.SetURL(GURL(kUnreachableWebDataURL));
+  failed_request.SetCacheMode(blink::mojom::FetchCacheMode::kNoStore);
+
   // The load of the error page can result in this frame being removed.
   // Use a WeakPtr as an easy way to detect whether this has occured. If so,
   // this method should return immediately and not touch any part of the object,
   // otherwise it will result in a use-after-free bug.
   base::WeakPtr<RenderFrameImpl> weak_this = weak_factory_.GetWeakPtr();
-
-  // Don't pass history entry for renderer initiated navigations.
-  bool pass_history_entry = request_params.nav_entry_id != 0;
-  // TODO(dgozman): if this DCHECK never triggers, we can just pass
-  // |history_entry| unconditionally.
-  DCHECK(pass_history_entry || !history_entry);
-  LoadNavigationErrorPage(error_html, error.url(), replace,
-                          pass_history_entry ? history_entry.get() : nullptr,
-                          std::move(navigation_params),
-                          std::move(document_state), &failed_request);
+  frame_->CommitDataNavigation(
+      failed_request, error_html, "text/html", "UTF-8", error.url(),
+      frame_load_type,
+      history_entry ? history_entry->root() : blink::WebHistoryItem(),
+      false /* is_client_redirect */, std::move(navigation_params),
+      std::move(document_state));
   if (!weak_this)
     return;
 
@@ -3981,6 +3983,14 @@
     // closing the RenderWidget we only drop the WebFrameWidget in order to also
     // drop its reference on the WebLocalFrameImpl for this detaching frame.
     render_view_->DetachWebFrameWidget();
+    // In the main frame case, we WarmupCompositor() when setting up the
+    // WebFrameWidget, because we can't unfreeze the RenderWidget until
+    // navigation completes. If that navigation aborts then we detach the
+    // provisional main frame, and drop the WebFrameWidget. Since we then no
+    // longer expect to use this RenderWidget immediately, we drop any resources
+    // that were being prepared. This is a no-op if the RenderWidget was
+    // unfrozen and not in a warming up state.
+    render_widget_->AbortWarmupCompositor();
   } else if (render_widget_) {
     // This closes/deletes the RenderWidget if this frame was a local root.
     render_widget_->CloseForFrame();
@@ -4529,23 +4539,22 @@
           frame_->GetDocumentLoader());
   int http_status_code = internal_data->http_status_code();
   if (GetContentClient()->renderer()->HasErrorPage(http_status_code)) {
-    // This call may run scripts, e.g. via the beforeunload event.
-    std::unique_ptr<DocumentState> document_state(BuildDocumentState());
-    std::unique_ptr<blink::WebNavigationParams> navigation_params =
-        std::make_unique<blink::WebNavigationParams>();
-    navigation_params->service_worker_network_provider =
-        BuildServiceWorkerNetworkProviderForNavigation(
-            nullptr /* request_params */, nullptr /* controller_info */);
-    WebURLRequest failed_request = frame_->GetDocumentLoader()->GetRequest();
+    WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
     WebURL unreachable_url = frame_->GetDocument().Url();
     std::string error_html;
     GetContentClient()->renderer()->PrepareErrorPageForHttpStatusError(
-        this, failed_request, unreachable_url, http_status_code, &error_html);
-    LoadNavigationErrorPage(error_html, unreachable_url, true /* replace */,
-                            nullptr /* entry */, std::move(navigation_params),
-                            std::move(document_state), &failed_request);
+        this, document_loader->GetRequest(), unreachable_url, http_status_code,
+        &error_html);
+    // This call may run scripts, e.g. via the beforeunload event, and possibly
+    // delete |this|.
+    LoadNavigationErrorPage(document_loader,
+                            WebURLError(net::ERR_FAILED, unreachable_url),
+                            error_html, true /* replace_current_item */,
+                            false /* inherit_document_state */);
+    if (!weak_self)
+      return;
+    // Do not use |this| or |frame_| here without checking |weak_self|.
   }
-  // Do not use |this| or |frame_| here without checking |weak_self|.
 }
 
 void RenderFrameImpl::RunScriptsAtDocumentIdle() {
@@ -5896,7 +5905,7 @@
 
   // The proxy should always exist.  If it was detached while the provisional
   // LocalFrame was being navigated, the provisional frame would've been
-  // cleaned up by RenderFrameProxy::frameDetached.  See
+  // cleaned up by RenderFrameProxy::FrameDetached.  See
   // https://crbug.com/526304 and https://crbug.com/568676 for context.
   RenderFrameProxy* proxy = RenderFrameProxy::FromRoutingID(proxy_routing_id_);
   CHECK(proxy);
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 8359b31..5106218 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -127,7 +127,6 @@
 struct FramePolicy;
 struct WebContextMenuData;
 struct WebCursorInfo;
-struct WebNavigationParams;
 struct WebMediaPlayerAction;
 struct WebImeTextSpan;
 struct WebScrollIntoViewParams;
@@ -160,7 +159,6 @@
 class CompositorDependencies;
 class ExternalPopupMenu;
 class FrameRequestBlocker;
-class HistoryEntry;
 class ManifestManager;
 class MediaPermissionDispatcher;
 class MediaStreamDeviceObserver;
@@ -1130,15 +1128,12 @@
                            base::string16* result);
 
   // Loads the appropriate error page for the specified failure into the frame.
-  // |entry| is only when navigating to a history item.
   void LoadNavigationErrorPage(
-      const std::string& error_html,
-      const GURL& error_url,
-      bool replace,
-      HistoryEntry* history_entry,
-      std::unique_ptr<blink::WebNavigationParams> navigation_params,
-      std::unique_ptr<blink::WebDocumentLoader::ExtraData> navigation_data,
-      const blink::WebURLRequest* failed_request);
+      blink::WebDocumentLoader* document_loader,
+      const blink::WebURLError& error,
+      const base::Optional<std::string>& error_page_content,
+      bool replace_current_item,
+      bool inherit_document_state);
 
   void HandleJavascriptExecutionResult(const base::string16& javascript,
                                        int id,
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 6e67445..68682fd 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1888,7 +1888,7 @@
 }
 
 void RenderThreadImpl::RequestNewLayerTreeFrameSink(
-    int routing_id,
+    int widget_routing_id,
     scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue,
     const GURL& url,
     LayerTreeFrameSinkCallback callback,
@@ -1933,7 +1933,7 @@
 
 #if defined(USE_AURA)
   if (features::IsMultiProcessMash()) {
-    if (!RendererWindowTreeClient::Get(routing_id)) {
+    if (!RendererWindowTreeClient::Get(widget_routing_id)) {
       std::move(callback).Run(nullptr);
       return;
     }
@@ -1944,12 +1944,13 @@
       std::move(callback).Run(nullptr);
       return;
     }
-    RendererWindowTreeClient::Get(routing_id)
+    RendererWindowTreeClient::Get(widget_routing_id)
         ->RequestLayerTreeFrameSink(
             gpu_->CreateContextProvider(std::move(channel)),
             GetGpuMemoryBufferManager(), std::move(callback));
     frame_sink_provider_->RegisterRenderFrameMetadataObserver(
-        routing_id, std::move(render_frame_metadata_observer_client_request),
+        widget_routing_id,
+        std::move(render_frame_metadata_observer_client_request),
         std::move(render_frame_metadata_observer_ptr));
     return;
   }
@@ -1964,10 +1965,11 @@
   if (is_gpu_compositing_disabled_) {
     DCHECK(!layout_test_mode());
     frame_sink_provider_->CreateForWidget(
-        routing_id, std::move(compositor_frame_sink_request),
+        widget_routing_id, std::move(compositor_frame_sink_request),
         std::move(compositor_frame_sink_client));
     frame_sink_provider_->RegisterRenderFrameMetadataObserver(
-        routing_id, std::move(render_frame_metadata_observer_client_request),
+        widget_routing_id,
+        std::move(render_frame_metadata_observer_client_request),
         std::move(render_frame_metadata_observer_ptr));
     std::move(callback).Run(
         std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
@@ -2026,9 +2028,9 @@
   if (layout_test_deps_) {
     if (!layout_test_deps_->UseDisplayCompositorPixelDump()) {
       std::move(callback).Run(layout_test_deps_->CreateLayerTreeFrameSink(
-          routing_id, std::move(gpu_channel_host), std::move(context_provider),
-          std::move(worker_context_provider), GetGpuMemoryBufferManager(),
-          this));
+          widget_routing_id, std::move(gpu_channel_host),
+          std::move(context_provider), std::move(worker_context_provider),
+          GetGpuMemoryBufferManager(), this));
       return;
     } else if (!params.compositor_task_runner) {
       // The frame sink provider expects a compositor task runner, but we might
@@ -2040,12 +2042,13 @@
 
 #if defined(OS_ANDROID)
   if (GetContentClient()->UsingSynchronousCompositing()) {
-    RenderWidget* widget = RenderWidget::FromRoutingID(routing_id);
+    RenderWidget* widget = RenderWidget::FromRoutingID(widget_routing_id);
     if (widget) {
       std::move(callback).Run(std::make_unique<SynchronousLayerTreeFrameSink>(
           std::move(context_provider), std::move(worker_context_provider),
           compositor_task_runner_, GetGpuMemoryBufferManager(),
-          sync_message_filter(), routing_id, g_next_layer_tree_frame_sink_id++,
+          sync_message_filter(), widget_routing_id,
+          g_next_layer_tree_frame_sink_id++,
           std::move(params.synthetic_begin_frame_source),
           widget->widget_input_handler_manager()
               ->GetSynchronousCompositorRegistry(),
@@ -2057,10 +2060,11 @@
   }
 #endif
   frame_sink_provider_->CreateForWidget(
-      routing_id, std::move(compositor_frame_sink_request),
+      widget_routing_id, std::move(compositor_frame_sink_request),
       std::move(compositor_frame_sink_client));
   frame_sink_provider_->RegisterRenderFrameMetadataObserver(
-      routing_id, std::move(render_frame_metadata_observer_client_request),
+      widget_routing_id,
+      std::move(render_frame_metadata_observer_client_request),
       std::move(render_frame_metadata_observer_ptr));
   params.gpu_memory_buffer_manager = GetGpuMemoryBufferManager();
   std::move(callback).Run(
@@ -2076,11 +2080,12 @@
 
 std::unique_ptr<cc::SwapPromise>
 RenderThreadImpl::RequestCopyOfOutputForLayoutTest(
-    int32_t routing_id,
+    int32_t widget_routing_id,
     std::unique_ptr<viz::CopyOutputRequest> request) {
   DCHECK(layout_test_deps_ &&
          !layout_test_deps_->UseDisplayCompositorPixelDump());
-  return layout_test_deps_->RequestCopyOfOutput(routing_id, std::move(request));
+  return layout_test_deps_->RequestCopyOfOutput(widget_routing_id,
+                                                std::move(request));
 }
 
 std::unique_ptr<blink::WebMediaStreamCenter>
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index a831902..a9d0667 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -268,7 +268,7 @@
   using LayerTreeFrameSinkCallback =
       base::OnceCallback<void(std::unique_ptr<cc::LayerTreeFrameSink>)>;
   void RequestNewLayerTreeFrameSink(
-      int routing_id,
+      int widget_routing_id,
       scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue,
       const GURL& url,
       LayerTreeFrameSinkCallback callback,
@@ -280,7 +280,7 @@
   blink::AssociatedInterfaceRegistry* GetAssociatedInterfaceRegistry();
 
   std::unique_ptr<cc::SwapPromise> RequestCopyOfOutputForLayoutTest(
-      int32_t routing_id,
+      int32_t widget_routing_id,
       std::unique_ptr<viz::CopyOutputRequest> request);
 
   // True if we are running layout tests. This currently disables forwarding
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index ec352c5..68f6d85 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -421,6 +421,7 @@
       was_shown_time_(base::TimeTicks::Now()),
       current_content_source_id_(0),
       widget_binding_(this, std::move(widget_request)),
+      warmup_weak_ptr_factory_(this),
       weak_ptr_factory_(this) {
   DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
   DCHECK(RenderThread::Get());
@@ -972,7 +973,7 @@
   // For widgets that are never visible, we don't start the compositor, so we
   // never get a request for a cc::LayerTreeFrameSink.
   DCHECK(!compositor_never_visible_);
-  // Inactive RenderWidgets should not be doing any compositing.
+  // Frozen RenderWidgets should not be doing any compositing.
   DCHECK(!is_frozen_);
 
   if (is_closing()) {
@@ -990,6 +991,23 @@
   if (for_child_local_root_frame_)
     CHECK(GetFrameWidget());
 
+  // If we have a warmup in progress, wait for that and store the callback
+  // to be run when the warmup completes.
+  if (warmup_frame_sink_request_pending_) {
+    after_warmup_callback_ = std::move(callback);
+    return;
+  }
+  // If a warmup previously completed, use the result.
+  if (warmup_frame_sink_) {
+    std::move(callback).Run(std::move(warmup_frame_sink_));
+    return;
+  }
+
+  DoRequestNewLayerTreeFrameSink(std::move(callback));
+}
+
+void RenderWidget::DoRequestNewLayerTreeFrameSink(
+    LayerTreeFrameSinkCallback callback) {
   // TODO(jonross): have this generated by the LayerTreeFrameSink itself, which
   // would then handle binding.
   mojom::RenderFrameMetadataObserverPtr ptr;
@@ -1613,6 +1631,55 @@
     StartCompositor();
 }
 
+void RenderWidget::WarmupCompositor() {
+  DCHECK(is_frozen_);
+
+  // Tests have no RenderThreadImpl /o\.
+  if (!RenderThreadImpl::current())
+    return;
+
+  // Keeping things simple. This would cancel any outstanding warmup if we
+  // happened to have one (this should be basically impossible). This avoids any
+  // extra book keeping about the outstanding reqeust.
+  warmup_weak_ptr_factory_.InvalidateWeakPtrs();
+  // And if we already did a warmup then we're done.
+  if (warmup_frame_sink_)
+    return;
+
+  // Mark us pending the warmup frame sink *before* calling
+  // DoRequestNewLayerTreeFrameSink() as it may run the reply callback
+  // synchronously. So we don't want to change any state after the call
+  // to DoRequestNewLayerTreeFrameSink() here.
+  warmup_frame_sink_request_pending_ = true;
+
+  auto cb = base::BindOnce(&RenderWidget::OnReplyForWarmupCompositor,
+                           warmup_weak_ptr_factory_.GetWeakPtr());
+  DoRequestNewLayerTreeFrameSink(std::move(cb));
+}
+
+void RenderWidget::OnReplyForWarmupCompositor(
+    std::unique_ptr<cc::LayerTreeFrameSink> sink) {
+  warmup_frame_sink_request_pending_ = false;
+
+  if (after_warmup_callback_)
+    std::move(after_warmup_callback_).Run(std::move(sink));
+  else
+    warmup_frame_sink_ = std::move(sink);
+}
+
+void RenderWidget::AbortWarmupCompositor() {
+  warmup_frame_sink_request_pending_ = false;
+  // Drop any pending warmup.
+  warmup_weak_ptr_factory_.InvalidateWeakPtrs();
+  // And drop any completed one.
+  warmup_frame_sink_.reset();
+
+  // If we had saved a callback to run after warmup, just do so now indicating
+  // failure.
+  if (after_warmup_callback_)
+    std::move(after_warmup_callback_).Run(nullptr);
+}
+
 void RenderWidget::DoDeferredClose() {
   Send(new WidgetHostMsg_Close(routing_id_));
 }
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index e181fd9..db21d9a 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -240,6 +240,20 @@
   void SetIsFrozen(bool is_frozen);
   bool is_frozen() const { return is_frozen_; }
 
+  // When a RenderWidget is created, even if frozen, if we expect to unfreeze
+  // and use the RenderWidget imminently, then we want to pre-emptively start
+  // the process of getting the resources needed for the compositor. This helps
+  // to parallelize the critical path to first pixels with the loading process.
+  // This should only be called when the RenderWidget is frozen, otherwise it
+  // would be redundant at best. Non-frozen RenderWidgets will start to warmup
+  // immediately on their own.
+  void WarmupCompositor();
+  // If after calling WarmupCompositor() we can determine that the RenderWidget
+  // does not expect to be used shortly after all, call this to cancel the
+  // warmup process and release any unused resources that had been created by
+  // it.
+  void AbortWarmupCompositor();
+
   // This is true once a Close IPC has been received. The actual action of
   // closing must be done on another stack frame, in case the IPC receipt
   // is in a nested message loop and will unwind back up to javascript (from
@@ -752,6 +766,15 @@
   // belongs to the frame tree associated with this RenderWidget.
   blink::WebLocalFrame* GetFocusedWebLocalFrameInWidget() const;
 
+  // Called with the resulting frame sink from WarmupCompositor() since frame
+  // sink creation can be asynchronous.
+  void OnReplyForWarmupCompositor(std::unique_ptr<cc::LayerTreeFrameSink> sink);
+
+  // Common code shared to execute the creation of a LayerTreeFrameSink, shared
+  // by the warmup and standard request paths. Callers should verify they really
+  // want to do this before calling it as this method does no verification.
+  void DoRequestNewLayerTreeFrameSink(LayerTreeFrameSinkCallback callback);
+
   // Routing ID that allows us to communicate to the parent browser process
   // RenderWidgetHost.
   const int32_t routing_id_;
@@ -925,6 +948,18 @@
   // Wraps the |webwidget_| as a MouseLockDispatcher::LockTarget interface.
   std::unique_ptr<MouseLockDispatcher::LockTarget> webwidget_mouse_lock_target_;
 
+  // Set to true while a warmup is in progress. Set to false if the warmup is
+  // completed or aborted. If aborted, the reply callback is also cancelled by
+  // invalidating the |warmup_weak_ptr_factory_|.
+  bool warmup_frame_sink_request_pending_ = false;
+  // Set after warmup completes without being aborted. This frame sink will be
+  // returned on the next request for a frame sink instead of creating a new
+  // one.
+  std::unique_ptr<cc::LayerTreeFrameSink> warmup_frame_sink_;
+  // Set if a request for a frame sink arrives while a warmup is in progress.
+  // Then this stores the request to be satisfied once the warmup completes.
+  LayerTreeFrameSinkCallback after_warmup_callback_;
+
   viz::LocalSurfaceIdAllocation local_surface_id_allocation_from_parent_;
 
   // Indicates whether this widget has focus.
@@ -994,6 +1029,10 @@
 
   uint32_t last_capture_sequence_number_ = 0u;
 
+  // Used to generate a callback for the reply when making the warmup frame
+  // sink, and to cancel that callback if the warmup is aborted.
+  base::WeakPtrFactory<RenderWidget> warmup_weak_ptr_factory_;
+
   base::WeakPtrFactory<RenderWidget> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidget);
diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc
index ed59ce3..e9a1225 100644
--- a/content/renderer/renderer_main.cc
+++ b/content/renderer/renderer_main.cc
@@ -35,6 +35,7 @@
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/renderer_main_platform_delegate.h"
 #include "media/media_buildflags.h"
+#include "mojo/public/cpp/bindings/mojo_buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "services/service_manager/sandbox/switches.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
@@ -59,6 +60,10 @@
 #include "content/renderer/pepper/pepper_plugin_registry.h"
 #endif
 
+#if BUILDFLAG(MOJO_RANDOM_DELAYS_ENABLED)
+#include "mojo/public/cpp/bindings/lib/test_random_mojo_delays.h"
+#endif
+
 namespace content {
 namespace {
 
@@ -208,6 +213,10 @@
     if (need_sandbox)
       should_run_loop = platform.EnableSandbox();
 
+#if BUILDFLAG(MOJO_RANDOM_DELAYS_ENABLED)
+    mojo::BeginRandomMojoDelays();
+#endif
+
     base::HighResolutionTimerManager hi_res_timer_manager;
 
     if (should_run_loop) {
diff --git a/content/test/data/browsing_data/media_license.html b/content/test/data/browsing_data/media_license.html
new file mode 100644
index 0000000..5768f2a
--- /dev/null
+++ b/content/test/data/browsing_data/media_license.html
@@ -0,0 +1,81 @@
+<html>
+<script>
+
+  function success_() {
+    domAutomationController.send(true);
+  }
+
+  function failure_() {
+    domAutomationController.send(false);
+  }
+
+  // EME creates session IDs dynamically, so we have no idea what it will be.
+  // As the tests only need to create a single session, keep track of the
+  // last session ID created.
+  var savedSessionId = 'UnknownSessionId';
+
+  function createPersistentSession() {
+    // This function creates a persistent-license type session, and resolves
+    // with the created session object on success.
+    return navigator.requestMediaKeySystemAccess(
+      'org.chromium.externalclearkey', [{
+        initDataTypes: ['keyids'],
+        audioCapabilities: [
+          // Include a set of codecs that should cover all user agents.
+          {contentType: 'audio/mp4; codecs="mp4a.40.2"'},
+          {contentType: 'audio/webm; codecs="opus"'}
+        ],
+        persistentState: 'required',
+        sessionTypes: ['persistent-license'],
+      }])
+    .then(function(access) {
+      return access.createMediaKeys();
+    })
+    .then(function(mediaKeys) {
+      return mediaKeys.createSession('persistent-license');
+    });
+  }
+
+  function handleMessageEvent(e) {
+    var session = e.target;
+    var te = new TextEncoder();
+    var license = te.encode(
+      '{"keys":[{"kty":"oct","k":"tQ0bJVWb6b0KPL6KtZIy_A","kid":"LwVHf8JLtPrv2GUXFW2v_A"}],"type":"persistent-license"}');
+
+    savedSessionId = session.sessionId;
+    session.update(license).then(success_, failure_);
+  }
+
+  function setMediaLicense() {
+    var te = new TextEncoder();
+    var initData = te.encode('{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}');
+
+    createPersistentSession().then(function(session) {
+      // generateRequest() will trigger a 'message' event, which we need to
+      // wait for in order to call update() which provides the license.
+      session.addEventListener('message', handleMessageEvent, false);
+      return session.generateRequest('keyids', initData);
+    })
+    // Success is reported from handleMessageEvent().
+    .catch(failure_);
+  }
+
+  function hasMediaLicense() {
+    createPersistentSession().then(function(session) {
+      return session.load(savedSessionId);
+    })
+    .then(function(result) {
+      // |result| is a boolean, indicating if the session was loaded or not.
+      domAutomationController.send(result);
+    })
+    .catch(failure_);
+  }
+</script>
+
+<body>
+  This page is used to test creation and deletion of Media Licenses.
+  The functions are called from BrowsingDataRemoverBrowserTest::HasDataForType
+  and BrowsingDataRemoverBrowserTest::SetDataForType.
+</body>
+
+</html>
\ No newline at end of file
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 9735dad..d0b64720 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -404,40 +404,14 @@
         ['win', 'passthrough', 'vulkan'], bug=2708) # ANGLE bug ID
     self.Fail('conformance/canvas/canvas-test.html',
         ['win', 'passthrough', 'vulkan'], bug=2929) # ANGLE bug ID
-    self.Fail('conformance/canvas/canvas-zero-size.html',
-        ['win', 'passthrough', 'vulkan'], bug=2918) # ANGLE bug ID
     self.Fail('conformance/context/' +
         'context-attribute-preserve-drawing-buffer.html',
         ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/extensions/webgl-debug-shaders.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/extensions/webgl-draw-buffers.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/bugs/sampler-array-using-loop-index.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/bugs/uniforms-should-not-lose-values.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/bugs/' +
-        'varying-arrays-should-not-be-reversed.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/functions/glsl-function*',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/matrices/glsl-mat3-construction.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/matrices/glsl-mat4-to-mat3.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/misc/shader-with-non-reserved-words.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/misc/shader-with-reserved-words.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
     self.Fail('conformance/glsl/misc/' +
         'shader-with-short-circuiting-operators.html',
         ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
-    self.Fail('conformance/glsl/misc/' +
-        'uninitialized-local-global-variables.html',
-        ['win', 'passthrough', 'vulkan'], bug=2909) # ANGLE bug ID
     self.Fail('conformance/misc/uninitialized-test.html',
-        ['win', 'passthrough', 'vulkan'], bug=2722) # ANGLE bug ID
+        ['win', 'passthrough', 'vulkan'], bug=2987) # ANGLE bug ID
     self.Fail('conformance/more/functions/copyTexSubImage2D.html',
         ['win', 'passthrough', 'vulkan'], bug=2911) # ANGLE bug ID
     self.Fail('conformance/offscreencanvas/context-lost-restored-worker.html',
@@ -458,23 +432,6 @@
         ['win', 'passthrough', 'vulkan'], bug=0) # ANGLE bug ID
     self.Fail('conformance/rendering/preservedrawingbuffer-leak.html',
         ['win', 'passthrough', 'vulkan'], bug=2919) # ANGLE bug ID
-    self.Fail('conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_byte.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/textures/canvas/' +
-        'tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/textures/canvas/' +
-        'tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/textures/image_bitmap_from_canvas/' +
-        'tex-2d-rgb-rgb-unsigned_byte.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/textures/image_bitmap_from_canvas/' +
-        'tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/textures/image_bitmap_from_canvas/' +
-        'tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
     self.Fail('conformance/textures/misc/copy-tex-image-and-sub-image-2d.html',
         ['win', 'passthrough', 'vulkan'], bug=2722) # ANGLE bug ID
     self.Fail('conformance/textures/misc/' +
@@ -497,15 +454,6 @@
         ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
     self.Fail('conformance/textures/misc/texture-mips.html',
         ['win', 'passthrough', 'vulkan'], bug=2722) # ANGLE bug ID
-    self.Fail('conformance/textures/webgl_canvas/' +
-        'tex-2d-rgb-rgb-unsigned_byte.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/textures/webgl_canvas/' +
-        'tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
-    self.Fail('conformance/textures/webgl_canvas/' +
-        'tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html',
-        ['win', 'passthrough', 'vulkan'], bug=2913) # ANGLE bug ID
     self.Fail('conformance/uniforms/out-of-bounds-uniform-array-access.html',
         ['win', 'passthrough', 'vulkan'], bug=2921) # ANGLE bug ID
     self.Fail('WebglExtension_ANGLE_instanced_arrays',
@@ -522,8 +470,6 @@
         ['win', 'passthrough', 'vulkan'], bug=2899) # ANGLE bug ID
     self.Fail('WebglExtension_EXT_sRGB',
         ['win', 'passthrough', 'vulkan'], bug=2900) # ANGLE bug ID
-    self.Fail('WebglExtension_EXT_texture_filter_anisotropic',
-        ['win', 'passthrough', 'vulkan'], bug=2901) # ANGLE bug ID
     self.Fail('WebglExtension_OES_element_index_uint',
         ['win', 'passthrough', 'vulkan'], bug=2902) # ANGLE bug ID
     self.Fail('WebglExtension_OES_standard_derivatives',
@@ -582,8 +528,7 @@
         ['win', 'passthrough', 'vulkan', 'amd'], bug=2922) # ANGLE bug ID
     self.Fail('conformance/rendering/clipping-wide-points.html',
         ['win', 'passthrough', 'vulkan', 'amd'], bug=2722) # ANGLE bug ID
-    self.Fail('conformance/textures/image_bitmap_from_canvas/' +
-        'tex-2d-rgba-rgba-unsigned_byte.html',
+    self.Fail('conformance/textures/image_bitmap_from_canvas/*',
         ['win', 'passthrough', 'vulkan', 'amd'], bug=2913) # ANGLE bug ID
     self.Fail('deqp/data/gles2/shaders/conversions.html',
         ['win', 'passthrough', 'vulkan', 'amd'], bug=2926) # ANGLE bug ID
diff --git a/content/test/mock_render_widget_host_delegate.cc b/content/test/mock_render_widget_host_delegate.cc
index bb23893..721d4cb 100644
--- a/content/test/mock_render_widget_host_delegate.cc
+++ b/content/test/mock_render_widget_host_delegate.cc
@@ -53,7 +53,7 @@
   return &text_input_manager_;
 }
 
-bool MockRenderWidgetHostDelegate::IsFullscreenForCurrentTab() const {
+bool MockRenderWidgetHostDelegate::IsFullscreenForCurrentTab() {
   return is_fullscreen_;
 }
 
diff --git a/content/test/mock_render_widget_host_delegate.h b/content/test/mock_render_widget_host_delegate.h
index 048ff0de..78bbdc5ed 100644
--- a/content/test/mock_render_widget_host_delegate.h
+++ b/content/test/mock_render_widget_host_delegate.h
@@ -47,7 +47,7 @@
       RenderWidgetHostImpl* widget_host) override;
   void SendScreenRects() override;
   TextInputManager* GetTextInputManager() override;
-  bool IsFullscreenForCurrentTab() const override;
+  bool IsFullscreenForCurrentTab() override;
 
  private:
   std::unique_ptr<NativeWebKeyboardEvent> last_event_;
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index f5809ec..f0bccb8 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -81,13 +81,12 @@
   EXPECT_FALSE(expect_set_history_offset_and_length_);
 }
 
-TestRenderFrameHost* TestWebContents::GetMainFrame() const {
+TestRenderFrameHost* TestWebContents::GetMainFrame() {
   return static_cast<TestRenderFrameHost*>(WebContentsImpl::GetMainFrame());
 }
 
-TestRenderViewHost* TestWebContents::GetRenderViewHost() const {
-    return static_cast<TestRenderViewHost*>(
-        WebContentsImpl::GetRenderViewHost());
+TestRenderViewHost* TestWebContents::GetRenderViewHost() {
+  return static_cast<TestRenderViewHost*>(WebContentsImpl::GetRenderViewHost());
 }
 
 TestRenderFrameHost* TestWebContents::GetPendingMainFrame() {
@@ -107,14 +106,14 @@
   return g_next_image_download_id;
 }
 
-const GURL& TestWebContents::GetLastCommittedURL() const {
+const GURL& TestWebContents::GetLastCommittedURL() {
   if (last_committed_url_.is_valid()) {
     return last_committed_url_;
   }
   return WebContentsImpl::GetLastCommittedURL();
 }
 
-const base::string16& TestWebContents::GetTitle() const {
+const base::string16& TestWebContents::GetTitle() {
   if (title_)
     return title_.value();
 
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h
index 880b4ae..7fc8011 100644
--- a/content/test/test_web_contents.h
+++ b/content/test/test_web_contents.h
@@ -53,16 +53,16 @@
   static TestWebContents* Create(const CreateParams& params);
 
   // WebContentsImpl overrides (returning the same values, but in Test* types)
-  TestRenderFrameHost* GetMainFrame() const override;
-  TestRenderViewHost* GetRenderViewHost() const override;
+  TestRenderFrameHost* GetMainFrame() override;
+  TestRenderViewHost* GetRenderViewHost() override;
   // Overrides to avoid establishing Mojo connection with renderer process.
   int DownloadImage(const GURL& url,
                     bool is_favicon,
                     uint32_t max_bitmap_size,
                     bool bypass_cache,
                     ImageDownloadCallback callback) override;
-  const GURL& GetLastCommittedURL() const override;
-  const base::string16& GetTitle() const override;
+  const GURL& GetLastCommittedURL() override;
+  const base::string16& GetTitle() override;
 
   // WebContentsTester implementation.
   void CommitPendingNavigation() override;
diff --git a/device/fido/authenticator_get_assertion_response.cc b/device/fido/authenticator_get_assertion_response.cc
index dc635d4..44d304b 100644
--- a/device/fido/authenticator_get_assertion_response.cc
+++ b/device/fido/authenticator_get_assertion_response.cc
@@ -36,9 +36,15 @@
   if (key_handle.empty())
     return base::nullopt;
 
-  auto flags = u2f_data.subspan<kFlagIndex, kFlagLength>();
+  auto flags = u2f_data.subspan<kFlagIndex, kFlagLength>()[0];
+  if (flags &
+      (static_cast<uint8_t>(AuthenticatorData::Flag::kExtensionDataIncluded) |
+       static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation))) {
+    // U2F responses cannot assert CTAP2 features.
+    return base::nullopt;
+  }
   auto counter = u2f_data.subspan<kCounterIndex, kCounterLength>();
-  AuthenticatorData authenticator_data(relying_party_id_hash, flags[0], counter,
+  AuthenticatorData authenticator_data(relying_party_id_hash, flags, counter,
                                        base::nullopt);
 
   auto signature =
diff --git a/device/fido/ctap_response_unittest.cc b/device/fido/ctap_response_unittest.cc
index 8feeffa..609965c 100644
--- a/device/fido/ctap_response_unittest.cc
+++ b/device/fido/ctap_response_unittest.cc
@@ -530,6 +530,21 @@
   EXPECT_FALSE(response);
 }
 
+TEST(CTAPResponseTest, TestParseU2fSignWithCTAP2Flags) {
+  std::vector<uint8_t> sign_response = GetTestSignResponse();
+  // Set two flags that should only be set in CTAP2 responses and expect parsing
+  // to fail.
+  sign_response[0] |=
+      static_cast<uint8_t>(AuthenticatorData::Flag::kExtensionDataIncluded);
+  sign_response[0] |=
+      static_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
+
+  auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
+      test_data::kApplicationParameter, sign_response,
+      GetTestCredentialRawIdBytes());
+  EXPECT_FALSE(response);
+}
+
 TEST(CTAPResponseTest, TestParseU2fSignWithNullCorruptedCounter) {
   // A sign response of less than 5 bytes.
   auto response = AuthenticatorGetAssertionResponse::CreateFromU2fSignResponse(
diff --git a/docs/cipd.md b/docs/cipd.md
index f9e438a..e689e9a5 100644
--- a/docs/cipd.md
+++ b/docs/cipd.md
@@ -121,11 +121,6 @@
 To actually create your package, you'll need:
 
  - the cipd.yaml file (described above)
- - a package version. For third-party packages, this should typically include
-   the version of the third-party package itself. If you want to support future
-   modifications within a given version of a third-party package (e.g., if you
-   want to make chromium-specific changes), it's best to include a suffix with
-   a numerical component.
  - [permission](#permissions-in-cipd).
 
 Once you have those, you can create your package like so:
@@ -134,9 +129,17 @@
 # Assuming that the third-party dependency in question is at version 1.2.3
 # and this is the first chromium revision of that version.
 $ cipd auth-login  # One-time auth.
-$ cipd create --pkg-def cipd.yaml -tag version:1.2.3-cr0
+$ cipd create --pkg-def cipd.yaml
+...
+[P114210 10:14:17.215 client.go:931 I] cipd: instance
+chromium/third_party/sample_cipd_dep:TX7HeY1_1JLwFVx-xiETOpT8YK4W5CbyO26SpmaMA0IC was
+successfully registered
 ```
 
+Take note of the instance ID printed in the log
+(`TX7HeY1_1JLwFVx-xiETOpT8YK4W5CbyO26SpmaMA0IC` in the example above).
+You'll be adding it to DEPS momentarily.
+
 ### 5. Add your CIPD package to DEPS
 
 You can add your package to DEPS by adding an entry of the following form to
@@ -154,7 +157,7 @@
     'packages': [
       {
         'package': 'chromium/third_party/sample_cipd_dep',
-        'version': 'version:1.2.3-cr0',
+        'version': 'TX7HeY1_1JLwFVx-xiETOpT8YK4W5CbyO26SpmaMA0IC',
       },
     ],
 
@@ -168,8 +171,8 @@
 ```
 
 This will result in CIPD package `chromium/third_party/sample_cipd_dep` at
-`version:1.2.3-cr0` being installed in `src/third_party/sample_cipd_dep`
-(relative to the gclient root directory).
+`TX7HeY1_1JLwFVx-xiETOpT8YK4W5CbyO26SpmaMA0IC` being installed in
+`src/third_party/sample_cipd_dep` (relative to the gclient root directory).
 
 ## Updating a CIPD dependency
 
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
index 6a88e39e..585657e 100644
--- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
+++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
@@ -83,7 +83,7 @@
 }
 
 HostID GenerateHostIDFromEmbedder(const extensions::Extension* extension,
-                                  const content::WebContents* web_contents) {
+                                  content::WebContents* web_contents) {
   if (extension)
     return HostID(HostID::EXTENSIONS, extension->id());
 
diff --git a/extensions/browser/guest_view/web_view/web_view_apitest.cc b/extensions/browser/guest_view/web_view/web_view_apitest.cc
index 867cbca..33c09e4 100644
--- a/extensions/browser/guest_view/web_view/web_view_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_apitest.cc
@@ -832,4 +832,8 @@
   RunTest("testFocus", "web_view/no_internal_calls_to_user_code", false);
 }
 
+IN_PROC_BROWSER_TEST_F(WebViewAPITest, TestClosedShadowRoot) {
+  RunTest("testClosedShadowRoot", "web_view/apitest");
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc
index 5be5c1b09..fde870c 100644
--- a/extensions/browser/process_manager.cc
+++ b/extensions/browser/process_manager.cc
@@ -450,7 +450,7 @@
 }
 
 const Extension* ProcessManager::GetExtensionForWebContents(
-    const content::WebContents* web_contents) {
+    content::WebContents* web_contents) {
   if (!web_contents->GetSiteInstance())
     return nullptr;
   const Extension* extension =
diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h
index dfbd40b58..73cf098 100644
--- a/extensions/browser/process_manager.h
+++ b/extensions/browser/process_manager.h
@@ -118,7 +118,7 @@
   // Returns the extension associated with the main frame of the given
   // |web_contents|, or null if there isn't one.
   const Extension* GetExtensionForWebContents(
-      const content::WebContents* web_contents);
+      content::WebContents* web_contents);
 
   // Getter and setter for the lazy background page's keepalive count. This is
   // the count of how many outstanding "things" are keeping the page alive.
diff --git a/extensions/browser/script_executor.h b/extensions/browser/script_executor.h
index ce07949..0e5b2407 100644
--- a/extensions/browser/script_executor.h
+++ b/extensions/browser/script_executor.h
@@ -36,7 +36,7 @@
 // Callback that ScriptExecutor uses to notify when content scripts and/or
 // tabs.executeScript calls run on a page.
 using ScriptsExecutedNotification = base::RepeatingCallback<
-    void(const content::WebContents*, const ExecutingScriptsMap&, const GURL&)>;
+    void(content::WebContents*, const ExecutingScriptsMap&, const GURL&)>;
 
 // Interface for executing extension content scripts (e.g. executeScript) as
 // described by the ExtensionMsg_ExecuteCode_Params IPC, and notifying the
diff --git a/extensions/common/manifest_handlers/content_capabilities_handler.cc b/extensions/common/manifest_handlers/content_capabilities_handler.cc
index 68aa79b..3ce1ed7 100644
--- a/extensions/common/manifest_handlers/content_capabilities_handler.cc
+++ b/extensions/common/manifest_handlers/content_capabilities_handler.cc
@@ -8,7 +8,6 @@
 
 #include "base/command_line.h"
 #include "base/lazy_instance.h"
-#include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -111,8 +110,7 @@
           keys::kContentCapabilities,
           permission_name));
     } else {
-      info->permissions.insert(
-          base::WrapUnique(permission_info->CreateAPIPermission()));
+      info->permissions.insert(permission_info->CreateAPIPermission());
     }
   }
 
diff --git a/extensions/common/permissions/api_permission.cc b/extensions/common/permissions/api_permission.cc
index 62e00ad4..47d791a 100644
--- a/extensions/common/permissions/api_permission.cc
+++ b/extensions/common/permissions/api_permission.cc
@@ -112,9 +112,10 @@
 
 APIPermissionInfo::~APIPermissionInfo() { }
 
-APIPermission* APIPermissionInfo::CreateAPIPermission() const {
-  return api_permission_constructor_ ?
-    api_permission_constructor_(this) : new SimpleAPIPermission(this);
+std::unique_ptr<APIPermission> APIPermissionInfo::CreateAPIPermission() const {
+  if (api_permission_constructor_)
+    return api_permission_constructor_(this);
+  return std::make_unique<SimpleAPIPermission>(this);
 }
 
 }  // namespace extensions
diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h
index d129d777..044e431 100644
--- a/extensions/common/permissions/api_permission.h
+++ b/extensions/common/permissions/api_permission.h
@@ -380,7 +380,8 @@
     kFlagSupportsContentCapabilities = 1 << 5,
   };
 
-  typedef APIPermission* (*APIPermissionConstructor)(const APIPermissionInfo*);
+  using APIPermissionConstructor =
+      std::unique_ptr<APIPermission> (*)(const APIPermissionInfo*);
 
   typedef std::set<APIPermission::ID> IDSet;
 
@@ -398,7 +399,7 @@
   ~APIPermissionInfo();
 
   // Creates a APIPermission instance.
-  APIPermission* CreateAPIPermission() const;
+  std::unique_ptr<APIPermission> CreateAPIPermission() const;
 
   int flags() const { return flags_; }
 
diff --git a/extensions/common/permissions/api_permission_set.cc b/extensions/common/permissions/api_permission_set.cc
index 3e5df07..80cbe37 100644
--- a/extensions/common/permissions/api_permission_set.cc
+++ b/extensions/common/permissions/api_permission_set.cc
@@ -5,7 +5,6 @@
 #include "extensions/common/permissions/api_permission_set.h"
 
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
@@ -133,7 +132,7 @@
   const APIPermissionInfo* permission_info =
       PermissionsInfo::GetInstance()->GetByID(id);
   DCHECK(permission_info);
-  insert(base::WrapUnique(permission_info->CreateAPIPermission()));
+  insert(permission_info->CreateAPIPermission());
 }
 
 void APIPermissionSet::insert(std::unique_ptr<APIPermission> permission) {
diff --git a/extensions/common/permissions/api_permission_set_unittest.cc b/extensions/common/permissions/api_permission_set_unittest.cc
index d081cfa..d3ea00b 100644
--- a/extensions/common/permissions/api_permission_set_unittest.cc
+++ b/extensions/common/permissions/api_permission_set_unittest.cc
@@ -40,8 +40,8 @@
 
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket);
-  std::unique_ptr<APIPermission> permission(
-      permission_info->CreateAPIPermission());
+  std::unique_ptr<APIPermission> permission =
+      permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -76,7 +76,7 @@
   apis2.insert(APIPermission::kPower);
   apis2.insert(APIPermission::kSerial);
 
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -90,7 +90,7 @@
   expected_apis.insert(APIPermission::kPower);
   expected_apis.insert(APIPermission::kSerial);
 
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -126,8 +126,8 @@
   // Intersection with an empty set.
   apis1.insert(APIPermission::kAudioCapture);
   apis1.insert(APIPermission::kDns);
-  std::unique_ptr<APIPermission> permission(
-      permission_info->CreateAPIPermission());
+  std::unique_ptr<APIPermission> permission =
+      permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -155,7 +155,7 @@
   apis2.insert(APIPermission::kHid);
   apis2.insert(APIPermission::kPower);
   apis2.insert(APIPermission::kSerial);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("udp-bind::8080");
@@ -166,7 +166,7 @@
   apis2.insert(std::move(permission));
 
   expected_apis.insert(APIPermission::kAudioCapture);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("udp-bind::8080");
@@ -199,8 +199,8 @@
   // Difference with an empty set.
   apis1.insert(APIPermission::kAudioCapture);
   apis1.insert(APIPermission::kDns);
-  std::unique_ptr<APIPermission> permission(
-      permission_info->CreateAPIPermission());
+  std::unique_ptr<APIPermission> permission =
+      permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -220,7 +220,7 @@
   apis2.insert(APIPermission::kHid);
   apis2.insert(APIPermission::kPower);
   apis2.insert(APIPermission::kSerial);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
@@ -230,7 +230,7 @@
   apis2.insert(std::move(permission));
 
   expected_apis.insert(APIPermission::kDns);
-  permission.reset(permission_info->CreateAPIPermission());
+  permission = permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("udp-bind::8080");
@@ -261,8 +261,8 @@
 
   apis.insert(APIPermission::kAudioCapture);
   apis.insert(APIPermission::kDns);
-  std::unique_ptr<APIPermission> permission(
-      permission_info->CreateAPIPermission());
+  std::unique_ptr<APIPermission> permission =
+      permission_info->CreateAPIPermission();
   {
     std::unique_ptr<base::ListValue> value(new base::ListValue());
     value->AppendString("tcp-connect:*.example.com:80");
diff --git a/extensions/common/permissions/base_set_operators.h b/extensions/common/permissions/base_set_operators.h
index 48cf9a2..7e878367 100644
--- a/extensions/common/permissions/base_set_operators.h
+++ b/extensions/common/permissions/base_set_operators.h
@@ -11,7 +11,7 @@
 #include <map>
 #include <memory>
 
-#include "base/memory/linked_ptr.h"
+#include "base/logging.h"
 
 namespace extensions {
 
@@ -31,8 +31,7 @@
   typedef typename BaseSetOperatorsTraits<T>::ElementType ElementType;
   typedef typename BaseSetOperatorsTraits<T>::ElementIDType ElementIDType;
 
-  // TODO(devlin): Un-link-ptr-ify this.
-  using Map = std::map<ElementIDType, linked_ptr<ElementType>>;
+  using Map = std::map<ElementIDType, std::unique_ptr<ElementType>>;
 
   class const_iterator :
     public std::iterator<std::input_iterator_tag, const ElementType*> {
@@ -249,7 +248,7 @@
 
   void insert(std::unique_ptr<ElementType> item) {
     ElementIDType id = item->id();
-    map_[id].reset(item.release());
+    map_[id] = std::move(item);
   }
 
   size_t size() const { return map_.size(); }
diff --git a/extensions/common/permissions/extensions_api_permissions.cc b/extensions/common/permissions/extensions_api_permissions.cc
index a89e9ed..2ce55ed2 100644
--- a/extensions/common/permissions/extensions_api_permissions.cc
+++ b/extensions/common/permissions/extensions_api_permissions.cc
@@ -6,6 +6,8 @@
 
 #include <stddef.h>
 
+#include <memory>
+
 #include "extensions/common/api/declarative_net_request/constants.h"
 #include "extensions/common/permissions/api_permission.h"
 #include "extensions/common/permissions/socket_permission.h"
@@ -17,8 +19,9 @@
 namespace {
 
 template <typename T>
-APIPermission* CreateAPIPermission(const APIPermissionInfo* permission) {
-  return new T(permission);
+std::unique_ptr<APIPermission> CreateAPIPermission(
+    const APIPermissionInfo* permission) {
+  return std::make_unique<T>(permission);
 }
 
 // WARNING: If you are modifying a permission message in this list, be sure to
diff --git a/extensions/renderer/resources/guest_view/guest_view_container.js b/extensions/renderer/resources/guest_view/guest_view_container.js
index 17a40b9..c5ac2d1 100644
--- a/extensions/renderer/resources/guest_view/guest_view_container.js
+++ b/extensions/renderer/resources/guest_view/guest_view_container.js
@@ -32,7 +32,7 @@
   this.setupAttributes();
 
   this.internalElement = this.createInternalElement$();
-  this.shadowRoot = $Element.attachShadow(this.element, {mode: 'open'});
+  this.shadowRoot = $Element.attachShadow(this.element, {mode: 'closed'});
   $Node.appendChild(this.shadowRoot, this.internalElement);
 
   GuestViewInternalNatives.RegisterView(this.viewInstanceId, this, viewType);
diff --git a/extensions/shell/test/shell_test.cc b/extensions/shell/test/shell_test.cc
index 251a882d..362eb7d 100644
--- a/extensions/shell/test/shell_test.cc
+++ b/extensions/shell/test/shell_test.cc
@@ -39,7 +39,7 @@
   extension_system_ = static_cast<ShellExtensionSystem*>(
       ExtensionSystem::Get(browser_context_));
   extension_system_->FinishInitialization();
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   base::RunLoop().RunUntilIdle();
 }
 
diff --git a/extensions/test/data/web_view/apitest/main.js b/extensions/test/data/web_view/apitest/main.js
index 582be355..d970de5 100644
--- a/extensions/test/data/web_view/apitest/main.js
+++ b/extensions/test/data/web_view/apitest/main.js
@@ -1831,6 +1831,29 @@
 
 function captureVisibleRegionDoCapture() {}
 
+// Ensure we use the closed encapsulation mode for the guest view shadow DOM
+// to prevent script from interfering with our internal elements and producing
+// unexpected behaviour.
+function testClosedShadowRoot() {
+  // Script could overwrite attachShadow to ignore the provided encapsulation
+  // mode. Ensure this does not happen when creating the guest view shadow
+  // DOM.
+  Element.prototype.realAttachShadow = Element.prototype.attachShadow;
+  Element.prototype.attachShadow = function() {
+    window.console.log('Tainted attachShadow was called.');
+    embedder.test.fail();
+    return this.realAttachShadow({mode: 'open'});
+  };
+
+  var webview = document.createElement('webview');
+  webview.src = 'data:text/html,webview test'
+  webview.addEventListener('loadstop', () => {
+    embedder.test.assertFalse(webview.shadowRoot);
+    embedder.test.succeed();
+  });
+  document.body.appendChild(webview);
+}
+
 // Tests end.
 
 embedder.test.testList = {
@@ -1905,7 +1928,8 @@
   'testWebRequestAPIWithHeaders': testWebRequestAPIWithHeaders,
   'testWebRequestAPIExistence': testWebRequestAPIExistence,
   'testWebRequestAPIGoogleProperty': testWebRequestAPIGoogleProperty,
-  'testCaptureVisibleRegion': testCaptureVisibleRegion
+  'testCaptureVisibleRegion': testCaptureVisibleRegion,
+  'testClosedShadowRoot': testClosedShadowRoot,
 };
 
 onload = function() {
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index e46ab62..8e69787 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -2304,6 +2304,13 @@
       mixins: "libfuzzer"
     }
     builders {
+      name: "Libfuzzer Upload Chrome OS ASan"
+      dimensions: "os:Ubuntu-14.04"
+      mixins: "fyi-ci"
+      # mixins: "fuzz-ci"
+      mixins: "libfuzzer"
+    }
+    builders {
       name: "Mac 10.6"
       dimensions: "os:Mac-10.12.2"
       mixins: "chromedriver-ci"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 8f8fd20..dd58e0b 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -1474,6 +1474,11 @@
     short_name: "med"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Chrome OS ASan"
+    category: "libfuzz"
+    short_name: "chromeos-asan"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan"
     category: "libfuzz"
     short_name: "linux"
@@ -2570,6 +2575,10 @@
     category: "chromium_afl"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Chrome OS ASan"
+    category: "chromium_libfuzzer"
+  }
+  builders {
     name: "buildbot/chromium.fyi/Libfuzzer Upload Linux ASan"
     name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Linux ASan"
     category: "chromium_libfuzzer"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index b6b50d8..08ebe8e 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -154,6 +154,7 @@
   triggers: "Jumbo Mac"
   triggers: "Jumbo Win x64"
   triggers: "Leak Detection Linux"
+  triggers: "Libfuzzer Upload Chrome OS ASan"
   triggers: "Libfuzzer Upload Linux ASan Debug"
   triggers: "Libfuzzer Upload Linux ASan"
   triggers: "Libfuzzer Upload Linux MSan"
@@ -3182,6 +3183,19 @@
 }
 
 job {
+  id: "Libfuzzer Upload Chrome OS ASan"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Libfuzzer Upload Chrome OS ASan"
+  }
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
+}
+
+job {
   id: "Libfuzzer Upload Linux ASan"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
index 245a3ee5..90d020b5 100644
--- a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
+++ b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
@@ -22,17 +22,32 @@
     {
       "include": "webrtc_tests.json",
       "device type": "iPhone 6s Plus",
-      "os": "11.4"
+      "os": "11.4",
+      "dimensions": [
+        { "os": "Mac-10.13.4", "pool": "Chrome" },
+        { "os": "Mac-10.13.5", "pool": "Chrome" },
+        { "os": "Mac-10.13.6", "pool": "Chrome" }
+      ]
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPhone 6s",
-      "os": "11.4"
+      "os": "11.4",
+      "dimensions": [
+        { "os": "Mac-10.13.4", "pool": "Chrome" },
+        { "os": "Mac-10.13.5", "pool": "Chrome" },
+        { "os": "Mac-10.13.6", "pool": "Chrome" }
+      ]
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPad Air 2",
-      "os": "11.4"
+      "os": "11.4",
+      "dimensions": [
+        { "os": "Mac-10.13.4", "pool": "Chrome" },
+        { "os": "Mac-10.13.5", "pool": "Chrome" },
+        { "os": "Mac-10.13.6", "pool": "Chrome" }
+      ]
     }
   ]
 }
diff --git a/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm b/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm
index f655ae8e..e474ea6 100644
--- a/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm
+++ b/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm
@@ -28,8 +28,6 @@
 #error "This file requires ARC support."
 #endif
 
-DEFINE_WEB_STATE_USER_DATA_KEY(AppLauncherTabHelper);
-
 namespace {
 
 bool IsValidAppUrl(const GURL& app_url) {
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.h b/ios/chrome/browser/autofill/form_input_accessory_view_controller.h
index 93bb005..8d82d8d 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.h
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.h
@@ -18,7 +18,8 @@
 
 // Creates and manages a custom input accessory view while the user is
 // interacting with a form. Also handles hiding and showing the default
-// accessory view elements.
+// accessory view elements. Defaults in paused state and needs to be started by
+// calling |continueCustomKeyboardView|.
 @interface FormInputAccessoryViewController
     : NSObject<FormInputAccessoryConsumer>
 
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
index 2621200..613ae0f 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_controller.mm
@@ -78,6 +78,7 @@
         manualFillAccessoryViewControllerDelegate {
   self = [super init];
   if (self) {
+    _paused = YES;
     _manualFillAccessoryViewControllerDelegate =
         manualFillAccessoryViewControllerDelegate;
     if (autofill::features::IsPasswordManualFallbackEnabled()) {
@@ -209,10 +210,9 @@
 
 - (void)restoreOriginalKeyboardView {
   [self.manualFillAccessoryViewController reset];
-  [self restoreOriginalInputAccessoryView];
+  [self removeCustomInputAccessoryView];
   [self.keyboardReplacementView removeFromSuperview];
   self.keyboardReplacementView = nil;
-  self.paused = NO;
 }
 
 - (void)pauseCustomKeyboardView {
@@ -278,11 +278,6 @@
   [self.grayBackgroundView removeFromSuperview];
 }
 
-// Removes the custom input accessory views and clears the references.
-- (void)restoreOriginalInputAccessoryView {
-  [self removeCustomInputAccessoryView];
-}
-
 // This searches in a keyboard view hierarchy for the best candidate to
 // constrain a view to the keyboard.
 - (UIView*)recursiveGetKeyboardConstraintView:(UIView*)view {
diff --git a/ios/chrome/browser/find_in_page/find_tab_helper.mm b/ios/chrome/browser/find_in_page/find_tab_helper.mm
index 0732b065..c34c39d 100644
--- a/ios/chrome/browser/find_in_page/find_tab_helper.mm
+++ b/ios/chrome/browser/find_in_page/find_tab_helper.mm
@@ -18,8 +18,6 @@
 const char kFindNextActionName[] = "FindNext";
 const char kFindPreviousActionName[] = "FindPrevious";
 
-DEFINE_WEB_STATE_USER_DATA_KEY(FindTabHelper);
-
 FindTabHelper::FindTabHelper(web::WebState* web_state) {
   web_state->AddObserver(this);
   controller_ = [[FindInPageController alloc] initWithWebState:web_state];
diff --git a/ios/chrome/browser/infobars/infobar_manager_impl.mm b/ios/chrome/browser/infobars/infobar_manager_impl.mm
index c67ab9ea..d1755753 100644
--- a/ios/chrome/browser/infobars/infobar_manager_impl.mm
+++ b/ios/chrome/browser/infobars/infobar_manager_impl.mm
@@ -21,8 +21,6 @@
 #error "This file requires ARC support."
 #endif
 
-DEFINE_WEB_STATE_USER_DATA_KEY(InfoBarManagerImpl);
-
 namespace {
 
 infobars::InfoBarDelegate::NavigationDetails
diff --git a/ios/chrome/browser/search_engines/search_engine_tab_helper.mm b/ios/chrome/browser/search_engines/search_engine_tab_helper.mm
index 36357ca9..c506036 100644
--- a/ios/chrome/browser/search_engines/search_engine_tab_helper.mm
+++ b/ios/chrome/browser/search_engines/search_engine_tab_helper.mm
@@ -22,8 +22,6 @@
 #error "This file requires ARC support."
 #endif
 
-DEFINE_WEB_STATE_USER_DATA_KEY(SearchEngineTabHelper);
-
 namespace {
 
 const char kCommandPrefix[] = "searchEngine";
@@ -105,8 +103,9 @@
       ios::ChromeBrowserState::FromBrowserState(web_state_->GetBrowserState());
   TemplateURLService* url_service =
       ios::TemplateURLServiceFactory::GetForBrowserState(browser_state);
-  if (url_service && url_service->loaded())
-    url_service->UpdateProviderFavicons(driver->GetActiveURL(), icon_url);
+  const GURL potential_search_url = driver->GetActiveURL();
+  if (url_service && url_service->loaded() && potential_search_url.is_valid())
+    url_service->UpdateProviderFavicons(potential_search_url, icon_url);
 }
 
 // When the page is loaded, checks if |searchable_url_| has a value generated
diff --git a/ios/chrome/browser/sessions/ios_chrome_session_tab_helper.mm b/ios/chrome/browser/sessions/ios_chrome_session_tab_helper.mm
index 68b8a256..46345af 100644
--- a/ios/chrome/browser/sessions/ios_chrome_session_tab_helper.mm
+++ b/ios/chrome/browser/sessions/ios_chrome_session_tab_helper.mm
@@ -8,8 +8,6 @@
 #error "This file requires ARC support."
 #endif
 
-DEFINE_WEB_STATE_USER_DATA_KEY(IOSChromeSessionTabHelper);
-
 IOSChromeSessionTabHelper::IOSChromeSessionTabHelper(web::WebState* web_state)
     : session_id_(SessionID::NewUnique()),
       window_id_(SessionID::InvalidValue()) {}
diff --git a/ios/chrome/browser/tabs/legacy_tab_helper.mm b/ios/chrome/browser/tabs/legacy_tab_helper.mm
index bd1eef8..a204394 100644
--- a/ios/chrome/browser/tabs/legacy_tab_helper.mm
+++ b/ios/chrome/browser/tabs/legacy_tab_helper.mm
@@ -10,8 +10,6 @@
 #error "This file requires ARC support."
 #endif
 
-DEFINE_WEB_STATE_USER_DATA_KEY(LegacyTabHelper);
-
 // static
 void LegacyTabHelper::CreateForWebState(web::WebState* web_state) {
   CreateForWebStateInternal(web_state, nil);
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index a6280af61..2db3d5f 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -346,7 +346,6 @@
     "//ios/chrome/browser/ui/payments",
     "//ios/chrome/browser/ui/popup_menu",
     "//ios/chrome/browser/ui/presenters",
-    "//ios/chrome/browser/ui/print",
     "//ios/chrome/browser/ui/qr_scanner:coordinator",
     "//ios/chrome/browser/ui/reading_list",
     "//ios/chrome/browser/ui/sad_tab",
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index 3436677b..af226a2 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -63,7 +63,8 @@
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/images",
     "//ios/public/provider/chrome/browser/signin",
-    "//services/identity/public/cpp:cpp",
+    "//services/identity/public/cpp",
+    "//services/identity/public/objc",
     "//ui/base",
     "//ui/gfx",
     "//url",
@@ -200,6 +201,6 @@
     "//ios/public/provider/chrome/browser/signin",
     "//ios/public/provider/chrome/browser/signin:test_support",
     "//ios/third_party/earl_grey:earl_grey+link",
-    "//services/identity/public/cpp:cpp",
+    "//services/identity/public/cpp",
   ]
 }
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
index c25570a..f6b7eeb 100644
--- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.mm
@@ -5,15 +5,11 @@
 #import "ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller.h"
 
 #import "base/mac/foundation_util.h"
-#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/ios/browser/oauth2_token_service_observer_bridge.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/signin/account_tracker_service_factory.h"
 #include "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/signin/chrome_identity_service_observer_bridge.h"
-#include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/chrome/browser/ui/authentication/resized_avatar_cache.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_account_item.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
@@ -31,6 +27,8 @@
 #import "ios/third_party/material_components_ios/src/components/Dialogs/src/MaterialDialogs.h"
 #import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#import "services/identity/public/objc/identity_manager_observer_bridge.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -39,7 +37,7 @@
 
 namespace {
 
-const int kMaxShownAccounts = 3;
+const size_t kMaxShownAccounts = 3;
 const CGFloat kAccountsExtraBottomInset = 16;
 const CGFloat kVerticalPadding = 24;
 const CGFloat kButtonVerticalPadding = 16;
@@ -110,16 +108,14 @@
 - (void)loadModel {
   [super loadModel];
   CollectionViewModel* model = self.collectionViewModel;
+  [model addSectionWithIdentifier:SectionIdentifierAccounts];
+
   NSMutableDictionary<NSString*, CollectionViewItem*>* mutableIdentityMap =
       [[NSMutableDictionary alloc] init];
 
-  [model addSectionWithIdentifier:SectionIdentifierAccounts];
-  ProfileOAuth2TokenService* oauth2_service =
-      ProfileOAuth2TokenServiceFactory::GetForBrowserState(_browserState);
-  AccountTrackerService* accountTracker =
-      ios::AccountTrackerServiceFactory::GetForBrowserState(_browserState);
-  for (const std::string& account_id : oauth2_service->GetAccounts()) {
-    AccountInfo account = accountTracker->GetAccountInfo(account_id);
+  identity::IdentityManager* identityManager =
+      IdentityManagerFactory::GetForBrowserState(_browserState);
+  for (const auto& account : identityManager->GetAccountsWithRefreshTokens()) {
     ChromeIdentity* identity = ios::GetChromeBrowserProvider()
                                    ->GetChromeIdentityService()
                                    ->GetIdentityWithGaiaID(account.gaia);
@@ -181,10 +177,11 @@
 
 @end
 
-@interface SignedInAccountsViewController ()<
-    OAuth2TokenServiceObserverBridgeDelegate> {
+@interface SignedInAccountsViewController () <
+    IdentityManagerObserverBridgeDelegate> {
   ios::ChromeBrowserState* _browserState;  // Weak.
-  std::unique_ptr<OAuth2TokenServiceObserverBridge> _tokenServiceObserver;
+  std::unique_ptr<identity::IdentityManagerObserverBridge>
+      _identityManagerObserver;
   MDCDialogTransitionController* _transitionController;
 
   UILabel* _titleLabel;
@@ -219,9 +216,9 @@
   if (self) {
     _browserState = browserState;
     _dispatcher = dispatcher;
-    _tokenServiceObserver.reset(new OAuth2TokenServiceObserverBridge(
-        ProfileOAuth2TokenServiceFactory::GetForBrowserState(_browserState),
-        self));
+    _identityManagerObserver =
+        std::make_unique<identity::IdentityManagerObserverBridge>(
+            IdentityManagerFactory::GetForBrowserState(_browserState), self);
     _transitionController = [[MDCDialogTransitionController alloc] init];
     self.modalPresentationStyle = UIModalPresentationCustom;
     self.transitioningDelegate = _transitionController;
@@ -246,13 +243,14 @@
 #pragma mark UIViewController
 
 - (CGSize)preferredContentSize {
-  CGFloat width = MIN(kDialogMaxWidth,
-                      self.presentingViewController.view.bounds.size.width -
-                          2 * kMDCMinHorizontalPadding);
-  OAuth2TokenService* token_service =
-      ProfileOAuth2TokenServiceFactory::GetForBrowserState(_browserState);
+  CGFloat width = std::min(
+      kDialogMaxWidth, self.presentingViewController.view.bounds.size.width -
+                           2 * kMDCMinHorizontalPadding);
+  identity::IdentityManager* identityManager =
+      IdentityManagerFactory::GetForBrowserState(_browserState);
   int shownAccounts =
-      MIN(kMaxShownAccounts, token_service->GetAccounts().size());
+      std::min(kMaxShownAccounts,
+               identityManager->GetAccountsWithRefreshTokens().size());
   CGSize maxSize = CGSizeMake(width - 2 * kHorizontalPadding, CGFLOAT_MAX);
   CGSize buttonSize = [_primaryButton sizeThatFits:maxSize];
   CGSize infoSize = [_infoLabel sizeThatFits:maxSize];
@@ -388,12 +386,12 @@
   }];
 }
 
-#pragma mark OAuth2TokenServiceObserverBridgeDelegate
+#pragma mark IdentityManagerObserverBridgeDelegate
 
-- (void)onEndBatchChanges {
-  ProfileOAuth2TokenService* tokenService =
-      ProfileOAuth2TokenServiceFactory::GetForBrowserState(_browserState);
-  if (tokenService->GetAccounts().empty()) {
+- (void)onEndBatchOfRefreshTokenStateChanges {
+  identity::IdentityManager* identityManager =
+      IdentityManagerFactory::GetForBrowserState(_browserState);
+  if (identityManager->GetAccountsWithRefreshTokens().empty()) {
     [self dismissWithCompletion:nil];
     return;
   }
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
index cbeaa47..9cb9c6f 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
@@ -51,6 +51,9 @@
 // The object that manages the currently-shown custom accessory view.
 @property(nonatomic, weak) id<FormInputSuggestionsProvider> currentProvider;
 
+// YES if the first responder is a text input other than the web view.
+@property(nonatomic, assign) BOOL editingUIKitTextInput;
+
 // The form input handler. This is in charge of form navigation.
 @property(nonatomic, strong)
     FormInputAccessoryViewHandler* formInputAccessoryHandler;
@@ -82,6 +85,10 @@
 // Whether suggestions are disabled.
 @property(nonatomic, assign) BOOL suggestionsDisabled;
 
+// YES if the latest form activity was made in a form that supports the
+// accessory.
+@property(nonatomic, assign) BOOL validActivityForAccessoryView;
+
 // The WebState this instance is observing. Can be null.
 @property(nonatomic, assign) web::WebState* webState;
 
@@ -227,10 +234,7 @@
 
 - (void)keyboardWillShowWithHardwareKeyboardAttached:(BOOL)isHardwareKeyboard {
   self.hardwareKeyboard = isHardwareKeyboard;
-  if (self.lastSuggestions) {
-    [self updateWithProvider:self.lastProvider
-                 suggestions:self.lastSuggestions];
-  }
+  [self updateWithProvider:self.lastProvider suggestions:self.lastSuggestions];
 }
 
 - (void)keyboardDidStayOnScreen {
@@ -249,30 +253,54 @@
     didRegisterFormActivity:(const autofill::FormActivityParams&)params
                     inFrame:(web::WebFrame*)frame {
   DCHECK_EQ(_webState, webState);
+  self.validActivityForAccessoryView = NO;
+
+  // Return early if |params| is not complete.
+  if (params.input_missing) {
+    return;
+  }
+
+  // Return early if the URL can't be verified.
   web::URLVerificationTrustLevel trustLevel;
   const GURL pageURL(webState->GetCurrentURL(&trustLevel));
-  if (params.input_missing ||
-      trustLevel != web::URLVerificationTrustLevel::kAbsolute ||
-      !web::UrlHasWebScheme(pageURL) || !webState->ContentIsHTML()) {
+  if (trustLevel != web::URLVerificationTrustLevel::kAbsolute) {
+    return;
+  }
+
+  // Return early, pause and reset if the url is not HTML.
+  if (!web::UrlHasWebScheme(pageURL) || !webState->ContentIsHTML()) {
+    [self pauseCustomKeyboardView];
     [self reset];
     return;
   }
 
+  // Return early and reset if messaging is enabled but frame is missing or
+  // can't call JS.
   if (autofill::switches::IsAutofillIFrameMessagingEnabled() &&
       (!frame || !frame->CanCallJavaScriptFunction())) {
     [self reset];
     return;
   }
 
+  self.validActivityForAccessoryView = YES;
+  [self continueCustomKeyboardView];
+
+  NSString* frameID;
+  if (frame) {
+    frameID = base::SysUTF8ToNSString(frame->GetFrameId());
+  } else {
+    frameID = base::SysUTF8ToNSString(params.frame_id);
+  }
+
+  [self.formInputAccessoryHandler setLastFocusFormActivityWebFrameID:frameID];
+  [self synchronizeNavigationControls];
+
+  // Don't look for suggestions in the next events.
   if (params.type == "blur" || params.type == "change" ||
       params.type == "form_changed") {
     return;
   }
 
-  [self.formInputAccessoryHandler
-      setLastFocusFormActivityWebFrameID:base::SysUTF8ToNSString(
-                                             params.frame_id)];
-  [self synchronizeNavigationControls];
   [self retrieveSuggestionsForForm:params webState:webState];
 }
 
@@ -296,7 +324,7 @@
 
 - (void)webStateWasShown:(web::WebState*)webState {
   DCHECK_EQ(_webState, webState);
-  [self.consumer continueCustomKeyboardView];
+  [self continueCustomKeyboardView];
 }
 
 - (void)webStateWasHidden:(web::WebState*)webState {
@@ -311,7 +339,7 @@
   if (IsIPadIdiom()) {
     [self reset];
   } else {
-    [self.consumer pauseCustomKeyboardView];
+    [self pauseCustomKeyboardView];
   }
 }
 
@@ -363,6 +391,32 @@
 
 #pragma mark - Private
 
+// Tells the consumer to pause the custom keyboard view.
+- (void)pauseCustomKeyboardView {
+  [self.consumer pauseCustomKeyboardView];
+}
+
+// Tells the consumer to continue the custom keyboard view if the last activity
+// is valid, the web state is visible, and there is no other text input.
+- (void)continueCustomKeyboardView {
+  // Return early if the form is not a supported one.
+  if (!self.validActivityForAccessoryView) {
+    return;
+  }
+
+  // Return early if the current webstate is not visible.
+  if (!self.webState || !self.webState->IsVisible()) {
+    return;
+  }
+
+  // Return early if the current text input is not the web view.
+  if (self.editingUIKitTextInput) {
+    return;
+  }
+
+  [self.consumer continueCustomKeyboardView];
+}
+
 // Update the status of the consumer form navigation buttons to match the
 // handler state.
 - (void)synchronizeNavigationControls {
@@ -407,6 +461,9 @@
 // Resets the current provider, the consumer view and the navigation handler. As
 // well as reenables suggestions.
 - (void)reset {
+  self.lastSuggestions = nil;
+  self.lastProvider = nil;
+
   [self.consumer restoreOriginalKeyboardView];
   [self.formInputAccessoryHandler reset];
 
@@ -511,13 +568,15 @@
 // begins editing, pause the consumer so it doesn't present the custom view over
 // the keyboard.
 - (void)handleTextInputDidBeginEditing:(NSNotification*)notification {
-  [self.consumer pauseCustomKeyboardView];
+  self.editingUIKitTextInput = YES;
+  [self pauseCustomKeyboardView];
 }
 
 // When any text field or text view (e.g. omnibox, settings, card unmask dialog)
 // ends editing, continue presenting.
 - (void)handleTextInputDidEndEditing:(NSNotification*)notification {
-  [self.consumer continueCustomKeyboardView];
+  self.editingUIKitTextInput = NO;
+  [self continueCustomKeyboardView];
 }
 
 #pragma mark - PasswordFetcherDelegate
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 6eb3837..e06f5a8c5 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -99,7 +99,6 @@
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_observer.h"
 #import "ios/chrome/browser/tabs/tab_private.h"
-#import "ios/chrome/browser/tabs/tab_title_util.h"
 #import "ios/chrome/browser/tabs/tab_util.h"
 #import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
 #import "ios/chrome/browser/translate/language_selection_handler.h"
@@ -161,7 +160,6 @@
 #import "ios/chrome/browser/ui/payments/payment_request_manager.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_coordinator.h"
 #import "ios/chrome/browser/ui/presenters/vertical_animation_container.h"
-#import "ios/chrome/browser/ui/print/print_controller.h"
 #import "ios/chrome/browser/ui/reading_list/offline_page_native_content.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h"
 #include "ios/chrome/browser/ui/sad_tab/features.h"
@@ -249,7 +247,6 @@
 #import "net/base/mac/url_conversions.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/ssl/ssl_info.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/google_toolbox_for_mac/src/iPhone/GTMUIImage+Resize.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -477,9 +474,6 @@
   // Used to display the Find In Page UI. Nil if not visible.
   FindBarControllerIOS* _findBarController;
 
-  // Used to display the Print UI. Nil if not visible.
-  PrintController* _printController;
-
   // Adapter to let BVC be the delegate for WebState.
   std::unique_ptr<web::WebStateDelegateBridge> _webStateDelegate;
 
@@ -1431,8 +1425,6 @@
   }
 
   [_paymentRequestManager cancelRequest];
-  [_printController dismissAnimated:YES];
-  _printController = nil;
   [self.dispatcher dismissPopupMenuAnimated:NO];
   [_contextMenuCoordinator stop];
 
@@ -4373,17 +4365,6 @@
                                                    self.currentWebState]];
 }
 
-- (void)printTab {
-  if (!_printController) {
-    _printController = [[PrintController alloc]
-        initWithContextGetter:_browserState->GetRequestContext()];
-  }
-  Tab* currentTab = self.tabModel.currentTab;
-  [_printController printView:[currentTab viewForPrinting]
-                    withTitle:tab_util::GetTabTitle(currentTab.webState)
-               viewController:self];
-}
-
 - (void)addToReadingList:(ReadingListAddCommand*)command {
   [self addToReadingListURL:[command URL] title:[command title]];
 }
diff --git a/ios/chrome/browser/ui/commands/OWNERS b/ios/chrome/browser/ui/commands/OWNERS
new file mode 100644
index 0000000..446fcc2
--- /dev/null
+++ b/ios/chrome/browser/ui/commands/OWNERS
@@ -0,0 +1,7 @@
+edchin@chromium.org
+gambard@chromium.org
+marq@chromium.org
+rohitrao@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index 13045e7..1315a9c 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -48,9 +48,6 @@
 // Bookmarks the current page.
 - (void)bookmarkPage;
 
-// Prints the currently active tab.
-- (void)printTab;
-
 // Adds a page to the reading list using data in |command|.
 - (void)addToReadingList:(ReadingListAddCommand*)command;
 
diff --git a/ios/chrome/browser/ui/commands/browser_coordinator_commands.h b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
index d2e2391..946d3b8f 100644
--- a/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
@@ -12,6 +12,9 @@
 // and representative of the contents.
 @protocol BrowserCoordinatorCommands
 
+// Prints the currently active tab.
+- (void)printTab;
+
 // Shows the Reading List UI.
 - (void)showReadingList;
 
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index b5669b0..2ce78c6 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -37,6 +37,7 @@
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/download",
     "//ios/chrome/browser/ui/page_info:coordinator",
+    "//ios/chrome/browser/ui/print",
     "//ios/chrome/browser/ui/qr_scanner:coordinator",
     "//ios/chrome/browser/ui/reading_list",
     "//ios/chrome/browser/ui/recent_tabs",
@@ -45,6 +46,7 @@
     "//ios/chrome/browser/web:tab_helper_delegates",
     "//ios/chrome/browser/web_state_list",
     "//ios/public/provider/chrome/browser",
+    "//net",
   ]
   public_deps = [
     "//ios/chrome/browser/ui:ui_internal",
diff --git a/ios/chrome/browser/ui/main/browser_coordinator.mm b/ios/chrome/browser/ui/main/browser_coordinator.mm
index c26c269..b240c5ab 100644
--- a/ios/chrome/browser/ui/main/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/main/browser_coordinator.mm
@@ -9,9 +9,12 @@
 #include "base/scoped_observer.h"
 #import "ios/chrome/browser/app_launcher/app_launcher_abuse_detector.h"
 #import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/download/pass_kit_tab_helper.h"
 #import "ios/chrome/browser/store_kit/store_kit_coordinator.h"
 #import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
+#import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/tabs/tab_title_util.h"
 #import "ios/chrome/browser/ui/alert_coordinator/repost_form_coordinator.h"
 #import "ios/chrome/browser/ui/app_launcher/app_launcher_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/form_input_accessory_coordinator.h"
@@ -22,6 +25,7 @@
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/download/pass_kit_coordinator.h"
 #import "ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.h"
+#import "ios/chrome/browser/ui/print/print_controller.h"
 #import "ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.h"
 #import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
@@ -30,6 +34,7 @@
 #import "ios/chrome/browser/web/repost_form_tab_helper_delegate.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
+#include "net/url_request/url_request_context_getter.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -60,6 +65,10 @@
 // Coordinator for the PassKit UI presentation.
 @property(nonatomic, strong) PassKitCoordinator* passKitCoordinator;
 
+// Used to display the Print UI. Nil if not visible.
+// TODO(crbug.com/910017): Convert to coordinator.
+@property(nonatomic, strong) PrintController* printController;
+
 // Coordinator for the QR scanner.
 @property(nonatomic, strong) QRScannerLegacyCoordinator* qrScannerCoordinator;
 
@@ -118,6 +127,10 @@
 - (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion
                            dismissOmnibox:(BOOL)dismissOmnibox {
   [self.passKitCoordinator stop];
+
+  [self.printController dismissAnimated:YES];
+  self.printController = nil;
+
   [self.viewController clearPresentedStateWithCompletion:completion
                                           dismissOmnibox:dismissOmnibox];
 }
@@ -153,6 +166,7 @@
 
   self.appLauncherCoordinator = [[AppLauncherCoordinator alloc]
       initWithBaseViewController:self.viewController];
+
   self.formInputAccessoryCoordinator = [[FormInputAccessoryCoordinator alloc]
       initWithBaseViewController:self.viewController
                     browserState:self.browserState
@@ -171,6 +185,8 @@
   self.passKitCoordinator = [[PassKitCoordinator alloc]
       initWithBaseViewController:self.viewController];
 
+  /* PrintController is created and started by a BrowserCommand */
+
   self.qrScannerCoordinator = [[QRScannerLegacyCoordinator alloc]
       initWithBaseViewController:self.viewController];
   self.qrScannerCoordinator.dispatcher = self.dispatcher;
@@ -204,6 +220,8 @@
   [self.passKitCoordinator stop];
   self.passKitCoordinator = nil;
 
+  self.printController = nil;
+
   [self.qrScannerCoordinator stop];
   self.qrScannerCoordinator = nil;
 
@@ -223,6 +241,19 @@
   self.storeKitCoordinator = nil;
 }
 
+#pragma mark - BrowserCoordinatorCommands
+
+- (void)printTab {
+  if (!self.printController) {
+    self.printController = [[PrintController alloc]
+        initWithContextGetter:self.browserState->GetRequestContext()];
+  }
+  Tab* currentTab = self.tabModel.currentTab;
+  [self.printController printView:[currentTab viewForPrinting]
+                        withTitle:tab_util::GetTabTitle(currentTab.webState)
+                   viewController:self.viewController];
+}
+
 - (void)showReadingList {
   self.readingListCoordinator = [[ReadingListCoordinator alloc]
       initWithBaseViewController:self.viewController
diff --git a/ios/chrome/browser/ui/reading_list/BUILD.gn b/ios/chrome/browser/ui/reading_list/BUILD.gn
index 1c2ccc7a..3cd95a0 100644
--- a/ios/chrome/browser/ui/reading_list/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/BUILD.gn
@@ -20,8 +20,6 @@
     "reading_list_menu_notification_delegate.h",
     "reading_list_menu_notifier.h",
     "reading_list_menu_notifier.mm",
-    "reading_list_side_swipe_provider.h",
-    "reading_list_side_swipe_provider.mm",
     "reading_list_table_view_item.h",
     "reading_list_table_view_item.mm",
     "reading_list_utils.h",
@@ -97,7 +95,6 @@
   deps = [
     "resources:reading_list_empty_state",
     "resources:reading_list_empty_state_new",
-    "resources:reading_list_side_swipe",
     "resources:reading_list_tools_icon",
     "//base",
     "//base:i18n",
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.h b/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.h
deleted file mode 100644
index 242a917..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_SIDE_SWIPE_PROVIDER_H_
-#define IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_SIDE_SWIPE_PROVIDER_H_
-
-#import "ios/chrome/browser/ui/side_swipe/side_swipe_controller.h"
-
-class ReadingListModel;
-
-@interface ReadingListSideSwipeProvider : NSObject<SideSwipeContentProvider>
-- (instancetype)initWithReadingList:(ReadingListModel*)readingListModel;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_READING_LIST_READING_LIST_SIDE_SWIPE_PROVIDER_H_
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm b/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm
deleted file mode 100644
index fd9df181..0000000
--- a/ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.mm
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.h"
-
-#include "base/logging.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "components/reading_list/core/reading_list_entry.h"
-#include "components/reading_list/core/reading_list_model.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/metrics/new_tab_page_uma.h"
-#include "ios/web/public/web_state/web_state.h"
-#include "net/base/network_change_notifier.h"
-#include "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-class ReadingListObserverBridge;
-
-@interface ReadingListSideSwipeProvider () {
-  // Keep a reference to detach before deallocing.
-  ReadingListModel* _readingListModel;  // weak
-}
-
-@end
-
-@implementation ReadingListSideSwipeProvider
-- (instancetype)initWithReadingList:(ReadingListModel*)readingListModel {
-  if (self = [super init]) {
-    _readingListModel = readingListModel;
-  }
-  return self;
-}
-
-- (BOOL)canGoBack {
-  return NO;
-}
-
-- (void)goBack:(web::WebState*)webState {
-  NOTREACHED();
-}
-
-- (BOOL)canGoForward {
-  return _readingListModel->unread_size() > 0;
-}
-
-- (UIImage*)paneIcon {
-  return [UIImage imageNamed:@"reading_list_side_swipe"];
-}
-
-- (BOOL)rotateForwardIcon {
-  return NO;
-}
-
-- (void)goForward:(web::WebState*)webState {
-  if (!webState || _readingListModel->unread_size() == 0) {
-    return;
-  }
-  const ReadingListEntry* firstEntry = _readingListModel->GetFirstUnreadEntry(
-      net::NetworkChangeNotifier::IsOffline());
-  DCHECK(firstEntry);
-  base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
-  new_tab_page_uma::RecordAction(
-      ios::ChromeBrowserState::FromBrowserState(webState->GetBrowserState()),
-      new_tab_page_uma::ACTION_OPENED_READING_LIST_ENTRY);
-
-  web::NavigationManager::WebLoadParams params(firstEntry->URL());
-  params.transition_type = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK;
-  webState->GetNavigationManager()->LoadURLWithParams(params);
-}
-
-@end
diff --git a/ios/chrome/browser/ui/reading_list/resources/BUILD.gn b/ios/chrome/browser/ui/reading_list/resources/BUILD.gn
index ade93f7..d5a3baa 100644
--- a/ios/chrome/browser/ui/reading_list/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/reading_list/resources/BUILD.gn
@@ -31,15 +31,6 @@
   ]
 }
 
-imageset("reading_list_side_swipe") {
-  sources = [
-    "reading_list_side_swipe.imageset/Contents.json",
-    "reading_list_side_swipe.imageset/reading_list_side_swipe.png",
-    "reading_list_side_swipe.imageset/reading_list_side_swipe@2x.png",
-    "reading_list_side_swipe.imageset/reading_list_side_swipe@3x.png",
-  ]
-}
-
 imageset("reading_list_tools_icon") {
   sources = [
     "reading_list_tools_icon.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/Contents.json b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/Contents.json
deleted file mode 100644
index e4d2212..0000000
--- a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "reading_list_side_swipe.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "reading_list_side_swipe@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "reading_list_side_swipe@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe.png b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe.png
deleted file mode 100644
index ef386f07..0000000
--- a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe@2x.png b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe@2x.png
deleted file mode 100644
index 11a90f5a..0000000
--- a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe@3x.png b/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe@3x.png
deleted file mode 100644
index e910790..0000000
--- a/ios/chrome/browser/ui/reading_list/resources/reading_list_side_swipe.imageset/reading_list_side_swipe@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/side_swipe/BUILD.gn b/ios/chrome/browser/ui/side_swipe/BUILD.gn
index 240926fa..333368ed 100644
--- a/ios/chrome/browser/ui/side_swipe/BUILD.gn
+++ b/ios/chrome/browser/ui/side_swipe/BUILD.gn
@@ -7,8 +7,6 @@
   sources = [
     "card_side_swipe_view.h",
     "card_side_swipe_view.mm",
-    "history_side_swipe_provider.h",
-    "history_side_swipe_provider.mm",
     "side_swipe_controller.h",
     "side_swipe_controller.mm",
     "side_swipe_navigation_view.h",
@@ -33,6 +31,7 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/ntp",
+    "//ios/chrome/browser/ui/ntp:util",
     "//ios/chrome/browser/ui/tab_grid/grid:grid_ui",
     "//ios/chrome/browser/ui/tabs/requirements",
     "//ios/chrome/browser/ui/toolbar/public",
@@ -54,6 +53,8 @@
   deps = [
     ":side_swipe",
     "//base",
+    "//base/test:test_support",
+    "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/web/public/test",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.h b/ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.h
deleted file mode 100644
index 04677c1d..0000000
--- a/ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_SIDE_SWIPE_HISTORY_SIDE_SWIPE_PROVIDER_H_
-#define IOS_CHROME_BROWSER_UI_SIDE_SWIPE_HISTORY_SIDE_SWIPE_PROVIDER_H_
-
-#import "ios/chrome/browser/ui/side_swipe/side_swipe_controller.h"
-
-@class TabModel;
-
-@interface HistorySideSwipeProvider : NSObject<SideSwipeContentProvider>
-- (instancetype)initWithTabModel:(TabModel*)tabModel;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_SIDE_SWIPE_HISTORY_SIDE_SWIPE_PROVIDER_H_
diff --git a/ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.mm b/ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.mm
deleted file mode 100644
index a86e19f..0000000
--- a/ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.mm
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.h"
-
-#include "ios/chrome/browser/tabs/tab.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface HistorySideSwipeProvider () {
-  // Keep a reference to detach before deallocing.
-  __weak TabModel* _tabModel;  // weak
-}
-
-@end
-
-@implementation HistorySideSwipeProvider
-- (instancetype)initWithTabModel:(TabModel*)tabModel {
-  self = [super init];
-  if (self) {
-    _tabModel = tabModel;
-  }
-  return self;
-}
-
-- (BOOL)canGoBack {
-  return [[_tabModel currentTab] canGoBack];
-}
-
-- (BOOL)canGoForward {
-  return [[_tabModel currentTab] canGoForward];
-}
-
-- (void)goForward:(web::WebState*)webState {
-  [[_tabModel currentTab] goForward];
-}
-
-- (void)goBack:(web::WebState*)webState {
-  [[_tabModel currentTab] goBack];
-}
-
-- (UIImage*)paneIcon {
-  return [UIImage imageNamed:@"side_swipe_navigation_back"];
-}
-
-- (BOOL)rotateForwardIcon {
-  return YES;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h
index 7cc5152..0fdcd2fe 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.h
@@ -22,24 +22,6 @@
 // Notification sent when the user finishes a side swipe (on tablet).
 extern NSString* const kSideSwipeDidStopNotification;
 
-// A protocol for the Side Swipe controller sources.
-@protocol SideSwipeContentProvider
-// Returns whether this source can provide content for a back/forward side swipe
-// gesture.
-- (BOOL)canGoBack;
-- (BOOL)canGoForward;
-
-// Called on completion of a back/forward gesture.
-- (void)goBack:(web::WebState*)webState;
-- (void)goForward:(web::WebState*)webState;
-
-// The icon to display in the side panel.
-- (UIImage*)paneIcon;
-
-// Whether the icon is oriented and should be reflected on forward pane.
-- (BOOL)rotateForwardIcon;
-@end
-
 @protocol SideSwipeControllerDelegate
 @required
 // Called when the horizontal stack view is done and should be removed.
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
index 198f035..57a2f27 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
@@ -9,9 +9,7 @@
 #include "base/feature_list.h"
 #import "base/ios/block_types.h"
 #include "base/scoped_observer.h"
-#include "components/reading_list/core/reading_list_model.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
@@ -20,9 +18,7 @@
 #import "ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller_factory.h"
 #import "ios/chrome/browser/ui/fullscreen/scoped_fullscreen_disabler.h"
-#import "ios/chrome/browser/ui/reading_list/reading_list_side_swipe_provider.h"
 #import "ios/chrome/browser/ui/side_swipe/card_side_swipe_view.h"
-#import "ios/chrome/browser/ui/side_swipe/history_side_swipe_provider.h"
 #import "ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.h"
 #import "ios/chrome/browser/ui/side_swipe/side_swipe_util.h"
 #import "ios/chrome/browser/ui/side_swipe_gesture_recognizer.h"
@@ -31,6 +27,7 @@
 #import "ios/chrome/browser/ui/toolbar/public/side_swipe_toolbar_interacting.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
+#import "ios/web/public/navigation_item.h"
 #import "ios/web/public/web_client.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 
@@ -94,14 +91,6 @@
   // Curtain over web view while waiting for it to load.
   UIView* curtain_;
 
-  // Provides forward/back action for history entries.
-  HistorySideSwipeProvider* historySideSwipeProvider_;
-
-  // Provides forward action for reading list.
-  ReadingListSideSwipeProvider* readingListSideSwipeProvider_;
-
-  __weak id<SideSwipeContentProvider> currentContentProvider_;
-
   // The disabler that prevents the toolbar from being scrolled away when the
   // side swipe gesture is being recognized.
   std::unique_ptr<ScopedFullscreenDisabler> fullscreenDisabler_;
@@ -114,6 +103,11 @@
   ios::ChromeBrowserState* browserState_;
 }
 
+// Whether to allow navigating from the leading edge.
+@property(nonatomic, assign) BOOL leadingEdgeNavigationEnabled;
+// Whether to allow navigating from the trailing edge.
+@property(nonatomic, assign) BOOL trailingEdgeNavigationEnabled;
+
 // Load grey snapshots for the next |kIpadGreySwipeTabCount| tabs in
 // |direction|.
 - (void)createGreyCache:(UISwipeGestureRecognizerDirection)direction;
@@ -150,13 +144,6 @@
   if (self) {
     model_ = model;
     [model_ addObserver:self];
-    historySideSwipeProvider_ =
-        [[HistorySideSwipeProvider alloc] initWithTabModel:model_];
-
-    readingListSideSwipeProvider_ = [[ReadingListSideSwipeProvider alloc]
-        initWithReadingList:ReadingListModelFactory::GetForBrowserState(
-                                browserState)];
-
     webStateObserverBridge_ =
         std::make_unique<web::WebStateObserverBridge>(self);
     scopedWebStateObserver_ =
@@ -219,11 +206,17 @@
 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
     shouldBeRequiredToFailByGestureRecognizer:
         (UIGestureRecognizer*)otherGestureRecognizer {
-  // Only take precedence over a pan gesture recognizer so that moving up and
+  // Take precedence over a pan gesture recognizer so that moving up and
   // down while swiping doesn't trigger overscroll actions.
   if ([otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
     return YES;
   }
+  // Take precedence over a WKWebView side swipe gesture.
+  if ([otherGestureRecognizer
+          isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
+    return YES;
+  }
+
   return NO;
 }
 
@@ -259,6 +252,16 @@
     if (![gesture isEqual:swipeGestureRecognizer_]) {
       return NO;
     }
+
+    if (gesture.direction == UISwipeGestureRecognizerDirectionRight &&
+        !self.leadingEdgeNavigationEnabled) {
+      return NO;
+    }
+
+    if (gesture.direction == UISwipeGestureRecognizerDirectionLeft &&
+        !self.trailingEdgeNavigationEnabled) {
+      return NO;
+    }
     swipeType_ = SwipeType::CHANGE_PAGE;
     return YES;
   }
@@ -402,20 +405,14 @@
   }
 }
 
-- (id<SideSwipeContentProvider>)contentProviderForGesture:(BOOL)goBack {
-  if (goBack && [historySideSwipeProvider_ canGoBack]) {
-    return historySideSwipeProvider_;
+- (BOOL)canNavigate:(BOOL)goBack {
+  if (goBack && [[model_ currentTab] canGoBack]) {
+    return YES;
   }
-  if (!goBack && [historySideSwipeProvider_ canGoForward]) {
-    return historySideSwipeProvider_;
+  if (!goBack && [[model_ currentTab] canGoForward]) {
+    return YES;
   }
-  if (goBack && [readingListSideSwipeProvider_ canGoBack]) {
-    return readingListSideSwipeProvider_;
-  }
-  if (!goBack && [readingListSideSwipeProvider_ canGoForward]) {
-    return readingListSideSwipeProvider_;
-  }
-  return nil;
+  return NO;
 }
 
 // Show swipe to navigate.
@@ -432,9 +429,6 @@
     [swipeDelegate_ updateAccessoryViewsForSideSwipeWithVisibility:NO];
     BOOL goBack = IsSwipingBack(gesture.direction);
 
-    currentContentProvider_ = [self contentProviderForGesture:goBack];
-    BOOL canNavigate = currentContentProvider_ != nil;
-
     CGRect gestureBounds = gesture.view.bounds;
     CGFloat headerHeight = [swipeDelegate_ headerHeightForSideSwipe];
     CGRect navigationFrame =
@@ -446,9 +440,8 @@
     pageSideSwipeView_ = [[SideSwipeNavigationView alloc]
         initWithFrame:navigationFrame
         withDirection:gesture.direction
-          canNavigate:canNavigate
-                image:[currentContentProvider_ paneIcon]
-        rotateForward:[currentContentProvider_ rotateForwardIcon]];
+          canNavigate:[self canNavigate:goBack]
+                image:[UIImage imageNamed:@"side_swipe_navigation_back"]];
     [pageSideSwipeView_ setTargetView:[swipeDelegate_ sideSwipeContentView]];
 
     [gesture.view insertSubview:pageSideSwipeView_
@@ -467,9 +460,9 @@
         BOOL wantsBack = IsSwipingBack(gesture.direction);
         web::WebState* webState = [weakCurrentTab webState];
         if (wantsBack) {
-          [currentContentProvider_ goBack:webState];
+          [[model_ currentTab] goBack];
         } else {
-          [currentContentProvider_ goForward:webState];
+          [[model_ currentTab] goForward];
         }
 
         // Checking -IsLoading() is likely incorrect, but to narrow the scope of
@@ -586,6 +579,41 @@
   completionHandler();
 }
 
+- (void)updateNavigationEdgeSwipeForWebState:(web::WebState*)webState {
+  // With slim nav disabled, always use SideSwipeController's edge swipe for
+  // navigation.
+  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    self.leadingEdgeNavigationEnabled = YES;
+    self.trailingEdgeNavigationEnabled = YES;
+    return;
+  }
+
+  // With slim nav enabled, disable SideSwipeController's edge swipe for a
+  // typical navigation.  Continue to use SideSwipeController when on, before,
+  // or after a native page.
+  self.leadingEdgeNavigationEnabled = NO;
+  self.trailingEdgeNavigationEnabled = NO;
+
+  web::NavigationItem* item =
+      webState->GetNavigationManager()->GetVisibleItem();
+  if (item && UseNativeSwipe(item)) {
+    self.leadingEdgeNavigationEnabled = YES;
+    self.trailingEdgeNavigationEnabled = YES;
+  }
+
+  // If the previous page is an NTP, enable leading edge swipe.
+  web::NavigationItemList backItems =
+      webState->GetNavigationManager()->GetBackwardItems();
+  if (backItems.size() > 0 && UseNativeSwipe(backItems[0]))
+    self.leadingEdgeNavigationEnabled = YES;
+
+  // If the next page is an NTP, enable trailing edge swipe.
+  web::NavigationItemList fordwardItems =
+      webState->GetNavigationManager()->GetForwardItems();
+  if (fordwardItems.size() > 0 && UseNativeSwipe(fordwardItems[0]))
+    self.trailingEdgeNavigationEnabled = YES;
+}
+
 #pragma mark - CRWWebStateObserver Methods
 
 // Checking -webStateDidStopLoading is likely incorrect, but to narrow the scope
@@ -621,6 +649,12 @@
   // the gesture recognizer.
   [swipeGestureRecognizer_ setEnabled:NO];
   [swipeGestureRecognizer_ setEnabled:YES];
+
+  [self updateNavigationEdgeSwipeForWebState:newTab.webState];
+}
+
+- (void)tabModel:(TabModel*)model didChangeTab:(Tab*)tab {
+  [self updateNavigationEdgeSwipeForWebState:tab.webState];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller_unittest.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller_unittest.mm
index a98d8bfb..3f0d12e 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller_unittest.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller_unittest.mm
@@ -2,8 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/ui/side_swipe/side_swipe_controller.h"
+#include "base/test/scoped_feature_list.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/web/public/features.h"
+#import "ios/web/public/navigation_item.h"
+#import "ios/web/public/test/fakes/test_navigation_manager.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
@@ -14,6 +20,12 @@
 #error "This file requires ARC support."
 #endif
 
+@interface SideSwipeController (ExposedForTesting)
+@property(nonatomic, assign) BOOL leadingEdgeNavigationEnabled;
+@property(nonatomic, assign) BOOL trailingEdgeNavigationEnabled;
+- (void)updateNavigationEdgeSwipeForWebState:(web::WebState*)webState;
+@end
+
 namespace {
 
 class SideSwipeControllerTest : public PlatformTest {
@@ -39,6 +51,7 @@
   UIView* view_;
   TabModel* tab_model_;
   SideSwipeController* side_swipe_controller_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(SideSwipeControllerTest, TestConstructor) {
@@ -55,4 +68,46 @@
   EXPECT_TRUE(hasRecognizer);
 }
 
+// Tests that pages that need to use Chromium native swipe
+TEST_F(SideSwipeControllerTest, TestEdgeNavigationEnabled) {
+  feature_list_.InitAndEnableFeature(web::features::kSlimNavigationManager);
+
+  auto testWebState = std::make_unique<web::TestWebState>();
+  auto testNavigationManager = std::make_unique<web::TestNavigationManager>();
+  std::unique_ptr<web::NavigationItem> item = web::NavigationItem::Create();
+  testNavigationManager->SetVisibleItem(item.get());
+  testWebState->SetNavigationManager(std::move(testNavigationManager));
+
+  // The NTP and chrome://crash should use native swipe.
+  item->SetURL(GURL(kChromeUINewTabURL));
+  [side_swipe_controller_
+      updateNavigationEdgeSwipeForWebState:testWebState.get()];
+  EXPECT_TRUE(side_swipe_controller_.leadingEdgeNavigationEnabled);
+  EXPECT_TRUE(side_swipe_controller_.trailingEdgeNavigationEnabled);
+
+  item->SetURL(GURL("chrome://crash"));
+  [side_swipe_controller_
+      updateNavigationEdgeSwipeForWebState:testWebState.get()];
+  EXPECT_TRUE(side_swipe_controller_.leadingEdgeNavigationEnabled);
+  EXPECT_TRUE(side_swipe_controller_.trailingEdgeNavigationEnabled);
+
+  item->SetURL(GURL("http://wwww.test.com"));
+  [side_swipe_controller_
+      updateNavigationEdgeSwipeForWebState:testWebState.get()];
+  EXPECT_FALSE(side_swipe_controller_.leadingEdgeNavigationEnabled);
+  EXPECT_FALSE(side_swipe_controller_.trailingEdgeNavigationEnabled);
+
+  item->SetURL(GURL("chrome://foo"));
+  [side_swipe_controller_
+      updateNavigationEdgeSwipeForWebState:testWebState.get()];
+  EXPECT_FALSE(side_swipe_controller_.leadingEdgeNavigationEnabled);
+  EXPECT_FALSE(side_swipe_controller_.trailingEdgeNavigationEnabled);
+
+  item->SetURL(GURL("chrome://version"));
+  [side_swipe_controller_
+      updateNavigationEdgeSwipeForWebState:testWebState.get()];
+  EXPECT_FALSE(side_swipe_controller_.leadingEdgeNavigationEnabled);
+  EXPECT_FALSE(side_swipe_controller_.trailingEdgeNavigationEnabled);
+}
+
 }  // anonymous namespace
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.h b/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.h
index c8a4e2b..0994865c 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.h
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.h
@@ -19,8 +19,7 @@
 - (instancetype)initWithFrame:(CGRect)frame
                 withDirection:(UISwipeGestureRecognizerDirection)direction
                   canNavigate:(BOOL)canNavigate
-                        image:(UIImage*)image
-                rotateForward:(BOOL)rotateForward;
+                        image:(UIImage*)image;
 
 // Update views for latest gesture, and call completion blocks whether
 // |threshold| is met.
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.mm
index 7b07882..d84eccf7 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.mm
@@ -81,10 +81,6 @@
   // If |NO| this is an edge gesture and navigation isn't possible. Don't show
   // arrows and bubbles and don't allow navigate.
   BOOL canNavigate_;
-
-  // If |YES| arrowView_ is directionnal and must be rotated 180 degreed for the
-  // forward panes.
-  BOOL rotateForward_;
 }
 // Returns a newly allocated and configured selection circle shape.
 - (CAShapeLayer*)newSelectionCircleLayer;
@@ -100,14 +96,12 @@
 - (instancetype)initWithFrame:(CGRect)frame
                 withDirection:(UISwipeGestureRecognizerDirection)direction
                   canNavigate:(BOOL)canNavigate
-                        image:(UIImage*)image
-                rotateForward:(BOOL)rotateForward {
+                        image:(UIImage*)image {
   self = [super initWithFrame:frame];
   if (self) {
     self.backgroundColor = [UIColor colorWithWhite:90.0 / 256 alpha:1.0];
 
     canNavigate_ = canNavigate;
-    rotateForward_ = rotateForward;
     if (canNavigate) {
       image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
       const CGRect imageSize = CGRectMake(0, 0, 24, 24);
@@ -188,13 +182,8 @@
   CGFloat rotationStart = -CGFloat(base::kPiDouble) / 2;
   CGFloat rotationEnd = 0;
   if (gesture.direction == UISwipeGestureRecognizerDirectionLeft) {
-    if (rotateForward_) {
-      rotationStart = CGFloat(base::kPiDouble) * 1.5;
-      rotationEnd = CGFloat(base::kPiDouble);
-    } else {
-      rotationStart = CGFloat(base::kPiDouble) / 2;
-      rotationEnd = 0;
-    }
+    rotationStart = CGFloat(base::kPiDouble) * 1.5;
+    rotationEnd = CGFloat(base::kPiDouble);
   }
   CGAffineTransform rotation = CGAffineTransformMakeRotation(MapValueToRange(
       {0, kArrowThreshold}, {rotationStart, rotationEnd}, distance));
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_util.h b/ios/chrome/browser/ui/side_swipe/side_swipe_util.h
index 27f28ac..715c145 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_util.h
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_util.h
@@ -7,10 +7,18 @@
 
 #import <UIKit/UIKit.h>
 
+namespace web {
+class NavigationItem;
+}
+
 // If swiping to the right (or left in RTL).
 BOOL IsSwipingBack(UISwipeGestureRecognizerDirection direction);
 
 // If swiping to the left (or right in RTL).
 BOOL IsSwipingForward(UISwipeGestureRecognizerDirection direction);
 
+// Returns |YES| if the item should use Chromium native swipe.  This is true for
+// the NTP and chrome://crash.
+BOOL UseNativeSwipe(web::NavigationItem* item);
+
 #endif  // IOS_CHROME_BROWSER_UI_SIDE_SWIPE_SIDE_SWIPE_UTIL_H_
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_util.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_util.mm
index 9b0e44c..bdcd3faf 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_util.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_util.mm
@@ -6,7 +6,11 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/chrome_url_util.h"
+#import "ios/chrome/browser/ui/ntp/ntp_util.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
+#import "ios/web/public/navigation_item.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -25,3 +29,14 @@
   else
     return direction == UISwipeGestureRecognizerDirectionLeft;
 }
+
+BOOL UseNativeSwipe(web::NavigationItem* item) {
+  if (IsURLNewTabPage(item->GetVirtualURL()))
+    return YES;
+
+  GURL url(item->GetURL());
+  if (UrlHasChromeScheme(url) && url.host_piece() == kChromeUICrashHost)
+    return YES;
+
+  return NO;
+}
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index 5308f62c..01547397f 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -8,11 +8,13 @@
   visibility = [ ":material_components_ios" ]
   include_dirs = [
     "src/components/ActivityIndicator/src",
+    "src/components/Buttons/src/ButtonThemer",
     "src/components/Buttons/src/ShapeThemer",
     "src/components/AppBar/src",
     "src/components/private/ShapeLibrary/src",
     "src/components/ButtonBar/src",
     "src/components/Buttons/src",
+    "src/components/Cards/src",
     "src/components/CollectionCells/src",
     "src/components/CollectionLayoutAttributes/src",
     "src/components/Collections/src",
@@ -137,6 +139,10 @@
     "src/components/Buttons/src/TypographyThemer/MDCButtonTypographyThemer.h",
     "src/components/Buttons/src/TypographyThemer/MDCButtonTypographyThemer.m",
     "src/components/Buttons/src/private/MDCButton+Subclassing.h",
+    "src/components/Cards/src/ColorThemer/MDCCardsColorThemer.h",
+    "src/components/Cards/src/ColorThemer/MDCCardsColorThemer.m",
+    "src/components/Cards/src/MDCCard.h",
+    "src/components/Cards/src/MDCCard.m",
     "src/components/CollectionCells/src/MDCCollectionViewCell.h",
     "src/components/CollectionCells/src/MDCCollectionViewCell.m",
     "src/components/CollectionCells/src/MDCCollectionViewTextCell.h",
diff --git a/ios/web/public/test/fakes/test_navigation_manager.mm b/ios/web/public/test/fakes/test_navigation_manager.mm
index 1f3485c..cecff9c 100644
--- a/ios/web/public/test/fakes/test_navigation_manager.mm
+++ b/ios/web/public/test/fakes/test_navigation_manager.mm
@@ -154,12 +154,10 @@
 }
 
 NavigationItemList TestNavigationManager::GetBackwardItems() const {
-  NOTREACHED();
   return NavigationItemList();
 }
 
 NavigationItemList TestNavigationManager::GetForwardItems() const {
-  NOTREACHED();
   return NavigationItemList();
 }
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 0cbf1daa..3c1e05c 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -954,6 +954,10 @@
   if (self) {
     _webStateImpl = webState;
     _webUsageEnabled = YES;
+
+    if (web::GetWebClient()->IsSlimNavigationManagerEnabled())
+      _allowsBackForwardNavigationGestures = YES;
+
     DCHECK(_webStateImpl);
     // Load phase when no WebView present is 'loaded' because this represents
     // the idle state.
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index d7c6f5c..bfd994b 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -212,7 +212,7 @@
                        context:nullptr];
     [[result stub] removeObserver:web_controller() forKeyPath:OCMOCK_ANY];
     [[result stub] evaluateJavaScript:OCMOCK_ANY completionHandler:OCMOCK_ANY];
-    // CRWWebController sets this property to NO by default.
+    [[result stub] setAllowsBackForwardNavigationGestures:YES];
     [[result stub] setAllowsBackForwardNavigationGestures:NO];
 
     return result;
@@ -310,13 +310,18 @@
   EXPECT_FALSE(observer.did_finish_navigation_info());
 }
 
-// Tests allowsBackForwardNavigationGestures default value and setting this
-// property to YES.
+// Tests allowsBackForwardNavigationGestures default value and negating this
+// property.
 TEST_P(CRWWebControllerTest, SetAllowsBackForwardNavigationGestures) {
-  OCMExpect([mock_web_view_ setAllowsBackForwardNavigationGestures:YES]);
-  EXPECT_FALSE(web_controller().allowsBackForwardNavigationGestures);
-  web_controller().allowsBackForwardNavigationGestures = YES;
-  EXPECT_TRUE(web_controller().allowsBackForwardNavigationGestures);
+  if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
+    EXPECT_TRUE(web_controller().allowsBackForwardNavigationGestures);
+    web_controller().allowsBackForwardNavigationGestures = NO;
+    EXPECT_FALSE(web_controller().allowsBackForwardNavigationGestures);
+  } else {
+    EXPECT_FALSE(web_controller().allowsBackForwardNavigationGestures);
+    web_controller().allowsBackForwardNavigationGestures = YES;
+    EXPECT_TRUE(web_controller().allowsBackForwardNavigationGestures);
+  }
 }
 
 INSTANTIATE_TEST_CASES(CRWWebControllerTest);
diff --git a/ios/web_view/internal/translate/web_view_translate_client.mm b/ios/web_view/internal/translate/web_view_translate_client.mm
index 6871fd4..1e5106bc 100644
--- a/ios/web_view/internal/translate/web_view_translate_client.mm
+++ b/ios/web_view/internal/translate/web_view_translate_client.mm
@@ -32,8 +32,6 @@
 #error "This file requires ARC support."
 #endif
 
-DEFINE_WEB_STATE_USER_DATA_KEY(ios_web_view::WebViewTranslateClient);
-
 namespace ios_web_view {
 
 WebViewTranslateClient::WebViewTranslateClient(web::WebState* web_state)
diff --git a/media/audio/audio_output_device.cc b/media/audio/audio_output_device.cc
index fa6a12c..b298f7a 100644
--- a/media/audio/audio_output_device.cc
+++ b/media/audio/audio_output_device.cc
@@ -15,6 +15,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/timer/timer.h"
 #include "base/trace_event/trace_event.h"
@@ -22,6 +23,7 @@
 #include "media/audio/audio_device_description.h"
 #include "media/audio/audio_output_controller.h"
 #include "media/audio/audio_output_device_thread_callback.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/limits.h"
 
 namespace media {
@@ -64,6 +66,17 @@
 }
 
 AudioOutputDevice::~AudioOutputDevice() {
+  {
+    // Abort any pending callbacks. Technically we don't need to acquire the
+    // lock here since ther eshould be no other calls outstanding, but because
+    // we've used the GUARDED_BY compiler syntax, we'll get an error without it.
+    base::AutoLock auto_lock(device_info_lock_);
+    if (pending_device_info_cb_) {
+      std::move(pending_device_info_cb_)
+          .Run(OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL));
+    }
+  }
+
 #if DCHECK_IS_ON()
   // Make sure we've stopped the stream properly before destructing |this|.
   DCHECK(audio_thread_lock_.Try());
@@ -97,7 +110,6 @@
     audio_thread_.reset();
     stopping_hack_ = true;
   }
-
   io_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&AudioOutputDevice::ShutDownOnIOThread, this));
 }
@@ -128,13 +140,29 @@
 OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() {
   TRACE_EVENT0("audio", "AudioOutputDevice::GetOutputDeviceInfo");
   DCHECK(!io_task_runner_->BelongsToCurrentThread());
-
   did_receive_auth_.Wait();
-  return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice(
-                              session_id_, device_id_)
-                              ? matched_device_id_
-                              : device_id_,
-                          device_status_, output_params_);
+  return GetOutputDeviceInfo_Signaled();
+}
+
+void AudioOutputDevice::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
+  {
+    // Hold the lock while checking the signal and setting the pending callback
+    // to avoid racing with authorization completion on the IO thread.
+    base::AutoLock auto_lock(device_info_lock_);
+    if (!did_receive_auth_.IsSignaled()) {
+      DCHECK(!pending_device_info_cb_);
+      pending_device_info_cb_ = BindToCurrentLoop(std::move(info_cb));
+      return;
+    }
+  }
+
+  // Always post to avoid the caller being reentrant. Local testing shows even
+  // on a powerful desktop, we haven't received device authorization by this
+  // point when AOD construction and GetOutputDeviceInfoAsync() happen back to
+  // back (which is the most common use case).
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(info_cb), GetOutputDeviceInfo_Signaled()));
 }
 
 bool AudioOutputDevice::IsOptimizedForHardwareParameters() {
@@ -307,7 +335,7 @@
                << ", device_id: " << device_id_
                << ", matched_device_id: " << matched_device_id_;
 
-      did_receive_auth_.Signal();
+      OnAuthSignal();
     }
   } else {
     TRACE_EVENT1("audio", "AudioOutputDevice not authorized", "auth status",
@@ -378,8 +406,39 @@
   ipc_.reset();
   state_ = IDLE;
 
-  // Signal to unblock any blocked threads waiting for parameters
+  OnAuthSignal();
+}
+
+OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo_Signaled() {
+  DCHECK(did_receive_auth_.IsSignaled());
+  return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice(
+                              session_id_, device_id_)
+                              ? matched_device_id_
+                              : device_id_,
+                          device_status_, output_params_);
+}
+
+void AudioOutputDevice::OnAuthSignal() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  // This lock is held while signaling to avoid any thread safety issues while
+  // GetOutputDeviceInfoAsync() may be checking the signal and modifying the
+  // |pending_device_info_cb_| on another thread.
+  //
+  // We might be able to get away with signaling outside of the lock, but this
+  // requires more careful construction for anyone checking the signal and
+  // using the result to set or get the pending callback value. The failure
+  // mode is also more subtle, callbacks will be lost versus a thread hang which
+  // is more easily detectable in the production population.
+  base::AutoLock auto_lock(device_info_lock_);
+
+  // Signal to unblock any blocked threads waiting for parameters.
   did_receive_auth_.Signal();
+
+  // The callback is always posted by way media::BindToCurrentLoop() usage upon
+  // receipt, so this is safe to run under the lock.
+  if (pending_device_info_cb_)
+    std::move(pending_device_info_cb_).Run(GetOutputDeviceInfo_Signaled());
 }
 
 void AudioOutputDevice::NotifyRenderCallbackOfError() {
diff --git a/media/audio/audio_output_device.h b/media/audio/audio_output_device.h
index cc48143..a512e72b 100644
--- a/media/audio/audio_output_device.h
+++ b/media/audio/audio_output_device.h
@@ -110,6 +110,7 @@
   void Pause() override;
   bool SetVolume(double volume) override;
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
   bool IsOptimizedForHardwareParameters() override;
   bool CurrentThreadIsRenderingThread() override;
 
@@ -171,6 +172,9 @@
 
   void NotifyRenderCallbackOfError();
 
+  OutputDeviceInfo GetOutputDeviceInfo_Signaled();
+  void OnAuthSignal();
+
   const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
   AudioParameters audio_parameters_;
@@ -226,6 +230,14 @@
   const base::TimeDelta auth_timeout_;
   std::unique_ptr<base::OneShotTimer> auth_timeout_action_;
 
+  // Pending callback for OutputDeviceInfo if it has not been received by the
+  // time a call to GetGetOutputDeviceInfoAsync() is called.
+  //
+  // Lock for use ONLY with |pending_device_info_cb_| and |did_receive_auth_|,
+  // if you add more usage of this lock ensure you have not added a deadlock.
+  base::Lock device_info_lock_;
+  OutputDeviceInfoCB pending_device_info_cb_ GUARDED_BY(device_info_lock_);
+
   DISALLOW_COPY_AND_ASSIGN(AudioOutputDevice);
 };
 
diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc
index 9eaba2e..416d057f 100644
--- a/media/audio/audio_output_device_unittest.cc
+++ b/media/audio/audio_output_device_unittest.cc
@@ -99,9 +99,11 @@
   void StartAudioDevice();
   void CallOnStreamCreated();
   void StopAudioDevice();
-  void CreateDevice(const std::string& device_id);
+  void CreateDevice(const std::string& device_id,
+                    base::TimeDelta timeout = kAuthTimeout);
   void SetDevice(const std::string& device_id);
-  void CheckDeviceStatus(OutputDeviceStatus device_status);
+
+  MOCK_METHOD1(OnDeviceInfoReceived, void(OutputDeviceInfo));
 
  protected:
   base::test::ScopedTaskEnvironment task_env_{
@@ -134,7 +136,8 @@
   audio_device_ = nullptr;
 }
 
-void AudioOutputDeviceTest::CreateDevice(const std::string& device_id) {
+void AudioOutputDeviceTest::CreateDevice(const std::string& device_id,
+                                         base::TimeDelta timeout) {
   // Make sure the previous device is properly cleaned up.
   if (audio_device_)
     StopAudioDevice();
@@ -142,7 +145,7 @@
   audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>();
   audio_device_ = new AudioOutputDevice(
       base::WrapUnique(audio_output_ipc_), task_env_.GetMainThreadTaskRunner(),
-      AudioSinkParameters(0, device_id), kAuthTimeout);
+      AudioSinkParameters(0, device_id), timeout);
 }
 
 void AudioOutputDeviceTest::SetDevice(const std::string& device_id) {
@@ -163,11 +166,6 @@
                             &callback_);
 }
 
-void AudioOutputDeviceTest::CheckDeviceStatus(OutputDeviceStatus status) {
-  DCHECK(!task_env_.GetMainThreadTaskRunner()->BelongsToCurrentThread());
-  EXPECT_EQ(status, audio_device_->GetOutputDeviceInfo().device_status());
-}
-
 void AudioOutputDeviceTest::ReceiveAuthorization(OutputDeviceStatus status) {
   device_status_ = status;
   if (device_status_ != OUTPUT_DEVICE_STATUS_OK)
@@ -333,6 +331,57 @@
   task_env_.FastForwardBy(base::TimeDelta());
 }
 
+TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Error) {
+  CreateDevice(kUnauthorizedDeviceId, base::TimeDelta());
+  EXPECT_CALL(*audio_output_ipc_,
+              RequestDeviceAuthorization(audio_device_.get(), 0,
+                                         kUnauthorizedDeviceId));
+  audio_device_->RequestDeviceAuthorization();
+  audio_device_->GetOutputDeviceInfoAsync(base::BindOnce(
+      &AudioOutputDeviceTest::OnDeviceInfoReceived, base::Unretained(this)));
+  task_env_.FastForwardBy(base::TimeDelta());
+
+  OutputDeviceInfo info;
+  constexpr auto kExpectedStatus = OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED;
+  EXPECT_CALL(*this, OnDeviceInfoReceived(_))
+      .WillOnce(testing::SaveArg<0>(&info));
+  ReceiveAuthorization(kExpectedStatus);
+
+  task_env_.FastForwardUntilNoTasksRemain();
+  EXPECT_EQ(kExpectedStatus, info.device_status());
+  EXPECT_EQ(kUnauthorizedDeviceId, info.device_id());
+  EXPECT_TRUE(
+      AudioParameters::UnavailableDeviceParams().Equals(info.output_params()));
+
+  audio_device_->Stop();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Okay) {
+  CreateDevice(kDefaultDeviceId, base::TimeDelta());
+  EXPECT_CALL(
+      *audio_output_ipc_,
+      RequestDeviceAuthorization(audio_device_.get(), 0, kDefaultDeviceId));
+  audio_device_->RequestDeviceAuthorization();
+  audio_device_->GetOutputDeviceInfoAsync(base::BindOnce(
+      &AudioOutputDeviceTest::OnDeviceInfoReceived, base::Unretained(this)));
+  task_env_.FastForwardBy(base::TimeDelta());
+
+  OutputDeviceInfo info;
+  constexpr auto kExpectedStatus = OUTPUT_DEVICE_STATUS_OK;
+  EXPECT_CALL(*this, OnDeviceInfoReceived(_))
+      .WillOnce(testing::SaveArg<0>(&info));
+  ReceiveAuthorization(kExpectedStatus);
+
+  task_env_.FastForwardUntilNoTasksRemain();
+  EXPECT_EQ(kExpectedStatus, info.device_status());
+  EXPECT_EQ(kDefaultDeviceId, info.device_id());
+  EXPECT_TRUE(default_audio_parameters_.Equals(info.output_params()));
+
+  audio_device_->Stop();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
 namespace {
 
 // This struct collects useful stuff without doing anything magical. It is used
diff --git a/media/audio/audio_output_stream_sink.cc b/media/audio/audio_output_stream_sink.cc
index 2ff00123..7974224a 100644
--- a/media/audio/audio_output_stream_sink.cc
+++ b/media/audio/audio_output_stream_sink.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/location.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "media/audio/audio_manager.h"
 #include "media/base/audio_timestamp_helper.h"
 
@@ -77,7 +78,13 @@
 }
 
 OutputDeviceInfo AudioOutputStreamSink::GetOutputDeviceInfo() {
-  return OutputDeviceInfo();
+  return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
+}
+
+void AudioOutputStreamSink::GetOutputDeviceInfoAsync(
+    OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), GetOutputDeviceInfo()));
 }
 
 bool AudioOutputStreamSink::IsOptimizedForHardwareParameters() {
diff --git a/media/audio/audio_output_stream_sink.h b/media/audio/audio_output_stream_sink.h
index 40bbecf0..e5fadfa90 100644
--- a/media/audio/audio_output_stream_sink.h
+++ b/media/audio/audio_output_stream_sink.h
@@ -42,6 +42,7 @@
   void Play() override;
   bool SetVolume(double volume) override;
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
   bool IsOptimizedForHardwareParameters() override;
   bool CurrentThreadIsRenderingThread() override;
 
diff --git a/media/audio/clockless_audio_sink.cc b/media/audio/clockless_audio_sink.cc
index 4f15d24..a41bf20 100644
--- a/media/audio/clockless_audio_sink.cc
+++ b/media/audio/clockless_audio_sink.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/simple_thread.h"
 #include "media/base/audio_hash.h"
 
@@ -47,27 +48,27 @@
   }
 
  private:
-   // Call Render() repeatedly, keeping track of the rendering time.
+  // Call Render() repeatedly, keeping track of the rendering time.
   void Run() override {
-     base::TimeTicks start;
-     while (!stop_event_->IsSignaled()) {
-       const int frames_received = callback_->Render(
-           base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get());
-       DCHECK_GE(frames_received, 0);
-       if (audio_hash_)
-         audio_hash_->Update(audio_bus_.get(), frames_received);
-       if (!frames_received) {
-         // No data received, so let other threads run to provide data.
-         base::PlatformThread::YieldCurrentThread();
-       } else if (start.is_null()) {
-         // First time we processed some audio, so record the starting time.
-         start = base::TimeTicks::Now();
-       } else {
-         // Keep track of the last time data was rendered.
-         playback_time_ = base::TimeTicks::Now() - start;
-       }
-     }
-   }
+    base::TimeTicks start;
+    while (!stop_event_->IsSignaled()) {
+      const int frames_received = callback_->Render(
+          base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get());
+      DCHECK_GE(frames_received, 0);
+      if (audio_hash_)
+        audio_hash_->Update(audio_bus_.get(), frames_received);
+      if (!frames_received) {
+        // No data received, so let other threads run to provide data.
+        base::PlatformThread::YieldCurrentThread();
+      } else if (start.is_null()) {
+        // First time we processed some audio, so record the starting time.
+        start = base::TimeTicks::Now();
+      } else {
+        // Keep track of the last time data was rendered.
+        playback_time_ = base::TimeTicks::Now() - start;
+      }
+    }
+  }
 
   AudioRendererSink::RenderCallback* callback_;
   std::unique_ptr<AudioBus> audio_bus_;
@@ -135,6 +136,11 @@
   return device_info_;
 }
 
+void ClocklessAudioSink::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), device_info_));
+}
+
 bool ClocklessAudioSink::IsOptimizedForHardwareParameters() {
   return is_optimized_for_hw_params_;
 }
diff --git a/media/audio/clockless_audio_sink.h b/media/audio/clockless_audio_sink.h
index fec3dd5..8dbfb52 100644
--- a/media/audio/clockless_audio_sink.h
+++ b/media/audio/clockless_audio_sink.h
@@ -31,6 +31,7 @@
   void Play() override;
   bool SetVolume(double volume) override;
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
   bool IsOptimizedForHardwareParameters() override;
   bool CurrentThreadIsRenderingThread() override;
 
diff --git a/media/audio/null_audio_sink.cc b/media/audio/null_audio_sink.cc
index 3a11ef6..f436cf2 100644
--- a/media/audio/null_audio_sink.cc
+++ b/media/audio/null_audio_sink.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "media/base/audio_hash.h"
 #include "media/base/fake_audio_worker.h"
 
@@ -79,6 +80,11 @@
   return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
 }
 
+void NullAudioSink::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), GetOutputDeviceInfo()));
+}
+
 bool NullAudioSink::IsOptimizedForHardwareParameters() {
   return false;
 }
diff --git a/media/audio/null_audio_sink.h b/media/audio/null_audio_sink.h
index 21d255e9..69e1a48 100644
--- a/media/audio/null_audio_sink.h
+++ b/media/audio/null_audio_sink.h
@@ -34,6 +34,7 @@
   void Play() override;
   bool SetVolume(double volume) override;
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
   bool IsOptimizedForHardwareParameters() override;
   bool CurrentThreadIsRenderingThread() override;
   void SwitchOutputDevice(const std::string& device_id,
diff --git a/media/base/audio_renderer_mixer_input.cc b/media/base/audio_renderer_mixer_input.cc
index ba821ee0..9754bb68 100644
--- a/media/base/audio_renderer_mixer_input.cc
+++ b/media/base/audio_renderer_mixer_input.cc
@@ -21,14 +21,9 @@
     const std::string& device_id,
     AudioLatency::LatencyType latency)
     : mixer_pool_(mixer_pool),
-      started_(false),
-      playing_(false),
-      volume_(1.0f),
       owner_id_(owner_id),
       device_id_(device_id),
       latency_(latency),
-      mixer_(nullptr),
-      callback_(nullptr),
       error_cb_(base::Bind(&AudioRendererMixerInput::OnRenderError,
                            base::Unretained(this))) {
   DCHECK(mixer_pool_);
@@ -112,6 +107,12 @@
                       owner_id_, 0 /* session_id */, device_id_);
 }
 
+void AudioRendererMixerInput::GetOutputDeviceInfoAsync(
+    OutputDeviceInfoCB info_cb) {
+  // TODO(dalecurtis): Implement this for https://crbug.com/905506.
+  NOTREACHED();
+}
+
 bool AudioRendererMixerInput::IsOptimizedForHardwareParameters() {
   return true;
 }
@@ -173,8 +174,8 @@
 
   // AudioConverter expects unfilled frames to be zeroed.
   if (frames_filled < audio_bus->frames()) {
-    audio_bus->ZeroFramesPartial(
-        frames_filled, audio_bus->frames() - frames_filled);
+    audio_bus->ZeroFramesPartial(frames_filled,
+                                 audio_bus->frames() - frames_filled);
   }
 
   // We're reading |volume_| from the audio device thread and must avoid racing
diff --git a/media/base/audio_renderer_mixer_input.h b/media/base/audio_renderer_mixer_input.h
index c4cfc49..aeb5781 100644
--- a/media/base/audio_renderer_mixer_input.h
+++ b/media/base/audio_renderer_mixer_input.h
@@ -47,6 +47,8 @@
   void Pause() override;
   bool SetVolume(double volume) override;
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
+
   bool IsOptimizedForHardwareParameters() override;
   void Initialize(const AudioParameters& params,
                   AudioRendererSink::RenderCallback* renderer) override;
@@ -74,9 +76,9 @@
   // SetVolume().
   base::Lock volume_lock_;
 
-  bool started_;
-  bool playing_;
-  double volume_ GUARDED_BY(volume_lock_);
+  bool started_ = false;
+  bool playing_ = false;
+  double volume_ GUARDED_BY(volume_lock_) = 1.0;
 
   // AudioConverter::InputCallback implementation.
   double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
@@ -90,10 +92,10 @@
 
   // AudioRendererMixer obtained from mixer pool during Initialize(),
   // guaranteed to live (at least) until it is returned to the pool.
-  AudioRendererMixer* mixer_;
+  AudioRendererMixer* mixer_ = nullptr;
 
   // Source of audio data which is provided to the mixer.
-  AudioRendererSink::RenderCallback* callback_;
+  AudioRendererSink::RenderCallback* callback_ = nullptr;
 
   // Error callback for handing to AudioRendererMixer.
   const base::Closure error_cb_;
diff --git a/media/base/audio_renderer_sink.h b/media/base/audio_renderer_sink.h
index 9b38c98..fefc018 100644
--- a/media/base/audio_renderer_sink.h
+++ b/media/base/audio_renderer_sink.h
@@ -66,12 +66,29 @@
   virtual bool SetVolume(double volume) = 0;
 
   // Returns current output device information. If the information is not
-  // available yet, this method may block until it becomes available.
-  // If the sink is not associated with any output device, |device_status| of
-  // OutputDeviceInfo should be set to OUTPUT_DEVICE_STATUS_ERROR_INTERNAL.
-  // Must never be called on the IO thread.
+  // available yet, this method may block until it becomes available. If the
+  // sink is not associated with any output device, |device_status| of
+  // OutputDeviceInfo should be set to OUTPUT_DEVICE_STATUS_ERROR_INTERNAL. Must
+  // never be called on the IO thread.
+  //
+  // Note: Prefer to use GetOutputDeviceInfoAsync instead if possible.
   virtual OutputDeviceInfo GetOutputDeviceInfo() = 0;
 
+  // Same as the above, but does not block and will execute |info_cb| when the
+  // OutputDeviceInfo is available. Callback will be executed on the calling
+  // thread. Prefer this function to the synchronous version, it does not have a
+  // timeout so will result in less spurious timeout errors.
+  //
+  // |info_cb| will always be posted (I.e., executed after this function
+  // returns), even if OutputDeviceInfo is already available.
+  //
+  // Upon destruction if OutputDeviceInfo is still not available, |info_cb| will
+  // be posted with OUTPUT_DEVICE_STATUS_ERROR_INTERNAL. Note: Because |info_cb|
+  // is posted it will execute after destruction, so clients must handle
+  // cancellation of the callback if needed.
+  using OutputDeviceInfoCB = base::OnceCallback<void(OutputDeviceInfo)>;
+  virtual void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) = 0;
+
   // Returns |true| if a source with hardware parameters is preferable.
   virtual bool IsOptimizedForHardwareParameters() = 0;
 
diff --git a/media/base/fake_audio_renderer_sink.cc b/media/base/fake_audio_renderer_sink.cc
index e80a0f81..924691ea 100644
--- a/media/base/fake_audio_renderer_sink.cc
+++ b/media/base/fake_audio_renderer_sink.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 
 namespace media {
 
@@ -69,6 +70,12 @@
   return output_device_info_;
 }
 
+void FakeAudioRendererSink::GetOutputDeviceInfoAsync(
+    OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), output_device_info_));
+}
+
 bool FakeAudioRendererSink::IsOptimizedForHardwareParameters() {
   return is_optimized_for_hw_params_;
 }
diff --git a/media/base/fake_audio_renderer_sink.h b/media/base/fake_audio_renderer_sink.h
index 7e87a96..de599d2 100644
--- a/media/base/fake_audio_renderer_sink.h
+++ b/media/base/fake_audio_renderer_sink.h
@@ -39,6 +39,7 @@
   void Play() override;
   bool SetVolume(double volume) override;
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
   bool IsOptimizedForHardwareParameters() override;
   bool CurrentThreadIsRenderingThread() override;
 
diff --git a/media/base/mock_audio_renderer_sink.cc b/media/base/mock_audio_renderer_sink.cc
index ecf21658..4c7c656d 100644
--- a/media/base/mock_audio_renderer_sink.cc
+++ b/media/base/mock_audio_renderer_sink.cc
@@ -4,6 +4,9 @@
 
 #include "media/base/mock_audio_renderer_sink.h"
 
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
 namespace media {
 MockAudioRendererSink::MockAudioRendererSink()
     : MockAudioRendererSink(OUTPUT_DEVICE_STATUS_OK) {}
@@ -45,6 +48,12 @@
   return output_device_info_;
 }
 
+void MockAudioRendererSink::GetOutputDeviceInfoAsync(
+    OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), output_device_info_));
+}
+
 bool MockAudioRendererSink::IsOptimizedForHardwareParameters() {
   return false;
 }
diff --git a/media/base/mock_audio_renderer_sink.h b/media/base/mock_audio_renderer_sink.h
index 78110e9..3c848812 100644
--- a/media/base/mock_audio_renderer_sink.h
+++ b/media/base/mock_audio_renderer_sink.h
@@ -32,6 +32,7 @@
   MOCK_METHOD0(CurrentThreadIsRenderingThread, bool());
 
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
 
   bool IsOptimizedForHardwareParameters() override;
 
diff --git a/media/blink/webaudiosourceprovider_impl.cc b/media/blink/webaudiosourceprovider_impl.cc
index 672d44d42..e24103f0 100644
--- a/media/blink/webaudiosourceprovider_impl.cc
+++ b/media/blink/webaudiosourceprovider_impl.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/thread_annotations.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/audio/null_audio_sink.h"
 #include "media/base/audio_timestamp_helper.h"
@@ -266,6 +267,20 @@
                : OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND);
 }
 
+void WebAudioSourceProviderImpl::GetOutputDeviceInfoAsync(
+    OutputDeviceInfoCB info_cb) {
+  base::AutoLock auto_lock(sink_lock_);
+  if (sink_) {
+    sink_->GetOutputDeviceInfoAsync(std::move(info_cb));
+    return;
+  }
+
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(info_cb),
+                     OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND)));
+}
+
 bool WebAudioSourceProviderImpl::IsOptimizedForHardwareParameters() {
   base::AutoLock auto_lock(sink_lock_);
   return client_ ? false : true;
diff --git a/media/blink/webaudiosourceprovider_impl.h b/media/blink/webaudiosourceprovider_impl.h
index 52f881f..4c68db2e 100644
--- a/media/blink/webaudiosourceprovider_impl.h
+++ b/media/blink/webaudiosourceprovider_impl.h
@@ -67,6 +67,7 @@
   void Pause() override;
   bool SetVolume(double volume) override;
   OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
   bool IsOptimizedForHardwareParameters() override;
   bool CurrentThreadIsRenderingThread() override;
   void SwitchOutputDevice(const std::string& device_id,
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 6107c0b4..c152880 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -8,12 +8,21 @@
 
 declare_args() {
   enable_mojo_tracing = false
+
+  # enable_random_mojo_delays starts a task runner that periodically pauses
+  # random Mojo bindings and later resumes them, in order to test whether parts
+  # of the code implicitly rely on FIFO processing of messages sent on different
+  # message pipes (which they should not).
+  enable_random_mojo_delays = false
 }
 
 buildflag_header("mojo_buildflags") {
   header = "mojo_buildflags.h"
 
-  flags = [ "MOJO_TRACE_ENABLED=$enable_mojo_tracing" ]
+  flags = [
+    "MOJO_TRACE_ENABLED=$enable_mojo_tracing",
+    "MOJO_RANDOM_DELAYS_ENABLED=$enable_random_mojo_delays",
+  ]
 }
 
 # Headers and sources which generated bindings can depend upon. No need for
@@ -167,6 +176,13 @@
     "unique_ptr_impl_ref_traits.h",
   ]
 
+  if (enable_random_mojo_delays) {
+    sources += [
+      "lib/test_random_mojo_delays.cc",
+      "lib/test_random_mojo_delays.h",
+    ]
+  }
+
   if (enable_ipc_fuzzer && !is_nacl_nonsfi) {
     sources += [
       "lib/message_dumper.cc",
diff --git a/mojo/public/cpp/bindings/lib/binding_state.cc b/mojo/public/cpp/bindings/lib/binding_state.cc
index bb4a20f..846c9ef 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.cc
+++ b/mojo/public/cpp/bindings/lib/binding_state.cc
@@ -3,8 +3,12 @@
 // found in the LICENSE file.
 
 #include "mojo/public/cpp/bindings/lib/binding_state.h"
-
 #include "mojo/public/cpp/bindings/lib/task_runner_helper.h"
+#include "mojo/public/cpp/bindings/mojo_buildflags.h"
+
+#if BUILDFLAG(MOJO_RANDOM_DELAYS_ENABLED)
+#include "mojo/public/cpp/bindings/lib/test_random_mojo_delays.h"
+#endif
 
 namespace mojo {
 namespace internal {
@@ -41,6 +45,8 @@
   if (!router_)
     return;
 
+  weak_ptr_factory_.InvalidateWeakPtrs();
+
   endpoint_client_.reset();
   router_->CloseMessagePipe();
   router_ = nullptr;
@@ -92,6 +98,7 @@
 
   auto sequenced_runner =
       GetTaskRunnerToUseFromUserProvidedTaskRunner(std::move(runner));
+
   MultiplexRouter::Config config =
       passes_associated_kinds
           ? MultiplexRouter::MULTI_INTERFACE
@@ -106,7 +113,12 @@
       router_->CreateLocalEndpointHandle(kMasterInterfaceId), stub,
       std::move(request_validator), has_sync_methods,
       std::move(sequenced_runner), interface_version));
+
+#if BUILDFLAG(MOJO_RANDOM_DELAYS_ENABLED)
+  MakeBindingRandomlyPaused(base::SequencedTaskRunnerHandle::Get(),
+                            weak_ptr_factory_.GetWeakPtr());
+#endif
 }
 
-}  // namesapce internal
+}  // namespace internal
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
index d1c561c..42a8892 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.h
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -31,6 +31,7 @@
 #include "mojo/public/cpp/system/core.h"
 
 namespace mojo {
+
 namespace internal {
 
 class MOJO_CPP_BINDINGS_EXPORT BindingStateBase {
@@ -117,6 +118,7 @@
   }
 
   InterfaceRequest<Interface> Unbind() {
+    weak_ptr_factory_.InvalidateWeakPtrs();
     endpoint_client_.reset();
     InterfaceRequest<Interface> request(router_->PassMessagePipe());
     router_ = nullptr;
@@ -136,7 +138,7 @@
   DISALLOW_COPY_AND_ASSIGN(BindingState);
 };
 
-}  // namesapce internal
+}  // namespace internal
 }  // namespace mojo
 
 #endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index 155e1fe..d2ebebc 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -45,8 +45,7 @@
 // single message pipe.
 //
 // It is created on the sequence where the master interface of the message pipe
-// lives. Although it is ref-counted, it is guarateed to be destructed on the
-// same sequence.
+// lives.
 // Some public methods are only allowed to be called on the creating sequence;
 // while the others are safe to call from any sequence. Please see the method
 // comments for more details.
diff --git a/mojo/public/cpp/bindings/lib/test_random_mojo_delays.cc b/mojo/public/cpp/bindings/lib/test_random_mojo_delays.cc
new file mode 100644
index 0000000..861aa99
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/test_random_mojo_delays.cc
@@ -0,0 +1,226 @@
+// 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 <list>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/no_destructor.h"
+#include "base/rand_util.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/time/time.h"
+#include "mojo/public/cpp/bindings/lib/binding_state.h"
+#include "mojo/public/cpp/bindings/lib/test_random_mojo_delays.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+constexpr int kInverseProbabilityOfDelay = 8;
+constexpr int kInverseProbabilityOfNotResuming = 10;
+constexpr base::TimeDelta kMillisecondsToResume =
+    base::TimeDelta::FromMilliseconds(2);
+constexpr base::TimeDelta kPauseBindingsFrequency =
+    base::TimeDelta::FromMilliseconds(7);
+}  // namespace
+
+// TODO(mpdenton) This only adds random delays on method call processing. This
+// also should add random delays on response processing. It is a mistake if a
+// user assumes a response callback is received and run before a subsequent
+// asynch call (over a different message pipe), and these random delays won't
+// make it any more likely to find the mistake during testing.
+class RandomMojoDelays {
+ public:
+  RandomMojoDelays()
+      : runner_for_pauses_(
+            base::CreateSequencedTaskRunnerWithTraits(base::TaskTraits())) {
+    DETACH_FROM_SEQUENCE(runner_for_pauses_sequence_checker);
+  }
+
+  void Start() {
+    runner_for_pauses_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&RandomMojoDelays::PauseRandomBindingStateBases,
+                       base::Unretained(this)));
+  }
+
+  // TODO(mpdenton) what about bindings with associated interfaces? Apparently
+  // you cannot pause on those? May need to change DCHECK to if(...) return;
+  void AddBindingStateBase(scoped_refptr<base::SequencedTaskRunner> runner,
+                           base::WeakPtr<BindingStateBase> binding_state_base) {
+    runner_for_pauses_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&RandomMojoDelays::AddBindingStateBaseInternal,
+                       base::Unretained(this), std::move(runner),
+                       std::move(binding_state_base)));
+  }
+
+ private:
+  using BindingList = std::list<base::WeakPtr<BindingStateBase>>;
+
+  // Must be called on |runner_for_pauses_| sequence. Adds a BindingStateBase
+  // for random pausing purposes.
+  void AddBindingStateBaseInternal(
+      scoped_refptr<base::SequencedTaskRunner> runner,
+      base::WeakPtr<BindingStateBase> binding_state_base) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(runner_for_pauses_sequence_checker);
+
+    binding_state_base_map_[std::move(runner)].push_back(
+        std::move(binding_state_base));
+  }
+
+  // Adds a list of BindingStateBases to be randomly paused. Used to re-attach
+  // a list of BindingStateBases to the map after randomly pausing some of the
+  // bindings on their bound sequences.
+  void AddBindingStateBaseList(scoped_refptr<base::SequencedTaskRunner> runner,
+                               BindingList binding_state_bases) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(runner_for_pauses_sequence_checker);
+
+    BindingList& list = binding_state_base_map_[std::move(runner)];
+    list.splice(list.end(), std::move(binding_state_bases));
+  }
+
+  // Resumes all bindings in |paused_binding_state_bases|.
+  void ResumeFrozenBindingStateBasesOnTaskRunner(
+      BindingList binding_state_bases,
+      BindingList paused_binding_state_bases) {
+    auto it = paused_binding_state_bases.begin();
+    while (it != paused_binding_state_bases.end()) {
+      base::WeakPtr<BindingStateBase> wptr = *it;
+      if (!wptr) {
+        // This WeakPtr was invalidated. We'll delete it
+        // from the binding_state_bases list on the next PauseLoop.
+        it = paused_binding_state_bases.erase(it);
+        continue;
+      }
+      // Skip the resume with a 1/kInverseProbabilityOfNotResuming chance.
+      if (base::RandInt(1, kInverseProbabilityOfNotResuming) >= 2) {
+        wptr->ResumeIncomingMethodCallProcessing();
+        it = paused_binding_state_bases.erase(it);
+        continue;
+      }
+      it++;
+    }
+    if (!paused_binding_state_bases.empty()) {
+      // Because we haven't resumed all the bindings, we should schedule another
+      // resumption task in the future.
+      // TODO(mpdenton) similar problem as below: can freeze shutdown if we
+      // forget to unpause bindings.
+      base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(
+              &RandomMojoDelays::ResumeFrozenBindingStateBasesOnTaskRunner,
+              base::Unretained(this), std::move(binding_state_bases),
+              std::move(paused_binding_state_bases)),
+          kMillisecondsToResume);
+      return;
+    }
+    // Re-attach the bindings to the global map for future pausing.
+    runner_for_pauses_->PostTask(
+        FROM_HERE, base::BindOnce(&RandomMojoDelays::AddBindingStateBaseList,
+                                  base::Unretained(this),
+                                  base::SequencedTaskRunnerHandle::Get(),
+                                  std::move(binding_state_bases)));
+  }
+
+  // Pause a random selection of bindings in the list |binding_state_bases|,
+  // and set them to resume in the future.
+  void PauseRandomBindingStateBasesOnTaskRunner(
+      BindingList binding_state_bases) {
+    BindingList paused_binding_state_bases;
+    auto it = binding_state_bases.begin();
+    while (it != binding_state_bases.end()) {
+      // Remove any BindingStateBases that have been destroyed already.
+      base::WeakPtr<BindingStateBase> wptr = *it;
+      if (!wptr) {
+        it = binding_state_bases.erase(it);
+        continue;
+      }
+      if (base::RandInt(1, kInverseProbabilityOfDelay) >= 2) {
+        it++;
+        continue;
+      }
+      wptr->PauseIncomingMethodCallProcessing();
+      paused_binding_state_bases.push_back(wptr);
+      it++;
+    }
+    // Set the bindings to resume soon.
+    // TODO(mpdenton) may cause deadlock on shutdown if this doesn't run. But
+    // there is no PostDelayedTaskWithTraits for a SequencedTaskRunner.
+    if (paused_binding_state_bases.size() > 0) {
+      base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(
+              &RandomMojoDelays::ResumeFrozenBindingStateBasesOnTaskRunner,
+              base::Unretained(this), std::move(binding_state_bases),
+              std::move(paused_binding_state_bases)),
+          kMillisecondsToResume);
+    } else if (binding_state_bases.size() > 0) {
+      // If we did not pause any bindings, re-attach the bindings to the global
+      // map for future pausing, if there are any left after deleting all the
+      // invalidated weak ptrs.
+      runner_for_pauses_->PostTask(
+          FROM_HERE, base::BindOnce(&RandomMojoDelays::AddBindingStateBaseList,
+                                    base::Unretained(this),
+                                    base::SequencedTaskRunnerHandle::Get(),
+                                    std::move(binding_state_bases)));
+    }
+  }
+
+  // Post tasks to every sequence with bound Mojo bindings, telling each to
+  // pause random Mojo bindings bound on the respective sequence.
+  void PauseRandomBindingStateBases() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(runner_for_pauses_sequence_checker);
+
+    auto map_it = binding_state_base_map_.begin();
+    while (map_it != binding_state_base_map_.end()) {
+      // Tell sequence to randomly pause some of its bindings.
+      map_it->first->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              &RandomMojoDelays::PauseRandomBindingStateBasesOnTaskRunner,
+              base::Unretained(this), std::move(map_it->second)));
+      // Erase the current key-value pair (it will be re-added if necessary
+      // after resuming the bindings--for now, drop the reference to the
+      // SequencedTaskRunner).
+      map_it = binding_state_base_map_.erase(map_it);
+    }
+    // Post delayed task, instead of using a RepeatingTimer, to avoid
+    // overwhelming the task scheduling.
+    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&RandomMojoDelays::PauseRandomBindingStateBases,
+                       base::Unretained(this)),
+        kPauseBindingsFrequency);
+  }
+
+  scoped_refptr<base::SequencedTaskRunner> runner_for_pauses_;
+  std::map<scoped_refptr<base::SequencedTaskRunner>, BindingList>
+      binding_state_base_map_;
+  SEQUENCE_CHECKER(runner_for_pauses_sequence_checker);
+};
+
+RandomMojoDelays& GetRandomMojoDelays() {
+  static base::NoDestructor<RandomMojoDelays> random_mojo_delays;
+  return *random_mojo_delays;
+}
+
+void MakeBindingRandomlyPaused(
+    scoped_refptr<base::SequencedTaskRunner> runner,
+    base::WeakPtr<BindingStateBase> binding_state_base) {
+  GetRandomMojoDelays().AddBindingStateBase(std::move(runner),
+                                            binding_state_base);
+}
+
+}  // namespace internal
+
+void BeginRandomMojoDelays() {
+  internal::GetRandomMojoDelays().Start();
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/test_random_mojo_delays.h b/mojo/public/cpp/bindings/lib/test_random_mojo_delays.h
new file mode 100644
index 0000000..87a0e0d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/test_random_mojo_delays.h
@@ -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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_TEST_RANDOM_MOJO_DELAYS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_TEST_RANDOM_MOJO_DELAYS_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "mojo/public/cpp/bindings/bindings_export.h"
+
+namespace mojo {
+// Begins issuing temporary pauses on randomly selected Mojo bindings for
+// debugging purposes.
+MOJO_CPP_BINDINGS_EXPORT void BeginRandomMojoDelays();
+namespace internal {
+class BindingStateBase;
+// Adds a binding state base to make it eligible for pausing.
+MOJO_CPP_BINDINGS_EXPORT void MakeBindingRandomlyPaused(
+    scoped_refptr<base::SequencedTaskRunner>,
+    base::WeakPtr<BindingStateBase>);
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEST_RANDOM_MOJO_DELAYS_H_
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index f50370c..1071530 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -32,6 +32,7 @@
   "//services/audio/public/cpp/typemaps.gni",
   "//services/device/public/mojom/typemaps.gni",
   "//services/identity/public/cpp/typemaps.gni",
+  "//services/media_session/public/cpp/typemaps.gni",
   "//services/network/public/cpp/typemaps.gni",
   "//services/preferences/public/cpp/typemaps.gni",
   "//services/proxy_resolver/public/cpp/typemaps.gni",
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 250423f..f335159 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1402,6 +1402,7 @@
       "third_party/quic/core/http/spdy_utils.cc",
       "third_party/quic/core/http/spdy_utils.h",
       "third_party/quic/core/packet_number_indexed_queue.h",
+      "third_party/quic/core/qpack/qpack_constants.cc",
       "third_party/quic/core/qpack/qpack_constants.h",
       "third_party/quic/core/qpack/qpack_decoder.cc",
       "third_party/quic/core/qpack/qpack_decoder.h",
@@ -1413,6 +1414,8 @@
       "third_party/quic/core/qpack/qpack_encoder_stream_sender.h",
       "third_party/quic/core/qpack/qpack_header_table.cc",
       "third_party/quic/core/qpack/qpack_header_table.h",
+      "third_party/quic/core/qpack/qpack_instruction_decoder.cc",
+      "third_party/quic/core/qpack/qpack_instruction_decoder.h",
       "third_party/quic/core/qpack/qpack_static_table.cc",
       "third_party/quic/core/qpack/qpack_static_table.h",
       "third_party/quic/core/quic_ack_listener_interface.cc",
@@ -5079,6 +5082,7 @@
     "third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc",
     "third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc",
     "third_party/quic/core/qpack/qpack_encoder_test.cc",
+    "third_party/quic/core/qpack/qpack_instruction_decoder_test.cc",
     "third_party/quic/core/qpack/qpack_static_table_test.cc",
     "third_party/quic/core/quic_alarm_test.cc",
     "third_party/quic/core/quic_arena_scoped_ptr_test.cc",
diff --git a/net/cert/cert_database_mac.cc b/net/cert/cert_database_mac.cc
index fa1e99a..e37e5e6 100644
--- a/net/cert/cert_database_mac.cc
+++ b/net/cert/cert_database_mac.cc
@@ -35,7 +35,7 @@
         registered_(false),
         called_shutdown_(false) {
     // Ensure an associated CFRunLoop.
-    DCHECK(base::MessageLoopForUI::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForUI::IsSet());
     DCHECK(task_runner_->BelongsToCurrentThread());
     task_runner_->PostTask(FROM_HERE,
                            base::Bind(&Notifier::Init,
diff --git a/net/cert_net/nss_ocsp.cc b/net/cert_net/nss_ocsp.cc
index 7bb72b4..cc3ef8b 100644
--- a/net/cert_net/nss_ocsp.cc
+++ b/net/cert_net/nss_ocsp.cc
@@ -70,7 +70,7 @@
 
   void StartUsing() {
     base::AutoLock autolock(lock_);
-    DCHECK(base::MessageLoopForIO::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForIO::IsSet());
     io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
   }
 
@@ -370,7 +370,7 @@
     {
       base::AutoLock autolock(lock_);
       DCHECK(!io_task_runner_);
-      DCHECK(base::MessageLoopForIO::IsCurrent());
+      DCHECK(base::MessageLoopCurrentForIO::IsSet());
       io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
       g_ocsp_io_loop.Get().AddRequest(this);
     }
diff --git a/net/dns/dns_reloader.cc b/net/dns/dns_reloader.cc
index ad96f68..e054568 100644
--- a/net/dns/dns_reloader.cc
+++ b/net/dns/dns_reloader.cc
@@ -49,7 +49,7 @@
 
   // NetworkChangeNotifier::DNSObserver:
   void OnDNSChanged() override {
-    DCHECK(base::MessageLoopForIO::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForIO::IsSet());
     base::AutoLock lock(lock_);
     resolver_generation_++;
   }
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index fcb3b0c..95331e3c 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -174,6 +174,11 @@
   return HandleConnectResult(result);
 }
 
+void HttpProxyConnectJob::ChangePriorityInternal(RequestPriority priority) {
+  if (client_socket_)
+    client_socket_->SetPriority(priority);
+}
+
 void HttpProxyConnectJob::OnConnectComplete(int result) {
   DCHECK_NE(ERR_IO_PENDING, result);
   result = HandleConnectResult(result);
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
index 5938c20..90f49f5 100644
--- a/net/http/http_proxy_client_socket_pool.h
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -139,6 +139,8 @@
   // a standard net error code will be returned.
   int ConnectInternal() override;
 
+  void ChangePriorityInternal(RequestPriority priority) override;
+
   void OnConnectComplete(int result);
 
   int HandleConnectResult(int result);
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index f5b5368d4..edc4273 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -222,6 +222,10 @@
     return transport_socket_pool_.last_request_priority();
   }
 
+  RequestPriority GetTransportRequestPriority(size_t index) const {
+    return transport_socket_pool_.requests()[index]->priority();
+  }
+
   const base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
   TestNetworkQualityEstimator* estimator() { return &estimator_; }
@@ -293,6 +297,26 @@
                              CompletionOnceCallback(), pool_.get(),
                              NetLogWithSource()));
   EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority());
+  EXPECT_EQ(HIGHEST, GetTransportRequestPriority(0));
+}
+
+TEST_P(HttpProxyClientSocketPoolTest, SetPriority) {
+  data_ = std::make_unique<SequencedSocketData>();
+  data_->set_connect_data(MockConnect(ASYNC, OK));
+
+  socket_factory()->AddSocketDataProvider(data_.get());
+
+  int rv = handle_.Init("a", CreateTunnelParams(), LOW, SocketTag(),
+                        ClientSocketPool::RespectLimits::ENABLED,
+                        callback_.callback(), pool_.get(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+  EXPECT_FALSE(handle_.is_initialized());
+  EXPECT_FALSE(handle_.socket());
+
+  EXPECT_EQ(LOW, GetTransportRequestPriority(0));
+
+  handle_.SetPriority(HIGHEST);
+  EXPECT_EQ(HIGHEST, GetTransportRequestPriority(0));
 }
 
 TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) {
@@ -425,15 +449,14 @@
 }
 
 // Make sure that HttpProxyConnectJob passes on its priority to its
-// SPDY session's socket request on Init (if applicable).
-TEST_P(HttpProxyClientSocketPoolTest,
-       SetSpdySessionSocketRequestPriorityOnInit) {
+// SPDY session's socket request on Init, and on SetPriority.
+TEST_P(HttpProxyClientSocketPoolTest, SetSpdySessionSocketRequestPriority) {
   if (GetParam() != SPDY)
     return;
 
-  spdy::SpdySerializedFrame req(
-      spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, MEDIUM,
-                                      HostPortPair("www.google.com", 443)));
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
+      kAuthHeaders, kAuthHeadersSize, 1, HIGHEST,
+      HostPortPair("www.google.com", 443)));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)};
   spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead spdy_reads[] = {CreateMockRead(resp, 1, ASYNC),
@@ -449,7 +472,10 @@
                    ClientSocketPool::RespectLimits::ENABLED,
                    callback_.callback(), pool_.get(), NetLogWithSource()));
   EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority());
+  EXPECT_EQ(MEDIUM, GetTransportRequestPriority(0));
 
+  handle_.SetPriority(HIGHEST);
+  // Expect frame with HIGHEST priority, not MEDIUM.
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
 }
 
diff --git a/net/http/http_proxy_client_socket_wrapper.cc b/net/http/http_proxy_client_socket_wrapper.cc
index c62ec6fb..74bea38 100644
--- a/net/http/http_proxy_client_socket_wrapper.cc
+++ b/net/http/http_proxy_client_socket_wrapper.cc
@@ -73,7 +73,7 @@
       tunnel_(tunnel),
       using_spdy_(false),
       is_trusted_proxy_(is_trusted_proxy),
-      quic_stream_request_(quic_stream_factory),
+      quic_stream_factory_(quic_stream_factory),
       http_auth_controller_(
           tunnel ? new HttpAuthController(
                        HttpAuth::AUTH_PROXY,
@@ -136,6 +136,27 @@
   return std::move(error_response_info_);
 }
 
+void HttpProxyClientSocketWrapper::SetPriority(RequestPriority priority) {
+  if (respect_limits_ == ClientSocketPool::RespectLimits::DISABLED) {
+    DCHECK_EQ(MAXIMUM_PRIORITY, priority_);
+    return;
+  }
+
+  priority_ = priority;
+
+  if (transport_socket_handle_)
+    transport_socket_handle_->SetPriority(priority);
+
+  if (spdy_stream_request_)
+    spdy_stream_request_->SetPriority(priority);
+
+  if (quic_stream_request_)
+    quic_stream_request_->SetPriority(priority);
+
+  if (transport_socket_)
+    transport_socket_->SetStreamPriority(priority);
+}
+
 const HttpResponseInfo* HttpProxyClientSocketWrapper::GetConnectResponseInfo()
     const {
   if (transport_socket_)
@@ -203,7 +224,8 @@
   connect_callback_.Reset();
   connect_timer_.Stop();
   next_state_ = STATE_NONE;
-  spdy_stream_request_.CancelRequest();
+  spdy_stream_request_.reset();
+  quic_stream_request_.reset();
   if (transport_socket_handle_) {
     if (transport_socket_handle_->socket())
       transport_socket_handle_->socket()->Disconnect();
@@ -615,7 +637,8 @@
   }
 
   next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
-  return spdy_stream_request_.StartRequest(
+  spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
+  return spdy_stream_request_->StartRequest(
       SPDY_BIDIRECTIONAL_STREAM, spdy_session,
       GURL("https://" + endpoint_.ToString()), priority_, initial_socket_tag_,
       spdy_session->net_log(),
@@ -625,11 +648,14 @@
 }
 
 int HttpProxyClientSocketWrapper::DoSpdyProxyCreateStreamComplete(int result) {
-  if (result < 0)
+  if (result < 0) {
+    spdy_stream_request_.reset();
     return result;
+  }
 
   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
-  base::WeakPtr<SpdyStream> stream = spdy_stream_request_.ReleaseStream();
+  base::WeakPtr<SpdyStream> stream = spdy_stream_request_->ReleaseStream();
+  spdy_stream_request_.reset();
   DCHECK(stream.get());
   // |transport_socket_| will set itself as |stream|'s delegate.
   transport_socket_.reset(new SpdyProxyClientSocket(
@@ -644,7 +670,9 @@
   next_state_ = STATE_QUIC_PROXY_CREATE_STREAM;
   const HostPortPair& proxy_server =
       ssl_params_->GetDirectConnectionParams()->destination().host_port_pair();
-  return quic_stream_request_.Request(
+  quic_stream_request_ =
+      std::make_unique<QuicStreamRequest>(quic_stream_factory_);
+  return quic_stream_request_->Request(
       proxy_server, quic_version_, ssl_params_->privacy_mode(), priority_,
       initial_socket_tag_, ssl_params_->ssl_config().GetCertVerifyFlags(),
       GURL("https://" + proxy_server.ToString()), net_log_,
@@ -655,11 +683,15 @@
 }
 
 int HttpProxyClientSocketWrapper::DoQuicProxyCreateStream(int result) {
-  if (result < 0)
+  if (result < 0) {
+    quic_stream_request_.reset();
     return result;
+  }
 
   next_state_ = STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE;
-  quic_session_ = quic_stream_request_.ReleaseSessionHandle();
+  quic_session_ = quic_stream_request_->ReleaseSessionHandle();
+  quic_stream_request_.reset();
+
   return quic_session_->RequestStream(
       false,
       base::Bind(&HttpProxyClientSocketWrapper::OnIOComplete,
diff --git a/net/http/http_proxy_client_socket_wrapper.h b/net/http/http_proxy_client_socket_wrapper.h
index ab8a1e7..8ddfcb84 100644
--- a/net/http/http_proxy_client_socket_wrapper.h
+++ b/net/http/http_proxy_client_socket_wrapper.h
@@ -87,6 +87,8 @@
 
   std::unique_ptr<HttpResponseInfo> GetAdditionalErrorState();
 
+  void SetPriority(RequestPriority priority);
+
   // ProxyClientSocket implementation.
   const HttpResponseInfo* GetConnectResponseInfo() const override;
   std::unique_ptr<HttpStream> CreateConnectResponseStream() override;
@@ -218,9 +220,10 @@
   // if necessary.
   CompletionOnceCallback connect_callback_;
 
-  SpdyStreamRequest spdy_stream_request_;
+  std::unique_ptr<SpdyStreamRequest> spdy_stream_request_;
 
-  QuicStreamRequest quic_stream_request_;
+  QuicStreamFactory* const quic_stream_factory_;
+  std::unique_ptr<QuicStreamRequest> quic_stream_request_;
   std::unique_ptr<QuicChromiumClientSession::Handle> quic_session_;
 
   scoped_refptr<HttpAuthController> http_auth_controller_;
diff --git a/net/http/http_proxy_client_socket_wrapper_unittest.cc b/net/http/http_proxy_client_socket_wrapper_unittest.cc
index e57c271..4ef82b3 100644
--- a/net/http/http_proxy_client_socket_wrapper_unittest.cc
+++ b/net/http/http_proxy_client_socket_wrapper_unittest.cc
@@ -162,13 +162,14 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructConnectRequestPacket(
-      quic::QuicPacketNumber packet_number) {
+      quic::QuicPacketNumber packet_number,
+      RequestPriority priority) {
     spdy::SpdyHeaderBlock block;
     PopulateConnectRequestIR(&block);
     return client_maker_.MakeRequestHeadersPacket(
         packet_number, client_data_stream_id1_, kIncludeVersion, !kFin,
-        ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY),
-        std::move(block), 0, nullptr, &header_stream_offset_);
+        ConvertRequestPriorityToQuicPriority(priority), std::move(block), 0,
+        nullptr, &header_stream_offset_);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructServerConnectReplyPacket(
@@ -258,7 +259,8 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructSettingsPacket(1));
-  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructConnectRequestPacket(2));
+  mock_quic_data_.AddWrite(SYNCHRONOUS,
+                           ConstructConnectRequestPacket(2, HIGHEST));
   mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
   mock_quic_data_.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   mock_quic_data_.AddWrite(
@@ -283,7 +285,7 @@
   client_socket_wrapper_.reset(new HttpProxyClientSocketWrapper(
       /*group_name=*/std::string(), /*requiest_priority=*/DEFAULT_PRIORITY,
       /*socket_tag=*/SocketTag(),
-      /*respect_limits=*/ClientSocketPool::RespectLimits::DISABLED,
+      /*respect_limits=*/ClientSocketPool::RespectLimits::ENABLED,
       /*connect_timeout_duration=*/base::TimeDelta::FromHours(1),
       /*proxy_negotiation_timeout_duration=*/base::TimeDelta::FromHours(1),
       /*transport_pool=*/nullptr, /*ssl_pool=*/nullptr,
@@ -295,7 +297,11 @@
 
   TestCompletionCallback callback;
   client_socket_wrapper_->Connect(callback.callback());
+  EXPECT_EQ(DEFAULT_PRIORITY, host_resolver_.request_priority(1));
 
+  client_socket_wrapper_->SetPriority(HIGHEST);
+
+  // Expect connect request packet with HIGHEST priority, not DEFAULT_PRIORITY.
   EXPECT_THAT(callback.WaitForResult(), IsOk());
 
   client_socket_wrapper_.reset();
@@ -311,7 +317,8 @@
   crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
 
   mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructSettingsPacket(1));
-  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructConnectRequestPacket(2));
+  mock_quic_data_.AddWrite(SYNCHRONOUS,
+                           ConstructConnectRequestPacket(2, DEFAULT_PRIORITY));
   mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
   mock_quic_data_.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   mock_quic_data_.AddWrite(
@@ -337,7 +344,7 @@
   client_socket_wrapper_.reset(new HttpProxyClientSocketWrapper(
       /*group_name=*/std::string(), /*requiest_priority=*/DEFAULT_PRIORITY,
       /*socket_tag=*/tag,
-      /*respect_limits=*/ClientSocketPool::RespectLimits::DISABLED,
+      /*respect_limits=*/ClientSocketPool::RespectLimits::ENABLED,
       /*connect_timeout_duration=*/base::TimeDelta::FromHours(1),
       /*proxy_negotiation_timeout_duration=*/base::TimeDelta::FromHours(1),
       /*transport_pool=*/nullptr, /*ssl_pool=*/nullptr,
diff --git a/net/http/proxy_client_socket.cc b/net/http/proxy_client_socket.cc
index fe23978..e646fe1e 100644
--- a/net/http/proxy_client_socket.cc
+++ b/net/http/proxy_client_socket.cc
@@ -19,6 +19,8 @@
 
 namespace net {
 
+void ProxyClientSocket::SetStreamPriority(RequestPriority priority) {}
+
 // static
 void ProxyClientSocket::BuildTunnelRequest(
     const HostPortPair& endpoint,
diff --git a/net/http/proxy_client_socket.h b/net/http/proxy_client_socket.h
index 046ea6c7..639ac30 100644
--- a/net/http/proxy_client_socket.h
+++ b/net/http/proxy_client_socket.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/net_export.h"
+#include "net/base/request_priority.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/stream_socket.h"
 
@@ -58,6 +59,9 @@
   // Returns the protocol negotiated with the proxy.
   virtual NextProto GetProxyNegotiatedProtocol() const = 0;
 
+  // Set the priority of the underlying stream (for SPDY and QUIC)
+  virtual void SetStreamPriority(RequestPriority priority);
+
  protected:
   // The HTTP CONNECT method for establishing a tunnel connection is documented
   // in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2817, Sections 5.2
diff --git a/net/http/transport_security_persister_unittest.cc b/net/http/transport_security_persister_unittest.cc
index b5392b9..e74c841 100644
--- a/net/http/transport_security_persister_unittest.cc
+++ b/net/http/transport_security_persister_unittest.cc
@@ -31,13 +31,13 @@
   TransportSecurityPersisterTest() = default;
 
   ~TransportSecurityPersisterTest() override {
-    EXPECT_TRUE(base::MessageLoopForIO::IsCurrent());
+    EXPECT_TRUE(base::MessageLoopCurrentForIO::IsSet());
     base::RunLoop().RunUntilIdle();
   }
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    ASSERT_TRUE(base::MessageLoopForIO::IsCurrent());
+    ASSERT_TRUE(base::MessageLoopCurrentForIO::IsSet());
     persister_ = std::make_unique<TransportSecurityPersister>(
         &state_, temp_dir_.GetPath(), base::ThreadTaskRunnerHandle::Get());
   }
diff --git a/net/quic/quic_chromium_client_stream.cc b/net/quic/quic_chromium_client_stream.cc
index fd52cc92..6433249b 100644
--- a/net/quic/quic_chromium_client_stream.cc
+++ b/net/quic/quic_chromium_client_stream.cc
@@ -480,7 +480,7 @@
   session_->HandlePromised(id(), promised_id, promise_headers);
 }
 
-void QuicChromiumClientStream::OnDataAvailable() {
+void QuicChromiumClientStream::OnBodyAvailable() {
   if (!FinishedReadingHeaders() || !headers_delivered_) {
     // Buffer the data in the sequencer until the headers have been read.
     return;
diff --git a/net/quic/quic_chromium_client_stream.h b/net/quic/quic_chromium_client_stream.h
index e0019e4..5b1ad12 100644
--- a/net/quic/quic_chromium_client_stream.h
+++ b/net/quic/quic_chromium_client_stream.h
@@ -218,7 +218,7 @@
   void OnPromiseHeaderList(quic::QuicStreamId promised_id,
                            size_t frame_len,
                            const quic::QuicHeaderList& header_list) override;
-  void OnDataAvailable() override;
+  void OnBodyAvailable() override;
   void OnClose() override;
   void OnCanWrite() override;
   size_t WriteHeaders(
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 653a291..2edd5c1 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -219,9 +219,6 @@
           FLAGS_quic_reloadable_flag_quic_fix_has_pending_crypto_data,
           true)
 
-// This flag fixes a bug where a zombie stream cannot be correctly reset.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_reset_zombie_streams, true)
-
 // When true, fix initialization and updating of
 // |time_of_first_packet_sent_after_receiving_| in QuicConnection.
 QUIC_FLAG(
@@ -229,20 +226,10 @@
     FLAGS_quic_reloadable_flag_quic_fix_time_of_first_packet_sent_after_receiving,
     true)
 
-// If true, deprecate PostProcessAfterData from QuicConnection. This is used to
-// fix a bug where window update causes session to write data.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_deprecate_post_process_after_data,
-          true)
-
 // When the STMP connection option is sent by the client, timestamps in the QUIC
 // ACK frame are sent and processed.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
 
-// When true, don't arm the path degrading alarm on the server side and stop
-// using HasUnackedPackets to decide when to arm it.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_path_degrading_alarm, true)
-
 // When true, QUIC server push uses a unidirectional stream.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_unidirectional_server_push_stream,
@@ -252,7 +239,7 @@
 // a new decryption key is made available.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_decrypt_packets_on_key_change,
-          false)
+          true)
 
 // This flag fixes a bug where dispatcher's last_packet_is_ietf_quic may be
 // wrong when getting proof asynchronously.
@@ -316,3 +303,18 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_bbr_startup_rate_reduction,
           false)
+
+// If true, enable the fix for the bug where v44 packets are rejected
+// by a lower-version connection close.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_fix_reject_by_session_type,
+          true)
+
+// If true, only send version negotiation packets when they are at least
+// 1200 bytes.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_limit_version_negotiation,
+          false)
+
+// If true, disables key share caching for QUIC key exchange
+QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_no_ephemeral_key_source, false)
diff --git a/net/quic/quic_proxy_client_socket.cc b/net/quic/quic_proxy_client_socket.cc
index ebcb314..de4c869 100644
--- a/net/quic/quic_proxy_client_socket.cc
+++ b/net/quic/quic_proxy_client_socket.cc
@@ -86,6 +86,10 @@
   return kProtoQUIC;
 }
 
+void QuicProxyClientSocket::SetStreamPriority(RequestPriority priority) {
+  stream_->SetPriority(ConvertRequestPriorityToSpdyPriority(priority));
+}
+
 // Sends a HEADERS frame to the proxy with a CONNECT request
 // for the specified endpoint.  Waits for the server to send back
 // a HEADERS frame.  OK will be returned if the status is 200.
diff --git a/net/quic/quic_proxy_client_socket.h b/net/quic/quic_proxy_client_socket.h
index 45d15fcb..b9688ec 100644
--- a/net/quic/quic_proxy_client_socket.h
+++ b/net/quic/quic_proxy_client_socket.h
@@ -47,6 +47,7 @@
   int RestartWithAuth(CompletionOnceCallback callback) override;
   bool IsUsingSpdy() const override;
   NextProto GetProxyNegotiatedProtocol() const override;
+  void SetStreamPriority(RequestPriority priority) override;
 
   // StreamSocket implementation.
   int Connect(CompletionOnceCallback callback) override;
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index bdcc92a..7c741911 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -299,13 +299,14 @@
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructConnectRequestPacket(
-      quic::QuicPacketNumber packet_number) {
+      quic::QuicPacketNumber packet_number,
+      RequestPriority request_priority = LOWEST) {
     spdy::SpdyHeaderBlock block;
     PopulateConnectRequestIR(&block);
     return client_maker_.MakeRequestHeadersPacket(
         packet_number, client_data_stream_id1_, kIncludeVersion, !kFin,
-        ConvertRequestPriorityToQuicPriority(LOWEST), std::move(block), 0,
-        nullptr, &header_stream_offset_);
+        ConvertRequestPriorityToQuicPriority(request_priority),
+        std::move(block), 0, nullptr, &header_stream_offset_);
   }
 
   std::unique_ptr<quic::QuicReceivedPacket> ConstructConnectAuthRequestPacket(
@@ -769,7 +770,21 @@
   EXPECT_EQ((int64_t)kLen333, sock_->GetTotalReceivedBytes());
 }
 
-// ----------- Write
+TEST_P(QuicProxyClientSocketTest, SetStreamPriority) {
+  mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructSettingsPacket(1));
+  mock_quic_data_.AddWrite(SYNCHRONOUS,
+                           ConstructConnectRequestPacket(2, HIGHEST));
+  mock_quic_data_.AddRead(ASYNC, ConstructServerConnectReplyPacket(1, !kFin));
+  mock_quic_data_.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  mock_quic_data_.AddWrite(
+      SYNCHRONOUS,
+      ConstructAckAndRstPacket(3, quic::QUIC_STREAM_CANCELLED, 1, 1, 1));
+
+  Initialize();
+
+  sock_->SetStreamPriority(HIGHEST);
+  AssertConnectSucceeds();
+}
 
 TEST_P(QuicProxyClientSocketTest, WriteSendsDataInDataFrame) {
   mock_quic_data_.AddWrite(SYNCHRONOUS, ConstructSettingsPacket(1));
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 3d411a3f..7c817c5 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -372,10 +372,24 @@
     stream_requests_.erase(request_iter);
   }
 
+  void SetPriority(RequestPriority priority) {
+    if (priority_ == priority)
+      return;
+
+    priority_ = priority;
+    if (io_state_ == STATE_RESOLVE_HOST_COMPLETE &&
+        !host_resolution_finished_) {
+      DCHECK(request_);
+      request_->ChangeRequestPriority(priority);
+    }
+  }
+
   const std::set<QuicStreamRequest*>& stream_requests() {
     return stream_requests_;
   }
 
+  RequestPriority priority() const { return priority_; }
+
   bool IsHostResolutionComplete() const { return host_resolution_finished_; }
 
  private:
@@ -418,7 +432,7 @@
   HostResolver* host_resolver_;
   std::unique_ptr<HostResolver::Request> request_;
   const QuicSessionAliasKey key_;
-  const RequestPriority priority_;
+  RequestPriority priority_;
   const int cert_verify_flags_;
   const bool was_alternative_service_recently_broken_;
   const bool retry_on_alternate_network_before_handshake_;
@@ -924,6 +938,11 @@
   return factory_->GetTimeDelayForWaitingJob(session_key_.server_id());
 }
 
+void QuicStreamRequest::SetPriority(RequestPriority priority) {
+  if (factory_)
+    factory_->SetRequestPriority(this, priority);
+}
+
 std::unique_ptr<QuicChromiumClientSession::Handle>
 QuicStreamRequest::ReleaseSessionHandle() {
   if (!session_ || !session_->IsConnected())
@@ -1417,6 +1436,14 @@
   job_iter->second->RemoveRequest(request);
 }
 
+void QuicStreamFactory::SetRequestPriority(QuicStreamRequest* request,
+                                           RequestPriority priority) {
+  auto job_iter = active_jobs_.find(request->session_key());
+  if (job_iter == active_jobs_.end())
+    return;
+  job_iter->second->SetPriority(priority);
+}
+
 void QuicStreamFactory::CloseAllSessions(int error,
                                          quic::QuicErrorCode quic_error) {
   base::UmaHistogramSparse("Net.QuicSession.CloseAllSessionsError", -error);
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index ec06a48..6dcb932 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -153,6 +153,10 @@
   // returns the amount of time waiting job should be delayed.
   base::TimeDelta GetTimeDelayForWaitingJob() const;
 
+  // If host resolution is underway, changes the priority of the host resolver
+  // request.
+  void SetPriority(RequestPriority priority);
+
   // Releases the handle to the QUIC session retrieved as a result of Request().
   std::unique_ptr<QuicChromiumClientSession::Handle> ReleaseSessionHandle();
 
@@ -295,6 +299,9 @@
   // Cancels a pending request.
   void CancelRequest(QuicStreamRequest* request);
 
+  // Sets priority of a request.
+  void SetRequestPriority(QuicStreamRequest* request, RequestPriority priority);
+
   // Closes all current sessions with specified network, QUIC error codes.
   // It sends connection close packet when closing connections.
   void CloseAllSessions(int error, quic::QuicErrorCode quic_error);
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 28d3611..04de076 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -8411,6 +8411,42 @@
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
 }
 
+TEST_P(QuicStreamFactoryTest, HostResolverRequestReprioritizedOnSetPriority) {
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  MockQuicData socket_data;
+  socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                host_port_pair_, version_, privacy_mode_, MAXIMUM_PRIORITY,
+                SocketTag(),
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+
+  EXPECT_EQ(MAXIMUM_PRIORITY, host_resolver_->last_request_priority());
+  EXPECT_EQ(MAXIMUM_PRIORITY, host_resolver_->request_priority(1));
+
+  QuicStreamRequest request2(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request2.Request(
+                host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+                SocketTag(),
+                /*cert_verify_flags=*/0, url2_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_EQ(DEFAULT_PRIORITY, host_resolver_->last_request_priority());
+  EXPECT_EQ(DEFAULT_PRIORITY, host_resolver_->request_priority(2));
+
+  request.SetPriority(LOWEST);
+  EXPECT_EQ(LOWEST, host_resolver_->request_priority(1));
+  EXPECT_EQ(DEFAULT_PRIORITY, host_resolver_->request_priority(2));
+}
+
 // Passes |max_time_before_crypto_handshake_seconds| and
 // |max_idle_time_before_crypto_handshake_seconds| to QuicStreamFactory, then
 // checks that its internal quic::QuicConfig is correct.
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index 8631afd..724bbd8 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -93,6 +93,10 @@
   return spdy_stream_->GetNegotiatedProtocol();
 }
 
+void SpdyProxyClientSocket::SetStreamPriority(RequestPriority priority) {
+  spdy_stream_->SetPriority(priority);
+}
+
 std::unique_ptr<HttpStream>
 SpdyProxyClientSocket::CreateConnectResponseStream() {
   return std::make_unique<ProxyConnectRedirectHttpStream>(
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
index 6aef81f45..98948d5e 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -63,6 +63,7 @@
   int RestartWithAuth(CompletionOnceCallback callback) override;
   bool IsUsingSpdy() const override;
   NextProto GetProxyNegotiatedProtocol() const override;
+  void SetStreamPriority(RequestPriority priority) override;
 
   // StreamSocket implementation.
   int Connect(CompletionOnceCallback callback) override;
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index a1326fc..5354c26 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -90,7 +90,8 @@
                   base::span<const MockWrite> writes);
   void PopulateConnectRequestIR(spdy::SpdyHeaderBlock* syn_ir);
   void PopulateConnectReplyIR(spdy::SpdyHeaderBlock* block, const char* status);
-  spdy::SpdySerializedFrame ConstructConnectRequestFrame();
+  spdy::SpdySerializedFrame ConstructConnectRequestFrame(
+      RequestPriority priority);
   spdy::SpdySerializedFrame ConstructConnectAuthRequestFrame();
   spdy::SpdySerializedFrame ConstructConnectReplyFrame();
   spdy::SpdySerializedFrame ConstructConnectAuthReplyFrame();
@@ -353,10 +354,11 @@
 
 // Constructs a standard SPDY HEADERS frame for a CONNECT request.
 spdy::SpdySerializedFrame
-SpdyProxyClientSocketTest::ConstructConnectRequestFrame() {
+SpdyProxyClientSocketTest::ConstructConnectRequestFrame(
+    RequestPriority priority = LOWEST) {
   spdy::SpdyHeaderBlock block;
   PopulateConnectRequestIR(&block);
-  return spdy_util_.ConstructSpdyHeaders(kStreamId, std::move(block), LOWEST,
+  return spdy_util_.ConstructSpdyHeaders(kStreamId, std::move(block), priority,
                                          false);
 }
 
@@ -533,6 +535,25 @@
   ASSERT_FALSE(sock_->IsConnected());
 }
 
+TEST_P(SpdyProxyClientSocketTest, SetStreamPriority) {
+  spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame(HIGHEST));
+  MockWrite writes[] = {
+      CreateMockWrite(conn, 0, SYNCHRONOUS),
+  };
+
+  spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame());
+  MockRead reads[] = {
+      CreateMockRead(resp, 1, ASYNC),
+      MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2),
+  };
+
+  Initialize(reads, writes);
+
+  sock_->SetStreamPriority(HIGHEST);
+
+  AssertConnectSucceeds();
+}
+
 // ----------- WasEverUsed
 
 TEST_P(SpdyProxyClientSocketTest, WasEverUsedReturnsCorrectValues) {
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index f0bee16..14e433b 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -715,6 +715,18 @@
   return base::trace_event::EstimateItemMemoryUsage(url_);
 }
 
+void SpdyStreamRequest::SetPriority(RequestPriority priority) {
+  if (priority_ == priority)
+    return;
+
+  if (stream_)
+    stream_->SetPriority(priority);
+  if (session_)
+    session_->ChangeStreamRequestPriority(weak_ptr_factory_.GetWeakPtr(),
+                                          priority);
+  priority_ = priority;
+}
+
 void SpdyStreamRequest::OnRequestCompleteSuccess(
     const base::WeakPtr<SpdyStream>& stream) {
   DCHECK(session_);
@@ -1622,7 +1634,7 @@
   return OK;
 }
 
-void SpdySession::CancelStreamRequest(
+bool SpdySession::CancelStreamRequest(
     const base::WeakPtr<SpdyStreamRequest>& request) {
   DCHECK(request);
   RequestPriority priority = request->priority();
@@ -1653,6 +1665,20 @@
     // present, should not be pending completion.
     DCHECK(std::find_if(it, queue->end(), RequestEquals(request)) ==
            queue->end());
+    return true;
+  }
+  return false;
+}
+
+void SpdySession::ChangeStreamRequestPriority(
+    const base::WeakPtr<SpdyStreamRequest>& request,
+    RequestPriority priority) {
+  // |request->priority()| is updated by the caller after this returns.
+  // |request| needs to still have its old priority in order for
+  // CancelStreamRequest() to find it in the correct queue.
+  DCHECK_NE(priority, request->priority());
+  if (CancelStreamRequest(request)) {
+    pending_create_stream_queues_[priority].push_back(request);
   }
 }
 
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index a04416b..9956f573 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -227,6 +227,10 @@
   // set a delegate for the returned stream (except for test code).
   base::WeakPtr<SpdyStream> ReleaseStream();
 
+  // Changes the priority of the stream, or changes the priority of the queued
+  // request in the session.
+  void SetPriority(RequestPriority priority);
+
   // Returns the estimate of dynamically allocated memory in bytes.
   size_t EstimateMemoryUsage() const;
 
@@ -606,8 +610,14 @@
                    base::WeakPtr<SpdyStream>* stream);
 
   // Called by SpdyStreamRequest to remove |request| from the stream
-  // creation queue.
-  void CancelStreamRequest(const base::WeakPtr<SpdyStreamRequest>& request);
+  // creation queue. Returns whether a request was removed from the queue.
+  bool CancelStreamRequest(const base::WeakPtr<SpdyStreamRequest>& request);
+
+  // Removes |request| from the stream creation queue and reinserts it into the
+  // queue at the new |priority|.
+  void ChangeStreamRequestPriority(
+      const base::WeakPtr<SpdyStreamRequest>& request,
+      RequestPriority priority);
 
   // Returns the next pending stream request to process, or NULL if
   // there is none.
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index e03c45d..ea6a123 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -2003,6 +2003,48 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(SpdySessionTest, ChangeStreamRequestPriority) {
+  MockRead reads[] = {
+      MockRead(ASYNC, ERR_IO_PENDING)  // Stall forever.
+  };
+
+  StaticSocketDataProvider data(reads, base::span<MockWrite>());
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  AddSSLSocketData();
+
+  CreateNetworkSession();
+  CreateSpdySession();
+
+  set_max_concurrent_streams(1);
+
+  TestCompletionCallback callback1;
+  SpdyStreamRequest request1;
+  ASSERT_EQ(OK, request1.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
+                                      test_url_, LOWEST, SocketTag(),
+                                      NetLogWithSource(), callback1.callback(),
+                                      TRAFFIC_ANNOTATION_FOR_TESTS));
+  TestCompletionCallback callback2;
+  SpdyStreamRequest request2;
+  ASSERT_EQ(ERR_IO_PENDING,
+            request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_,
+                                  test_url_, LOWEST, SocketTag(),
+                                  NetLogWithSource(), callback2.callback(),
+                                  TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  request1.SetPriority(HIGHEST);
+  request2.SetPriority(MEDIUM);
+
+  ASSERT_EQ(0u, pending_create_stream_queue_size(HIGHEST));
+  // Priority of queued request is changed.
+  ASSERT_EQ(1u, pending_create_stream_queue_size(MEDIUM));
+  ASSERT_EQ(0u, pending_create_stream_queue_size(LOWEST));
+
+  base::WeakPtr<SpdyStream> stream1 = request1.ReleaseStream();
+  // Priority of stream is updated if request has been fulfilled.
+  ASSERT_EQ(HIGHEST, stream1->priority());
+}
+
 TEST_F(SpdySessionTest, Initialize) {
   MockRead reads[] = {
     MockRead(ASYNC, 0, 0)  // EOF
diff --git a/net/third_party/quic/core/congestion_control/bbr_sender.cc b/net/third_party/quic/core/congestion_control/bbr_sender.cc
index 02b29f5..2770424 100644
--- a/net/third_party/quic/core/congestion_control/bbr_sender.cc
+++ b/net/third_party/quic/core/congestion_control/bbr_sender.cc
@@ -769,16 +769,14 @@
   // Slow the pacing rate in STARTUP by the bytes_lost / CWND.
   if (startup_rate_reduction_multiplier_ != 0 && has_ever_detected_loss &&
       has_non_app_limited_sample_) {
-    if (startup_bytes_lost_ > congestion_window_) {
-      pacing_rate_ = BandwidthEstimate();
-    } else {
-      pacing_rate_ =
-          (1 - (startup_bytes_lost_ * startup_rate_reduction_multiplier_ *
-                1.0f / congestion_window_)) *
-          target_rate;
-      // Ensure the pacing rate doesn't drop below the bandwidth estimate.
-      pacing_rate_ = std::max(pacing_rate_, BandwidthEstimate());
-    }
+    pacing_rate_ =
+        (1 - (startup_bytes_lost_ * startup_rate_reduction_multiplier_ * 1.0f /
+              congestion_window_)) *
+        target_rate;
+    // Ensure the pacing rate doesn't drop below the startup growth target times
+    // the bandwidth estimate.
+    pacing_rate_ =
+        std::max(pacing_rate_, kStartupGrowthTarget * BandwidthEstimate());
     return;
   }
 
diff --git a/net/third_party/quic/core/congestion_control/bbr_sender_test.cc b/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
index 9b13a9d6..71330a0 100644
--- a/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
+++ b/net/third_party/quic/core/congestion_control/bbr_sender_test.cc
@@ -818,7 +818,6 @@
       kTestRtt + QuicTime::Delta::FromMilliseconds(200);
   simulator_.RunFor(0.60 * time_to_exit_probe_rtt);
   EXPECT_EQ(BbrSender::PROBE_RTT, sender_->ExportDebugState().mode);
-  EXPECT_TRUE(sender_->ExportDebugState().last_sample_is_app_limited);
   // Lose a packet before exiting PROBE_RTT, which puts us in packet
   // conservation and then continue there for a while and ensure the bandwidth
   // estimate doesn't decrease.
@@ -1197,7 +1196,7 @@
     EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow());
     EXPECT_GT(original_pacing_rate, sender_->PacingRate(0));
     EXPECT_GE(pacing_rate, sender_->PacingRate(0));
-    EXPECT_LE(sender_->BandwidthEstimate(), sender_->PacingRate(0));
+    EXPECT_LE(1.25 * sender_->BandwidthEstimate(), sender_->PacingRate(0));
     pacing_rate = sender_->PacingRate(0);
   }
 }
@@ -1247,7 +1246,7 @@
     EXPECT_EQ(original_cwnd, sender_->GetCongestionWindow());
     EXPECT_GT(original_pacing_rate, sender_->PacingRate(0));
     EXPECT_GE(pacing_rate, sender_->PacingRate(0));
-    EXPECT_LE(sender_->BandwidthEstimate(), sender_->PacingRate(0));
+    EXPECT_LE(1.25 * sender_->BandwidthEstimate(), sender_->PacingRate(0));
     pacing_rate = sender_->PacingRate(0);
   }
 }
diff --git a/net/third_party/quic/core/crypto/quic_crypto_server_config.cc b/net/third_party/quic/core/crypto/quic_crypto_server_config.cc
index 02cd7df..90c7a97 100644
--- a/net/third_party/quic/core/crypto/quic_crypto_server_config.cc
+++ b/net/third_party/quic/core/crypto/quic_crypto_server_config.cc
@@ -1111,6 +1111,14 @@
   }
 
   QuicString forward_secure_public_value;
+  if (GetQuicRestartFlag(quic_no_ephemeral_key_source)) {
+    if (ephemeral_key_source_) {
+      QUIC_BUG << "quic_no_ephemeral_key_source flag is on, but "
+                  "ephemeral_key_source is present";
+    } else {
+      QUIC_FLAG_COUNT(quic_restart_flag_quic_no_ephemeral_key_source);
+    }
+  }
   if (ephemeral_key_source_) {
     params->forward_secure_premaster_secret =
         ephemeral_key_source_->CalculateForwardSecureKey(
diff --git a/net/third_party/quic/core/frames/quic_message_frame.h b/net/third_party/quic/core/frames/quic_message_frame.h
index 83d7808..575e154 100644
--- a/net/third_party/quic/core/frames/quic_message_frame.h
+++ b/net/third_party/quic/core/frames/quic_message_frame.h
@@ -23,8 +23,8 @@
   // message_id is only used on the sender side and does not get serialized on
   // wire.
   QuicMessageId message_id;
-  // The actual data is not owned.
-  QuicStringPiece message_data;
+  // The actual data.
+  QuicString message_data;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/http/quic_spdy_client_stream.cc b/net/third_party/quic/core/http/quic_spdy_client_stream.cc
index 07fc556..5f685543 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_stream.cc
+++ b/net/third_party/quic/core/http/quic_spdy_client_stream.cc
@@ -96,7 +96,7 @@
   }
 }
 
-void QuicSpdyClientStream::OnDataAvailable() {
+void QuicSpdyClientStream::OnBodyAvailable() {
   // For push streams, visitor will not be set until the rendezvous
   // between server promise and client request is complete.
   if (visitor() == nullptr)
diff --git a/net/third_party/quic/core/http/quic_spdy_client_stream.h b/net/third_party/quic/core/http/quic_spdy_client_stream.h
index f8a6dc8b..e0db425 100644
--- a/net/third_party/quic/core/http/quic_spdy_client_stream.h
+++ b/net/third_party/quic/core/http/quic_spdy_client_stream.h
@@ -45,7 +45,7 @@
                            const QuicHeaderList& header_list) override;
 
   // QuicStream implementation called by the session when there's data for us.
-  void OnDataAvailable() override;
+  void OnBodyAvailable() override;
 
   // Serializes the headers and body, sends it to the server, and
   // returns the number of bytes sent.
diff --git a/net/third_party/quic/core/http/quic_spdy_server_stream_base_test.cc b/net/third_party/quic/core/http/quic_spdy_server_stream_base_test.cc
index bb168b7c..8c293be 100644
--- a/net/third_party/quic/core/http/quic_spdy_server_stream_base_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_server_stream_base_test.cc
@@ -22,7 +22,7 @@
                            StreamType type)
       : QuicSpdyServerStreamBase(id, session, type) {}
 
-  void OnDataAvailable() override {}
+  void OnBodyAvailable() override {}
 };
 
 class QuicSpdyServerStreamBaseTest : public QuicTest {
diff --git a/net/third_party/quic/core/http/quic_spdy_session_test.cc b/net/third_party/quic/core/http/quic_spdy_session_test.cc
index b574ec6..c845c386 100644
--- a/net/third_party/quic/core/http/quic_spdy_session_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_session_test.cc
@@ -123,7 +123,7 @@
 
   using QuicStream::CloseWriteSide;
 
-  void OnDataAvailable() override {}
+  void OnBodyAvailable() override {}
 
   MOCK_METHOD0(OnCanWrite, void());
   MOCK_METHOD3(RetransmitStreamData,
@@ -247,7 +247,6 @@
 
   using QuicSession::closed_streams;
   using QuicSession::next_outgoing_stream_id;
-  using QuicSession::PostProcessAfterData;
   using QuicSession::zombie_streams;
 
  private:
@@ -1153,9 +1152,6 @@
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
                                QUIC_STREAM_CANCELLED, kByteOffset);
   session_.OnRstStream(rst_frame);
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
   EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
 }
 
@@ -1172,9 +1168,6 @@
       kInitialSessionFlowControlWindowForTest / 2 - 1;
   QuicStreamFrame frame(stream->id(), true, kByteOffset, ".");
   session_.OnStreamFrame(frame);
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
   EXPECT_TRUE(connection_->connected());
 
   EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed());
@@ -1383,12 +1376,6 @@
   // Create one more data streams to exceed limit of open stream.
   QuicStreamFrame data1(kFinalStreamId, false, 0, QuicStringPiece("HT"));
   session_.OnStreamFrame(data1);
-
-  // Called after any new data is received by the session, and triggers the
-  // call to close the connection.
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
 }
 
 TEST_P(QuicSpdySessionTestServer, DrainingStreamsDoNotCountAsOpened) {
@@ -1419,12 +1406,6 @@
     session_.StreamDraining(i);
     EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams());
   }
-
-  // Called after any new data is received by the session, and triggers the call
-  // to close the connection.
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
 }
 
 TEST_P(QuicSpdySessionTestServer, TestMaxIncomingAndOutgoingStreamsAllowed) {
@@ -1491,10 +1472,6 @@
   stream->Reset(QUIC_STREAM_CANCELLED);
   EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream));
 
-  // Allow the session to delete the stream object.
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
   EXPECT_TRUE(connection_->connected());
   EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id));
   EXPECT_FALSE(QuicSessionPeer::IsStreamCreated(&session_, stream_id));
diff --git a/net/third_party/quic/core/http/quic_spdy_stream.cc b/net/third_party/quic/core/http/quic_spdy_stream.cc
index 9a45e5b..1704504 100644
--- a/net/third_party/quic/core/http/quic_spdy_stream.cc
+++ b/net/third_party/quic/core/http/quic_spdy_stream.cc
@@ -14,6 +14,7 @@
 #include "net/third_party/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
+#include "net/third_party/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
@@ -265,6 +266,10 @@
   CloseWriteSide();
 }
 
+void QuicSpdyStream::OnDataAvailable() {
+  OnBodyAvailable();
+}
+
 void QuicSpdyStream::OnClose() {
   QuicStream::OnClose();
 
diff --git a/net/third_party/quic/core/http/quic_spdy_stream.h b/net/third_party/quic/core/http/quic_spdy_stream.h
index d8436b8..53512fdf 100644
--- a/net/third_party/quic/core/http/quic_spdy_stream.h
+++ b/net/third_party/quic/core/http/quic_spdy_stream.h
@@ -21,7 +21,6 @@
 #include "net/third_party/quic/core/quic_stream_sequencer.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
-#include "net/third_party/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quic/platform/api/quic_socket_address.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/spdy/core/spdy_framer.h"
@@ -97,6 +96,13 @@
   // QUIC_STREAM_NO_ERROR.
   void OnStreamReset(const QuicRstStreamFrame& frame) override;
 
+  // Called by the sequencer when new data is available. Decodes the data and
+  // calls OnBodyAvailable() to pass to the upper layer.
+  void OnDataAvailable() override;
+
+  // Called in OnDataAvailable() after it finishes the decoding job.
+  virtual void OnBodyAvailable() = 0;
+
   // Writes the headers contained in |header_block| to the dedicated
   // headers stream.
   virtual size_t WriteHeaders(
diff --git a/net/third_party/quic/core/http/quic_spdy_stream_test.cc b/net/third_party/quic/core/http/quic_spdy_stream_test.cc
index add92f4..750dc8d4 100644
--- a/net/third_party/quic/core/http/quic_spdy_stream_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_stream_test.cc
@@ -49,7 +49,7 @@
       : QuicSpdyStream(id, session, BIDIRECTIONAL),
         should_process_data_(should_process_data) {}
 
-  void OnDataAvailable() override {
+  void OnBodyAvailable() override {
     if (!should_process_data_) {
       return;
     }
diff --git a/net/third_party/quic/core/qpack/qpack_constants.cc b/net/third_party/quic/core/qpack/qpack_constants.cc
new file mode 100644
index 0000000..0975699
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_constants.cc
@@ -0,0 +1,137 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quic/core/qpack/qpack_constants.h"
+
+namespace quic {
+
+bool operator==(const QpackInstructionOpcode& a,
+                const QpackInstructionOpcode& b) {
+  return std::tie(a.value, a.mask) == std::tie(b.value, b.mask);
+}
+
+const QpackInstruction* InsertWithNameReferenceInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kInsertWithNameReferenceOpcode,
+                                 kInsertWithNameReferenceOpcodeMask};
+  static const QpackInstruction* const instruction = new QpackInstruction{
+      *opcode,
+      {{QpackInstructionFieldType::kStaticBit,
+        kInsertWithNameReferenceStaticBit},
+       {QpackInstructionFieldType::kVarint,
+        kInsertWithNameReferenceNameIndexPrefixLength},
+       {QpackInstructionFieldType::kValue, kLiteralValuePrefixLength}}};
+  return instruction;
+}
+
+const QpackInstruction* InsertWithoutNameReferenceInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kInsertWithoutNameReferenceOpcode,
+                                 kInsertWithoutNameReferenceOpcodeMask};
+  static const QpackInstruction* const instruction = new QpackInstruction{
+      *opcode,
+      {{QpackInstructionFieldType::kName,
+        kInsertWithoutNameReferenceNameLengthPrefixLength},
+       {QpackInstructionFieldType::kValue, kLiteralValuePrefixLength}}};
+  return instruction;
+}
+
+const QpackInstruction* DuplicateInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kDuplicateOpcode, kDuplicateOpcodeMask};
+  static const QpackInstruction* const instruction = new QpackInstruction{
+      *opcode,
+      {{QpackInstructionFieldType::kVarint, kDuplicateIndexPrefixLength}}};
+  return instruction;
+}
+
+const QpackInstruction* DynamicTableSizeUpdateInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kDynamicTableSizeUpdateOpcode,
+                                 kDynamicTableSizeUpdateOpcodeMask};
+  static const QpackInstruction* const instruction =
+      new QpackInstruction{*opcode,
+                           {{QpackInstructionFieldType::kVarint,
+                             kDynamicTableSizeUpdateMaxSizePrefixLength}}};
+  return instruction;
+}
+
+const QpackLanguage* QpackEncoderStreamLanguage() {
+  static const QpackLanguage* const language = new QpackLanguage{
+      InsertWithNameReferenceInstruction(),
+      InsertWithoutNameReferenceInstruction(), DuplicateInstruction(),
+      DynamicTableSizeUpdateInstruction()};
+  return language;
+}
+
+const QpackInstruction* QpackIndexedHeaderFieldInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kIndexedHeaderFieldOpcodeValue,
+                                 kIndexedHeaderFieldOpcodeMask};
+  static const QpackInstruction* const instruction = new QpackInstruction{
+      *opcode,
+      {{QpackInstructionFieldType::kStaticBit, kIndexedHeaderFieldStaticBit},
+       {QpackInstructionFieldType::kVarint, kIndexedHeaderFieldPrefixLength}}};
+  return instruction;
+}
+
+const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kIndexedHeaderFieldPostBaseOpcodeValue,
+                                 kIndexedHeaderFieldPostBaseOpcodeMask};
+  static const QpackInstruction* const instruction =
+      new QpackInstruction{*opcode,
+                           {{QpackInstructionFieldType::kVarint,
+                             kIndexedHeaderFieldPostBasePrefixLength}}};
+  return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kLiteralHeaderFieldNameReferenceOpcodeValue,
+                                 kLiteralHeaderFieldNameReferenceOpcodeMask};
+  static const QpackInstruction* const instruction = new QpackInstruction{
+      *opcode,
+      {{QpackInstructionFieldType::kStaticBit,
+        kLiteralHeaderFieldNameReferenceStaticBit},
+       {QpackInstructionFieldType::kVarint,
+        kLiteralHeaderFieldNameReferencePrefixLength},
+       {QpackInstructionFieldType::kValue, kLiteralValuePrefixLength}}};
+  return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kLiteralHeaderFieldPostBaseOpcodeValue,
+                                 kLiteralHeaderFieldPostBaseOpcodeMask};
+  static const QpackInstruction* const instruction = new QpackInstruction{
+      *opcode,
+      {{QpackInstructionFieldType::kVarint,
+        kLiteralHeaderFieldPostBasePrefixLength},
+       {QpackInstructionFieldType::kValue, kLiteralValuePrefixLength}}};
+  return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldInstruction() {
+  static const QpackInstructionOpcode* const opcode =
+      new QpackInstructionOpcode{kLiteralHeaderFieldOpcodeValue,
+                                 kLiteralHeaderFieldOpcodeMask};
+  static const QpackInstruction* const instruction = new QpackInstruction{
+      *opcode,
+      {{QpackInstructionFieldType::kName, kLiteralHeaderFieldPrefixLength},
+       {QpackInstructionFieldType::kValue, kLiteralValuePrefixLength}}};
+  return instruction;
+}
+
+const QpackLanguage* QpackRequestStreamLanguage() {
+  static const QpackLanguage* const language =
+      new QpackLanguage{QpackIndexedHeaderFieldInstruction(),
+                        QpackIndexedHeaderFieldPostBaseInstruction(),
+                        QpackLiteralHeaderFieldNameReferenceInstruction(),
+                        QpackLiteralHeaderFieldPostBaseInstruction(),
+                        QpackLiteralHeaderFieldInstruction()};
+  return language;
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_constants.h b/net/third_party/quic/core/qpack/qpack_constants.h
index 585577d..217e22c 100644
--- a/net/third_party/quic/core/qpack/qpack_constants.h
+++ b/net/third_party/quic/core/qpack/qpack_constants.h
@@ -6,9 +6,76 @@
 #define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_
 
 #include <cstdint>
+#include <tuple>
+#include <vector>
+
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
 
 namespace quic {
 
+// Each instruction is identified with an opcode in the first byte.
+// |mask| determines which bits are part of the opcode.
+// |value| is the value of these bits.  (Other bits in value must be zero.)
+struct QUIC_EXPORT_PRIVATE QpackInstructionOpcode {
+  uint8_t value;
+  uint8_t mask;
+};
+
+bool operator==(const QpackInstructionOpcode& a,
+                const QpackInstructionOpcode& b);
+
+// Possible types of an instruction field.  Decoding a static bit does not
+// consume the current byte.  Decoding an integer or a length-prefixed string
+// literal consumes all bytes containing the field value.
+enum class QpackInstructionFieldType {
+  // A single bit indicating whether the index is static.
+  kStaticBit,
+  // An integer encoded with variable length encoding.  This could be an index,
+  // stream ID, or maximum size.
+  kVarint,
+  // A header name or header value encoded as:
+  //   a bit indicating whether it is Huffman encoded;
+  //   the encoded length of the string;
+  //   the header name or value optionally Huffman encoded.
+  kName,
+  kValue
+};
+
+// Each instruction field has a type and a parameter.
+// The meaning of the parameter depends on the field type.
+struct QUIC_EXPORT_PRIVATE QpackInstructionField {
+  QpackInstructionFieldType type;
+  // For a kStaticBit field, |param| is a mask with exactly one bit set.
+  // For kVarint fields, |param| is the prefix length of the integer encoding.
+  // For kName and kValue fields, |param| is the prefix length of the length of
+  // the string, and the bit immediately preceding the prefix is interpreted as
+  // the Huffman bit.
+  uint8_t param;
+};
+
+using QpackInstructionFields = std::vector<QpackInstructionField>;
+
+// A QPACK instruction consists of an opcode identifying the instruction,
+// followed by a non-empty list of fields.  The last field must be integer or
+// string literal type to guarantee that all bytes of the instruction are
+// consumed.
+struct QUIC_EXPORT_PRIVATE QpackInstruction {
+  QpackInstruction(const QpackInstruction&) = delete;
+  const QpackInstruction& operator=(const QpackInstruction&) = delete;
+
+  QpackInstructionOpcode opcode;
+  QpackInstructionFields fields;
+};
+
+// A language is a collection of instructions.  The order does not matter.
+// The set of instruction opcodes must cover all possible input.
+using QpackLanguage = std::vector<const QpackInstruction*>;
+
+// Maximum length of header name and header value.  This limits the amount of
+// memory the peer can make the decoder allocate when sending string literals.
+const size_t kStringLiteralLengthLimit = 1024 * 1024;
+
 // TODO(bnc): Move this into HpackVarintEncoder.
 // The integer encoder can encode up to 2^64-1, which can take up to 10 bytes
 // (each carrying 7 bits) after the prefix.
@@ -17,6 +84,11 @@
 // Wire format defined in
 // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5
 
+// Value encoding for instructions with literal value.
+const uint8_t kLiteralValueHuffmanMask = 0b10000000;
+const uint8_t kLiteralValueWithoutHuffmanEncoding = 0b00000000;
+const uint8_t kLiteralValuePrefixLength = 7;
+
 // Encoder stream
 
 // 5.2.1 Insert With Name Reference
@@ -25,56 +97,76 @@
 const uint8_t kInsertWithNameReferenceStaticBit = 0b01000000;
 const uint8_t kInsertWithNameReferenceNameIndexPrefixLength = 6;
 
+const QpackInstruction* InsertWithNameReferenceInstruction();
+
 // 5.2.2 Insert Without Name Reference
 const uint8_t kInsertWithoutNameReferenceOpcode = 0b01000000;
 const uint8_t kInsertWithoutNameReferenceOpcodeMask = 0b11000000;
 const uint8_t kInsertWithoutNameReferenceNameHuffmanBit = 0b00100000;
 const uint8_t kInsertWithoutNameReferenceNameLengthPrefixLength = 5;
 
+const QpackInstruction* InsertWithoutNameReferenceInstruction();
+
 // 5.2.3 Duplicate
 const uint8_t kDuplicateOpcode = 0b00000000;
 const uint8_t kDuplicateOpcodeMask = 0b11100000;
 const uint8_t kDuplicateIndexPrefixLength = 5;
 
+const QpackInstruction* DuplicateInstruction();
+
 // 5.2.4 Dynamic Table Size Update
 const uint8_t kDynamicTableSizeUpdateOpcode = 0b00100000;
 const uint8_t kDynamicTableSizeUpdateOpcodeMask = 0b11100000;
 const uint8_t kDynamicTableSizeUpdateMaxSizePrefixLength = 5;
 
+const QpackInstruction* DynamicTableSizeUpdateInstruction();
+
+// Description of language (set of instructions) used on the encoder stream.
+const QpackLanguage* QpackEncoderStreamLanguage();
+
 // Request and push streams
 
 // 5.4.2.1. Indexed Header Field
-const uint8_t kIndexedHeaderFieldOpcode = 0b10000000;
+const uint8_t kIndexedHeaderFieldOpcodeValue = 0b10000000;
 const uint8_t kIndexedHeaderFieldOpcodeMask = 0b10000000;
 const uint8_t kIndexedHeaderFieldStaticBit = 0b01000000;
 const uint8_t kIndexedHeaderFieldPrefixLength = 6;
 
+const QpackInstruction* QpackIndexedHeaderFieldInstruction();
+
 // 5.4.2.2. Indexed Header Field With Post-Base Index
-const uint8_t kIndexedHeaderFieldPostBaseOpcode = 0b00010000;
+const uint8_t kIndexedHeaderFieldPostBaseOpcodeValue = 0b00010000;
 const uint8_t kIndexedHeaderFieldPostBaseOpcodeMask = 0b11110000;
 const uint8_t kIndexedHeaderFieldPostBasePrefixLength = 4;
 
+const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction();
+
 // 5.4.2.3. Literal Header Field With Name Reference
-const uint8_t kLiteralHeaderFieldNameReferenceOpcode = 0b01000000;
+const uint8_t kLiteralHeaderFieldNameReferenceOpcodeValue = 0b01000000;
 const uint8_t kLiteralHeaderFieldNameReferenceOpcodeMask = 0b11000000;
 const uint8_t kLiteralHeaderFieldNameReferenceStaticBit = 0b00010000;
 const uint8_t kLiteralHeaderFieldNameReferencePrefixLength = 4;
 
+const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction();
+
 // 5.4.2.4. Literal Header Field With Post-Base Name Reference
-const uint8_t kLiteralHeaderFieldPostBaseOpcode = 0b00000000;
+const uint8_t kLiteralHeaderFieldPostBaseOpcodeValue = 0b00000000;
 const uint8_t kLiteralHeaderFieldPostBaseOpcodeMask = 0b11110000;
 const uint8_t kLiteralHeaderFieldPostBasePrefixLength = 3;
 
+const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction();
+
 // 5.4.2.5. Literal Header Field Without Name Reference
-const uint8_t kLiteralHeaderFieldOpcode = 0b00100000;
+const uint8_t kLiteralHeaderFieldOpcodeValue = 0b00100000;
 const uint8_t kLiteralHeaderFieldOpcodeMask = 0b11100000;
 const uint8_t kLiteralNameHuffmanMask = 0b00001000;
 const uint8_t kLiteralHeaderFieldPrefixLength = 3;
 
-// Value encoding for instructions with literal value.
-const uint8_t kLiteralValueHuffmanMask = 0b10000000;
-const uint8_t kLiteralValueWithoutHuffmanEncoding = 0b00000000;
-const uint8_t kLiteralValuePrefixLength = 7;
+const QpackInstruction* QpackLiteralHeaderFieldInstruction();
+
+// Description of language (set of instructions) used on request and push
+// streams.
+const QpackLanguage* QpackRequestStreamLanguage();
 
 }  // namespace quic
 
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.cc b/net/third_party/quic/core/qpack/qpack_decoder.cc
index b862c93..284ac35 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder.cc
@@ -14,16 +14,11 @@
 QpackDecoder::ProgressiveDecoder::ProgressiveDecoder(
     QpackHeaderTable* header_table,
     QpackDecoder::HeadersHandlerInterface* handler)
-    : header_table_(header_table),
+    : instruction_decoder_(QpackRequestStreamLanguage(), this),
+      header_table_(header_table),
       handler_(handler),
-      state_(State::kStart),
       decoding_(true),
-      error_detected_(false),
-      literal_name_(false),
-      literal_value_(false),
-      name_length_(0),
-      value_length_(0),
-      is_huffman_(false) {}
+      error_detected_(false) {}
 
 void QpackDecoder::ProgressiveDecoder::Decode(QuicStringPiece data) {
   DCHECK(decoding_);
@@ -32,61 +27,7 @@
     return;
   }
 
-  while (true) {
-    size_t bytes_consumed = 0;
-
-    switch (state_) {
-      case State::kStart:
-        bytes_consumed = DoStart(data);
-        break;
-      case State::kVarintResume:
-        bytes_consumed = DoVarintResume(data);
-        break;
-      case State::kVarintDone:
-        DoVarintDone();
-        break;
-      case State::kReadName:
-        bytes_consumed = DoReadName(data);
-        break;
-      case State::kDecodeName:
-        DoDecodeName();
-        break;
-      case State::kValueLengthStart:
-        bytes_consumed = DoValueLengthStart(data);
-        break;
-      case State::kValueLengthResume:
-        bytes_consumed = DoValueLengthResume(data);
-        break;
-      case State::kValueLengthDone:
-        DoValueLengthDone();
-        break;
-      case State::kReadValue:
-        bytes_consumed = DoReadValue(data);
-        break;
-      case State::kDecodeValue:
-        DoDecodeValue();
-        break;
-      case State::kDone:
-        DoDone();
-        break;
-    }
-
-    if (error_detected_) {
-      return;
-    }
-
-    DCHECK_LE(bytes_consumed, data.size());
-
-    data = QuicStringPiece(data.data() + bytes_consumed,
-                           data.size() - bytes_consumed);
-
-    // Stop processing if no more data but next state would require it.
-    if (data.empty() && (state_ != State::kVarintDone) &&
-        (state_ != State::kDecodeName) && (state_ != State::kValueLengthDone) &&
-        (state_ != State::kDecodeValue) && (state_ != State::kDone)) {
-      return;
-    }
-  }
+  instruction_decoder_.Decode(data);
 }
 
 void QpackDecoder::ProgressiveDecoder::EndHeaderBlock() {
@@ -97,271 +38,73 @@
     return;
   }
 
-  if (state_ == State::kStart) {
+  if (instruction_decoder_.AtInstructionBoundary()) {
     handler_->OnDecodingCompleted();
   } else {
     OnError("Incomplete header block.");
   }
 }
 
-size_t QpackDecoder::ProgressiveDecoder::DoStart(QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  size_t prefix_length = 0;
-  if ((data[0] & kIndexedHeaderFieldOpcodeMask) == kIndexedHeaderFieldOpcode) {
-    if ((data[0] & kIndexedHeaderFieldStaticBit) !=
-        kIndexedHeaderFieldStaticBit) {
+bool QpackDecoder::ProgressiveDecoder::OnInstructionDecoded(
+    const QpackInstruction* instruction) {
+  if (instruction == QpackIndexedHeaderFieldInstruction()) {
+    if (!instruction_decoder_.is_static()) {
       // TODO(bnc): Implement.
       OnError("Indexed Header Field with dynamic entry not implemented.");
-      return 0;
+      return false;
     }
-    prefix_length = kIndexedHeaderFieldPrefixLength;
-    literal_name_ = false;
-    literal_value_ = false;
-  } else if ((data[0] & kIndexedHeaderFieldPostBaseOpcodeMask) ==
-             kIndexedHeaderFieldPostBaseOpcode) {
+
+    auto entry = header_table_->LookupEntry(instruction_decoder_.varint());
+    if (!entry) {
+      OnError("Invalid static table index.");
+      return false;
+    }
+
+    handler_->OnHeaderDecoded(entry->name(), entry->value());
+    return true;
+  }
+
+  if (instruction == QpackIndexedHeaderFieldPostBaseInstruction()) {
     // TODO(bnc): Implement.
     OnError("Indexed Header Field With Post-Base Index not implemented.");
-    return 0;
-  } else if ((data[0] & kLiteralHeaderFieldNameReferenceOpcodeMask) ==
-             kLiteralHeaderFieldNameReferenceOpcode) {
-    if ((data[0] & kLiteralHeaderFieldNameReferenceStaticBit) !=
-        kLiteralHeaderFieldNameReferenceStaticBit) {
+    return false;
+  }
+
+  if (instruction == QpackLiteralHeaderFieldNameReferenceInstruction()) {
+    if (!instruction_decoder_.is_static()) {
       // TODO(bnc): Implement.
       OnError(
           "Literal Header Field With Name Reference with dynamic entry not "
           "implemented.");
-      return 0;
+      return false;
     }
-    prefix_length = kLiteralHeaderFieldNameReferencePrefixLength;
-    literal_name_ = false;
-    literal_value_ = true;
-  } else if ((data[0] & kLiteralHeaderFieldPostBaseOpcodeMask) ==
-             kLiteralHeaderFieldPostBaseOpcode) {
+
+    auto entry = header_table_->LookupEntry(instruction_decoder_.varint());
+    if (!entry) {
+      OnError(
+          "Invalid static table index in Literal Header Field With Name "
+          "Reference instruction.");
+      return false;
+    }
+
+    handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value());
+    return true;
+  }
+
+  if (instruction == QpackLiteralHeaderFieldPostBaseInstruction()) {
     // TODO(bnc): Implement.
     OnError(
-        "Literal Header Field With Post-Base Name Reference not implemented.");
-    return 0;
-  } else {
-    DCHECK_EQ(kLiteralHeaderFieldOpcode,
-              data[0] & kLiteralHeaderFieldOpcodeMask);
-
-    is_huffman_ =
-        (data[0] & kLiteralNameHuffmanMask) == kLiteralNameHuffmanMask;
-    prefix_length = kLiteralHeaderFieldPrefixLength;
-    literal_name_ = true;
-    literal_value_ = true;
+        "Literal Header Field With Post-Base Name Reference not "
+        "implemented.");
+    return false;
   }
 
-  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
-  http2::DecodeStatus status =
-      varint_decoder_.Start(data[0], prefix_length, &buffer);
+  DCHECK_EQ(instruction, QpackLiteralHeaderFieldInstruction());
 
-  size_t bytes_consumed = 1 + buffer.Offset();
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kVarintDone;
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeInProgress:
-      state_ = State::kVarintResume;
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("Encoded integer too large.");
-      return bytes_consumed;
-  }
-}
+  handler_->OnHeaderDecoded(instruction_decoder_.name(),
+                            instruction_decoder_.value());
 
-size_t QpackDecoder::ProgressiveDecoder::DoVarintResume(QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  http2::DecodeBuffer buffer(data);
-  http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
-
-  size_t bytes_consumed = buffer.Offset();
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kVarintDone;
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK_EQ(bytes_consumed, data.size());
-      DCHECK(buffer.Empty());
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("Encoded integer too large.");
-      return bytes_consumed;
-  }
-}
-
-void QpackDecoder::ProgressiveDecoder::DoVarintDone() {
-  if (literal_name_) {
-    // TODO(bnc): Impose a sensible limit on length to avoid memory exhaustion
-    // attacks.
-    name_length_ = varint_decoder_.value();
-    name_.clear();
-    name_.reserve(name_length_);
-    // Do not handle empty names differently.  (They are probably forbidden by
-    // higher layers, but it is not enforced in this class.)  If there is no
-    // more data to read, then processing stalls, but the instruction is not
-    // complete without the value, so OnHeaderDecoded() could not be called yet
-    // anyway.
-    state_ = State::kReadName;
-    return;
-  }
-
-  auto entry = header_table_->LookupEntry(varint_decoder_.value());
-  if (!entry) {
-    OnError("Invalid static table index.");
-    return;
-  }
-
-  if (literal_value_) {
-    name_.assign(entry->name().data(), entry->name().size());
-    state_ = State::kValueLengthStart;
-    return;
-  }
-
-  // Call OnHeaderDecoded() here instead of changing to State::kDone
-  // to prevent copying two strings.
-  handler_->OnHeaderDecoded(entry->name(), entry->value());
-  state_ = State::kStart;
-}
-
-size_t QpackDecoder::ProgressiveDecoder::DoReadName(QuicStringPiece data) {
-  DCHECK(!data.empty());
-  // |name_length_| might be zero.
-  DCHECK_LE(name_.size(), name_length_);
-
-  size_t bytes_consumed = std::min(name_length_ - name_.size(), data.size());
-  name_.append(data.data(), bytes_consumed);
-
-  DCHECK_LE(name_.size(), name_length_);
-  if (name_.size() == name_length_) {
-    state_ = State::kDecodeName;
-  }
-
-  return bytes_consumed;
-}
-
-void QpackDecoder::ProgressiveDecoder::DoDecodeName() {
-  DCHECK_EQ(name_.size(), name_length_);
-
-  if (is_huffman_) {
-    huffman_decoder_.Reset();
-    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
-    QuicString decoded_name;
-    huffman_decoder_.Decode(name_, &decoded_name);
-    if (!huffman_decoder_.InputProperlyTerminated()) {
-      OnError("Error in Huffman-encoded name.");
-      return;
-    }
-    name_ = decoded_name;
-  }
-
-  state_ = State::kValueLengthStart;
-}
-
-size_t QpackDecoder::ProgressiveDecoder::DoValueLengthStart(
-    QuicStringPiece data) {
-  DCHECK(!data.empty());
-  DCHECK(literal_value_);
-
-  is_huffman_ =
-      (data[0] & kLiteralValueHuffmanMask) == kLiteralValueHuffmanMask;
-
-  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
-  http2::DecodeStatus status =
-      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
-
-  size_t bytes_consumed = 1 + buffer.Offset();
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kValueLengthDone;
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeInProgress:
-      state_ = State::kValueLengthResume;
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("ValueLen too large.");
-      return bytes_consumed;
-  }
-}
-
-size_t QpackDecoder::ProgressiveDecoder::DoValueLengthResume(
-    QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  http2::DecodeBuffer buffer(data);
-  http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
-
-  size_t bytes_consumed = buffer.Offset();
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kValueLengthDone;
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK_EQ(bytes_consumed, data.size());
-      DCHECK(buffer.Empty());
-      return bytes_consumed;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("ValueLen too large.");
-      return bytes_consumed;
-  }
-}
-
-void QpackDecoder::ProgressiveDecoder::DoValueLengthDone() {
-  value_.clear();
-  // TODO(bnc): Impose a sensible limit on length to avoid memory exhaustion
-  // attacks.
-  value_length_ = varint_decoder_.value();
-
-  // If value is empty, skip DoReadValue() and DoDecodeValue() and jump directly
-  // to DoDone().  This is so that OnHeaderDecoded() is called even if there is
-  // no more data.
-  if (value_length_ == 0) {
-    state_ = State::kDone;
-    return;
-  }
-
-  value_.reserve(value_length_);
-  state_ = State::kReadValue;
-}
-
-size_t QpackDecoder::ProgressiveDecoder::DoReadValue(QuicStringPiece data) {
-  DCHECK(!data.empty());
-  DCHECK_LT(0u, value_length_);
-  DCHECK_LT(value_.size(), value_length_);
-
-  size_t bytes_consumed = std::min(value_length_ - value_.size(), data.size());
-  value_.append(data.data(), bytes_consumed);
-
-  DCHECK_LE(value_.size(), value_length_);
-  if (value_.size() == value_length_) {
-    state_ = State::kDecodeValue;
-  }
-  return bytes_consumed;
-}
-
-void QpackDecoder::ProgressiveDecoder::DoDecodeValue() {
-  DCHECK_EQ(value_.size(), value_length_);
-
-  if (is_huffman_) {
-    huffman_decoder_.Reset();
-    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
-    QuicString decoded_value;
-    huffman_decoder_.Decode(value_, &decoded_value);
-    if (!huffman_decoder_.InputProperlyTerminated()) {
-      OnError("Error in Huffman-encoded value.");
-      return;
-    }
-    value_ = decoded_value;
-  }
-
-  state_ = State::kDone;
-}
-
-void QpackDecoder::ProgressiveDecoder::DoDone() {
-  handler_->OnHeaderDecoded(name_, value_);
-  state_ = State::kStart;
+  return true;
 }
 
 void QpackDecoder::ProgressiveDecoder::OnError(QuicStringPiece error_message) {
diff --git a/net/third_party/quic/core/qpack/qpack_decoder.h b/net/third_party/quic/core/qpack/qpack_decoder.h
index 3d8e0cb5..497d7f9 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder.h
+++ b/net/third_party/quic/core/qpack/qpack_decoder.h
@@ -7,9 +7,8 @@
 
 #include <memory>
 
-#include "net/third_party/http2/hpack/huffman/hpack_huffman_decoder.h"
-#include "net/third_party/http2/hpack/varint/hpack_varint_decoder.h"
 #include "net/third_party/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quic/core/qpack/qpack_instruction_decoder.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
@@ -47,14 +46,15 @@
   };
 
   // Class to decode a single header block.
-  class QUIC_EXPORT_PRIVATE ProgressiveDecoder {
+  class QUIC_EXPORT_PRIVATE ProgressiveDecoder
+      : public QpackInstructionDecoder::Delegate {
    public:
     ProgressiveDecoder() = delete;
     ProgressiveDecoder(QpackHeaderTable* header_table,
                        HeadersHandlerInterface* handler);
     ProgressiveDecoder(const ProgressiveDecoder&) = delete;
     ProgressiveDecoder& operator=(const ProgressiveDecoder&) = delete;
-    ~ProgressiveDecoder() = default;
+    ~ProgressiveDecoder() override = default;
 
     // Provide a data fragment to decode.
     void Decode(QuicStringPiece data);
@@ -63,80 +63,20 @@
     // through Decode().  No methods must be called afterwards.
     void EndHeaderBlock();
 
+    // QpackInstructionDecoder::Delegate implementation.
+    bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+    void OnError(QuicStringPiece error_message) override;
+
    private:
-    enum class State {
-      // Every instruction starts encoding an integer on the first octet:
-      // either an index or the length of the name string literal.
-      kStart,
-      kVarintResume,
-      kVarintDone,
-      // This might be followed by the name as a string literal,
-      // optionally Huffman encoded.
-      kReadName,
-      kDecodeName,
-      // This might be followed by the length of the value.
-      kValueLengthStart,
-      kValueLengthResume,
-      kValueLengthDone,
-      // This might be followed by the value as a string literal,
-      // optionally Huffman encoded.
-      kReadValue,
-      kDecodeValue,
-      kDone,
-    };
-
-    // One method for each state.  Some take input data and return the number of
-    // octets processed.  Some only change internal state.
-    size_t DoStart(QuicStringPiece data);
-    size_t DoVarintResume(QuicStringPiece data);
-    void DoVarintDone();
-    size_t DoReadName(QuicStringPiece data);
-    void DoDecodeName();
-    size_t DoValueLengthStart(QuicStringPiece data);
-    size_t DoValueLengthResume(QuicStringPiece data);
-    void DoValueLengthDone();
-    size_t DoReadValue(QuicStringPiece data);
-    void DoDecodeValue();
-    void DoDone();
-
-    void OnError(QuicStringPiece error_message);
-
+    QpackInstructionDecoder instruction_decoder_;
     const QpackHeaderTable* const header_table_;
     HeadersHandlerInterface* handler_;
-    State state_;
-    http2::HpackVarintDecoder varint_decoder_;
-    http2::HpackHuffmanDecoder huffman_decoder_;
 
     // True until EndHeaderBlock() is called.
     bool decoding_;
 
     // True if a decoding error has been detected.
     bool error_detected_;
-
-    // The following variables are used to carry information between states
-    // within a single header field.  That is, a value assigned while decoding
-    // one header field shall never be used for decoding subsequent header
-    // fields.
-
-    // True if the header field name is encoded as a string literal.
-    bool literal_name_;
-
-    // True if the header field value is encoded as a string literal.
-    bool literal_value_;
-
-    // Decoded length for header name.
-    size_t name_length_;
-
-    // Decoded length for header value.
-    size_t value_length_;
-
-    // Whether the currently parsed string (name or value) is
-    // Huffman encoded.
-    bool is_huffman_;
-
-    // Decoded header name and value.
-    QuicString name_;
-    QuicString value_;
   };
 
   // Factory method to create a ProgressiveDecoder for decoding a header block.
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_test.cc
index 9f43a39..742b92e 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder_test.cc
@@ -125,9 +125,18 @@
   Decode(QuicTextUtils::HexDecode("27ffffffffffffffffffff"));
 }
 
-TEST_P(QpackDecoderTest, ValueLenTooLarge) {
-  EXPECT_CALL(handler_,
-              OnDecodingErrorDetected(QuicStringPiece("ValueLen too large.")));
+// Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_P(QpackDecoderTest, NameLenExceedsLimit) {
+  EXPECT_CALL(handler_, OnDecodingErrorDetected(
+                            QuicStringPiece("String literal too long.")));
+
+  Decode(QuicTextUtils::HexDecode("27ffff7f"));
+}
+
+// Value Length value is too large for varint decoder to decode.
+TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) {
+  EXPECT_CALL(handler_, OnDecodingErrorDetected(
+                            QuicStringPiece("Encoded integer too large.")));
 
   Decode(QuicTextUtils::HexDecode("23666f6f7fffffffffffffffffffff"));
 }
@@ -167,8 +176,8 @@
 }
 
 TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("Error in Huffman-encoded name.")));
+  EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+                            "Error in Huffman-encoded string.")));
 
   // 'y' ends in 0b0 on the most significant bit of the last byte.
   // The remaining 7 bits must be a prefix of EOS, which is all 1s.
@@ -177,7 +186,7 @@
 
 TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) {
   EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
-                            "Error in Huffman-encoded value.")));
+                            "Error in Huffman-encoded string.")));
 
   // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte.
   // The remaining 5 bits must be a prefix of EOS, which is all 1s.
@@ -185,8 +194,8 @@
 }
 
 TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("Error in Huffman-encoded name.")));
+  EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+                            "Error in Huffman-encoded string.")));
 
   // The trailing EOS prefix must be at most 7 bits long.  Appending one octet
   // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a
@@ -197,7 +206,7 @@
 
 TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) {
   EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
-                            "Error in Huffman-encoded value.")));
+                            "Error in Huffman-encoded string.")));
 
   // The trailing EOS prefix must be at most 7 bits long.  Appending one octet
   // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a
@@ -246,6 +255,7 @@
 
   Decode(QuicTextUtils::HexDecode("ff23ff24"));
 }
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_encoder.cc b/net/third_party/quic/core/qpack/qpack_encoder.cc
index eee1f9d..6c27319 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder.cc
@@ -103,7 +103,7 @@
   DCHECK(HasNext());
   DCHECK_NE(0u, max_encoded_bytes);
 
-  while (max_encoded_bytes > 0 && HasNext()) {
+  while (HasNext()) {
     size_t encoded_bytes = 0;
 
     switch (state_) {
@@ -132,6 +132,13 @@
 
     DCHECK_LE(encoded_bytes, max_encoded_bytes);
     max_encoded_bytes -= encoded_bytes;
+
+    // Run VarintDone state (even with no more space to write left, since this
+    // state does not write) so that |header_list_iterator_| is incremented and
+    // thus HasNext() returns correct value if encoding has completed.
+    if (max_encoded_bytes == 0 && state_ != State::kVarintDone) {
+      return;
+    }
   }
 }
 
@@ -148,7 +155,7 @@
       literal_value_ = false;
 
       output->push_back(varint_encoder_.StartEncoding(
-          kIndexedHeaderFieldOpcode | kIndexedHeaderFieldStaticBit,
+          kIndexedHeaderFieldOpcodeValue | kIndexedHeaderFieldStaticBit,
           kIndexedHeaderFieldPrefixLength, index));
 
       break;
@@ -157,7 +164,7 @@
       literal_value_ = true;
 
       output->push_back(varint_encoder_.StartEncoding(
-          kLiteralHeaderFieldNameReferenceOpcode |
+          kLiteralHeaderFieldNameReferenceOpcodeValue |
               kLiteralHeaderFieldNameReferenceStaticBit,
           kLiteralHeaderFieldNameReferencePrefixLength, index));
 
@@ -172,12 +179,12 @@
       if (huffman_encoded_string_.size() < name.size()) {
         string_to_write_ = huffman_encoded_string_;
         output->push_back(varint_encoder_.StartEncoding(
-            kLiteralHeaderFieldOpcode | kLiteralNameHuffmanMask,
+            kLiteralHeaderFieldOpcodeValue | kLiteralNameHuffmanMask,
             kLiteralHeaderFieldPrefixLength, string_to_write_.size()));
       } else {
         string_to_write_ = name;
         output->push_back(varint_encoder_.StartEncoding(
-            kLiteralHeaderFieldOpcode, kLiteralHeaderFieldPrefixLength,
+            kLiteralHeaderFieldOpcodeValue, kLiteralHeaderFieldPrefixLength,
             string_to_write_.size()));
       }
       break;
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
index 628a2e8..45a3b8d 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.cc
@@ -11,376 +11,43 @@
 namespace quic {
 
 QpackEncoderStreamReceiver::QpackEncoderStreamReceiver(Delegate* delegate)
-    : delegate_(delegate),
-      state_(State::kStart),
-      is_huffman_(false),
-      is_static_(false),
-      literal_name_(false),
-      name_index_(0),
-      name_length_(0),
-      value_length_(0),
+    : instruction_decoder_(QpackEncoderStreamLanguage(), this),
+      delegate_(delegate),
       error_detected_(false) {
   DCHECK(delegate_);
 }
 
 void QpackEncoderStreamReceiver::Decode(QuicStringPiece data) {
-  while (!error_detected_) {
-    size_t bytes_consumed = 0;
-
-    switch (state_) {
-      case State::kStart:
-        bytes_consumed = DoStart(data);
-        break;
-      case State::kNameIndexOrLengthResume:
-        bytes_consumed = DoNameIndexOrLengthResume(data);
-        break;
-      case State::kNameIndexOrLengthDone:
-        DoNameIndexOrLengthDone();
-        break;
-      case State::kReadName:
-        bytes_consumed = DoReadName(data);
-        break;
-      case State::kDecodeName:
-        DoDecodeName();
-        break;
-      case State::kValueLengthStart:
-        bytes_consumed = DoValueLengthStart(data);
-        break;
-      case State::kValueLengthResume:
-        bytes_consumed = DoValueLengthResume(data);
-        break;
-      case State::kValueLengthDone:
-        DoValueLengthDone();
-        break;
-      case State::kReadValue:
-        bytes_consumed = DoReadValue(data);
-        break;
-      case State::kDecodeValue:
-        DoDecodeValue();
-        break;
-      case State::kInsertDone:
-        DoInsertDone();
-        break;
-      case State::kIndexResume:
-        bytes_consumed = DoIndexResume(data);
-        break;
-      case State::kIndexDone:
-        DoIndexDone();
-        break;
-      case State::kMaxSizeResume:
-        bytes_consumed = DoMaxSizeResume(data);
-        break;
-      case State::kMaxSizeDone:
-        DoMaxSizeDone();
-        break;
-    }
-
-    DCHECK_LE(bytes_consumed, data.size());
-
-    data = QuicStringPiece(data.data() + bytes_consumed,
-                           data.size() - bytes_consumed);
-
-    // Stop processing if no more data but next state would require it.
-    if (data.empty() && (state_ != State::kNameIndexOrLengthDone) &&
-        (state_ != State::kDecodeName) && (state_ != State::kValueLengthDone) &&
-        (state_ != State::kDecodeValue) && (state_ != State::kInsertDone) &&
-        (state_ != State::kIndexDone) && (state_ != State::kMaxSizeDone)) {
-      return;
-    }
-  }
-}
-
-size_t QpackEncoderStreamReceiver::DoStart(QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  size_t prefix_length;
-  State state_varint_in_progress;
-  State state_varint_done;
-  if ((data[0] & kInsertWithNameReferenceOpcodeMask) ==
-      kInsertWithNameReferenceOpcode) {
-    is_static_ = (data[0] & kInsertWithNameReferenceStaticBit) ==
-                 kInsertWithNameReferenceStaticBit;
-
-    prefix_length = kInsertWithNameReferenceNameIndexPrefixLength;
-    literal_name_ = false;
-    state_varint_in_progress = State::kNameIndexOrLengthResume;
-    state_varint_done = State::kNameIndexOrLengthDone;
-  } else if ((data[0] & kInsertWithoutNameReferenceOpcodeMask) ==
-             kInsertWithoutNameReferenceOpcode) {
-    is_huffman_ = (data[0] & kInsertWithoutNameReferenceNameHuffmanBit) ==
-                  kInsertWithoutNameReferenceNameHuffmanBit;
-    prefix_length = kInsertWithoutNameReferenceNameLengthPrefixLength;
-    literal_name_ = true;
-    state_varint_in_progress = State::kNameIndexOrLengthResume;
-    state_varint_done = State::kNameIndexOrLengthDone;
-  } else if ((data[0] & kDuplicateOpcodeMask) == kDuplicateOpcode) {
-    prefix_length = kDuplicateIndexPrefixLength;
-    state_varint_in_progress = State::kIndexResume;
-    state_varint_done = State::kIndexDone;
-  } else {
-    DCHECK_EQ(kDynamicTableSizeUpdateOpcode,
-              data[0] & kDynamicTableSizeUpdateOpcodeMask);
-
-    prefix_length = kDynamicTableSizeUpdateMaxSizePrefixLength;
-    state_varint_in_progress = State::kMaxSizeResume;
-    state_varint_done = State::kMaxSizeDone;
-  }
-
-  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
-  http2::DecodeStatus status =
-      varint_decoder_.Start(data[0], prefix_length, &buffer);
-
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = state_varint_done;
-      break;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK(buffer.Empty());
-      state_ = state_varint_in_progress;
-      break;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("Encoded integer too large.");
-      break;
-  }
-  return 1 + buffer.Offset();
-}
-
-size_t QpackEncoderStreamReceiver::DoNameIndexOrLengthResume(
-    QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  http2::DecodeBuffer buffer(data);
-  http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
-
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kNameIndexOrLengthDone;
-      break;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK(buffer.Empty());
-      break;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("Encoded integer too large.");
-      break;
-  }
-  return buffer.Offset();
-}
-
-void QpackEncoderStreamReceiver::DoNameIndexOrLengthDone() {
-  DCHECK(name_.empty());
-
-  if (literal_name_) {
-    name_length_ = varint_decoder_.value();
-    name_.reserve(name_length_);
-    // Do not handle empty names differently.  (They are probably forbidden by
-    // higher layers, but it is not enforced in this class.)  If there is no
-    // more data to read, then processing stalls, but the instruction is not
-    // complete without the value, so OnInsertWithoutNameReference() could not
-    // be called yet anyway.
-    state_ = State::kReadName;
+  if (data.empty() || error_detected_) {
     return;
   }
 
-  name_index_ = varint_decoder_.value();
-  state_ = State::kValueLengthStart;
+  instruction_decoder_.Decode(data);
 }
 
-size_t QpackEncoderStreamReceiver::DoReadName(QuicStringPiece data) {
-  DCHECK(!data.empty());
-  // |name_length_| might be zero.
-  DCHECK_LE(name_.size(), name_length_);
-
-  size_t bytes_consumed = std::min(name_length_ - name_.size(), data.size());
-  name_.append(data.data(), bytes_consumed);
-
-  DCHECK_LE(name_.size(), name_length_);
-  if (name_.size() == name_length_) {
-    state_ = State::kDecodeName;
+bool QpackEncoderStreamReceiver::OnInstructionDecoded(
+    const QpackInstruction* instruction) {
+  if (instruction == InsertWithNameReferenceInstruction()) {
+    delegate_->OnInsertWithNameReference(instruction_decoder_.is_static(),
+                                         instruction_decoder_.varint(),
+                                         instruction_decoder_.value());
+    return true;
   }
 
-  return bytes_consumed;
-}
-
-void QpackEncoderStreamReceiver::DoDecodeName() {
-  DCHECK_EQ(name_.size(), name_length_);
-
-  if (is_huffman_) {
-    huffman_decoder_.Reset();
-    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
-    QuicString decoded_name;
-    huffman_decoder_.Decode(name_, &decoded_name);
-    if (!huffman_decoder_.InputProperlyTerminated()) {
-      OnError("Error in Huffman-encoded name.");
-      return;
-    }
-    name_ = decoded_name;
+  if (instruction == InsertWithoutNameReferenceInstruction()) {
+    delegate_->OnInsertWithoutNameReference(instruction_decoder_.name(),
+                                            instruction_decoder_.value());
+    return true;
   }
 
-  state_ = State::kValueLengthStart;
-}
-
-size_t QpackEncoderStreamReceiver::DoValueLengthStart(QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  is_huffman_ =
-      (data[0] & kLiteralValueHuffmanMask) == kLiteralValueHuffmanMask;
-
-  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
-  http2::DecodeStatus status =
-      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
-
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kValueLengthDone;
-      break;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK(buffer.Empty());
-      state_ = State::kValueLengthResume;
-      break;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("ValueLen too large.");
-      break;
-  }
-  return 1 + buffer.Offset();
-}
-
-size_t QpackEncoderStreamReceiver::DoValueLengthResume(QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
-  http2::DecodeStatus status =
-      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
-
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kValueLengthDone;
-      break;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK(buffer.Empty());
-      break;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("ValueLen too large.");
-      break;
-  }
-  return 1 + buffer.Offset();
-}
-
-void QpackEncoderStreamReceiver::DoValueLengthDone() {
-  DCHECK(value_.empty());
-
-  value_length_ = varint_decoder_.value();
-
-  // If value is empty, skip DoReadValue() and DoDecodeValue() and jump directly
-  // to DoInsertDone().  This is so that OnInsertName*() is called even if there
-  // is no more data.
-  if (value_length_ == 0) {
-    state_ = State::kInsertDone;
-    return;
+  if (instruction == DuplicateInstruction()) {
+    delegate_->OnDuplicate(instruction_decoder_.varint());
+    return true;
   }
 
-  value_.reserve(value_length_);
-  state_ = State::kReadValue;
-}
-
-size_t QpackEncoderStreamReceiver::DoReadValue(QuicStringPiece data) {
-  DCHECK(!data.empty());
-  DCHECK_LT(0u, value_length_);
-  DCHECK_LT(value_.size(), value_length_);
-
-  size_t bytes_consumed = std::min(value_length_ - value_.size(), data.size());
-  value_.append(data.data(), bytes_consumed);
-
-  DCHECK_LE(value_.size(), value_length_);
-  if (value_.size() == value_length_) {
-    state_ = State::kDecodeValue;
-  }
-
-  return bytes_consumed;
-}
-
-void QpackEncoderStreamReceiver::DoDecodeValue() {
-  DCHECK_EQ(value_.size(), value_length_);
-
-  if (is_huffman_) {
-    huffman_decoder_.Reset();
-    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
-    QuicString decoded_value;
-    huffman_decoder_.Decode(value_, &decoded_value);
-    if (!huffman_decoder_.InputProperlyTerminated()) {
-      OnError("Error in Huffman-encoded value.");
-      return;
-    }
-    value_ = decoded_value;
-  }
-
-  state_ = State::kInsertDone;
-}
-
-void QpackEncoderStreamReceiver::DoInsertDone() {
-  if (literal_name_) {
-    delegate_->OnInsertWithoutNameReference(name_, value_);
-    name_.clear();
-    value_.clear();
-  } else {
-    delegate_->OnInsertWithNameReference(is_static_, name_index_, value_);
-    value_.clear();
-  }
-
-  state_ = State::kStart;
-}
-
-size_t QpackEncoderStreamReceiver::DoIndexResume(QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
-  http2::DecodeStatus status =
-      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
-
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kIndexDone;
-      break;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK(buffer.Empty());
-      break;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("Index too large.");
-      break;
-  }
-  return 1 + buffer.Offset();
-}
-
-void QpackEncoderStreamReceiver::DoIndexDone() {
-  delegate_->OnDuplicate(varint_decoder_.value());
-
-  state_ = State::kStart;
-}
-
-size_t QpackEncoderStreamReceiver::DoMaxSizeResume(QuicStringPiece data) {
-  DCHECK(!data.empty());
-
-  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
-  http2::DecodeStatus status =
-      varint_decoder_.Start(data[0], kLiteralValuePrefixLength, &buffer);
-
-  switch (status) {
-    case http2::DecodeStatus::kDecodeDone:
-      state_ = State::kMaxSizeDone;
-      break;
-    case http2::DecodeStatus::kDecodeInProgress:
-      DCHECK(buffer.Empty());
-      break;
-    case http2::DecodeStatus::kDecodeError:
-      OnError("Maximum table size too large.");
-      break;
-  }
-  return 1 + buffer.Offset();
-}
-
-void QpackEncoderStreamReceiver::DoMaxSizeDone() {
-  delegate_->OnDynamicTableSizeUpdate(varint_decoder_.value());
-
-  state_ = State::kStart;
+  DCHECK_EQ(instruction, DynamicTableSizeUpdateInstruction());
+  delegate_->OnDynamicTableSizeUpdate(instruction_decoder_.varint());
+  return true;
 }
 
 void QpackEncoderStreamReceiver::OnError(QuicStringPiece error_message) {
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
index 55d47440..90db8c8 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver.h
@@ -8,8 +8,7 @@
 #include <cstddef>
 #include <cstdint>
 
-#include "net/third_party/http2/hpack/huffman/hpack_huffman_decoder.h"
-#include "net/third_party/http2/hpack/varint/hpack_varint_decoder.h"
+#include "net/third_party/quic/core/qpack/qpack_instruction_decoder.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
@@ -17,7 +16,8 @@
 namespace quic {
 
 // This class decodes data received on the encoder stream.
-class QUIC_EXPORT_PRIVATE QpackEncoderStreamReceiver {
+class QUIC_EXPORT_PRIVATE QpackEncoderStreamReceiver
+    : public QpackInstructionDecoder::Delegate {
  public:
   // An interface for handling instructions decoded from the encoder stream, see
   // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
@@ -51,83 +51,13 @@
   // and all further data is ignored.
   void Decode(QuicStringPiece data);
 
+  // QpackInstructionDecoder::Delegate implementation.
+  bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+  void OnError(QuicStringPiece error_message) override;
+
  private:
-  enum class State {
-    // Identify the instruction and start decoding an integer.
-    kStart,
-    // Decode name index or name length.
-    kNameIndexOrLengthResume,
-    kNameIndexOrLengthDone,
-    // Read name string literal, which is optionally Huffman encoded.
-    kReadName,
-    // Optionally decode Huffman encoded name.
-    kDecodeName,
-    // Read value string length.
-    kValueLengthStart,
-    kValueLengthResume,
-    kValueLengthDone,
-    // Read value string literal, which is optionally Huffman encoded.
-    kReadValue,
-    // Optionally decode Huffman encoded value.
-    kDecodeValue,
-    // Done with insertion instruction.
-    kInsertDone,
-    // Read index to duplicate.
-    kIndexResume,
-    kIndexDone,
-    // Read maximum table size.
-    kMaxSizeResume,
-    kMaxSizeDone,
-  };
-
-  // One method for each state.  Some take input data and return the number of
-  // octets processed.  Some only change internal state.
-  size_t DoStart(QuicStringPiece data);
-  size_t DoNameIndexOrLengthResume(QuicStringPiece data);
-  void DoNameIndexOrLengthDone();
-  size_t DoReadName(QuicStringPiece data);
-  void DoDecodeName();
-  size_t DoValueLengthStart(QuicStringPiece data);
-  size_t DoValueLengthResume(QuicStringPiece data);
-  void DoValueLengthDone();
-  size_t DoReadValue(QuicStringPiece data);
-  void DoDecodeValue();
-  void DoInsertDone();
-  size_t DoIndexResume(QuicStringPiece data);
-  void DoIndexDone();
-  size_t DoMaxSizeResume(QuicStringPiece data);
-  void DoMaxSizeDone();
-
-  void OnError(QuicStringPiece error_message);
-
+  QpackInstructionDecoder instruction_decoder_;
   Delegate* const delegate_;
-  http2::HpackVarintDecoder varint_decoder_;
-  http2::HpackHuffmanDecoder huffman_decoder_;
-  State state_;
-
-  // True if the currently parsed string (name or value) is Huffman encoded.
-  bool is_huffman_;
-
-  // True if the name index refers to the static table.
-  bool is_static_;
-
-  // True if the header field value is encoded as a string literal.
-  bool literal_name_;
-
-  // Decoded name index.
-  uint64_t name_index_;
-
-  // Decoded length for header name.
-  size_t name_length_;
-
-  // Decoded header name.
-  QuicString name_;
-
-  // Decoded length for header value.
-  size_t value_length_;
-
-  // Decoded header value.
-  QuicString value_;
 
   // True if a decoding error has been detected.
   bool error_detected_;
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
index 16737f570..df86c82 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -72,7 +72,8 @@
 }
 
 TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceValueTooLong) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("ValueLen too large.")));
+  EXPECT_CALL(*delegate(),
+              OnErrorDetected(QuicStringPiece("Encoded integer too large.")));
 
   Decode(QuicTextUtils::HexDecode("c57fffffffffffffffffffff"));
 }
@@ -107,8 +108,11 @@
   Decode(QuicTextUtils::HexDecode("5fffffffffffffffffffff"));
 }
 
-TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReferenceValueTooLong) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("ValueLen too large.")));
+// Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_F(QpackEncoderStreamReceiverTest,
+       InsertWithoutNameReferenceNameExceedsLimit) {
+  EXPECT_CALL(*delegate(),
+              OnErrorDetected(QuicStringPiece("Encoded integer too large.")));
 
   Decode(QuicTextUtils::HexDecode("436261727fffffffffffffffffffff"));
 }
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_test.cc
index 8cc733c..84c85dc 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_test.cc
@@ -124,6 +124,22 @@
   }
 }
 
+TEST_P(QpackEncoderTest, SimpleIndexed) {
+  spdy::SpdyHeaderBlock header_list;
+  header_list[":path"] = "/";
+
+  QpackEncoder encoder;
+  auto progressive_encoder = encoder.EncodeHeaderList(&header_list);
+  EXPECT_TRUE(progressive_encoder->HasNext());
+
+  // This indexed header field takes exactly one byte.
+  QuicString output;
+  progressive_encoder->Next(1, &output);
+
+  EXPECT_EQ(QuicTextUtils::HexDecode("c1"), output);
+  EXPECT_FALSE(progressive_encoder->HasNext());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_instruction_decoder.cc b/net/third_party/quic/core/qpack/qpack_instruction_decoder.cc
new file mode 100644
index 0000000..b86a8a6a
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_instruction_decoder.cc
@@ -0,0 +1,287 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quic/core/qpack/qpack_instruction_decoder.h"
+
+#include "base/logging.h"
+#include "net/third_party/quic/core/qpack/qpack_constants.h"
+
+namespace quic {
+
+QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language,
+                                                 Delegate* delegate)
+    : language_(language),
+      delegate_(delegate),
+      is_static_(false),
+      varint_(0),
+      is_huffman_encoded_(false),
+      string_length_(0),
+      error_detected_(false),
+      state_(State::kStartInstruction) {}
+
+void QpackInstructionDecoder::Decode(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(!error_detected_);
+
+  while (true) {
+    size_t bytes_consumed = 0;
+
+    switch (state_) {
+      case State::kStartInstruction:
+        DoStartInstruction(data);
+        break;
+      case State::kStartField:
+        DoStartField();
+        break;
+      case State::kReadBit:
+        DoReadBit(data);
+        break;
+      case State::kVarintStart:
+        bytes_consumed = DoVarintStart(data);
+        break;
+      case State::kVarintResume:
+        bytes_consumed = DoVarintResume(data);
+        break;
+      case State::kVarintDone:
+        DoVarintDone();
+        break;
+      case State::kReadString:
+        bytes_consumed = DoReadString(data);
+        break;
+      case State::kReadStringDone:
+        DoReadStringDone();
+        break;
+    }
+
+    if (error_detected_) {
+      return;
+    }
+
+    DCHECK_LE(bytes_consumed, data.size());
+
+    data = QuicStringPiece(data.data() + bytes_consumed,
+                           data.size() - bytes_consumed);
+
+    // Stop processing if no more data but next state would require it.
+    if (data.empty() && (state_ != State::kStartField) &&
+        (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) {
+      return;
+    }
+  }
+}
+
+bool QpackInstructionDecoder::AtInstructionBoundary() const {
+  return state_ == State::kStartInstruction;
+}
+
+void QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  instruction_ = LookupOpcode(data[0]);
+  field_ = instruction_->fields.begin();
+
+  state_ = State::kStartField;
+}
+
+void QpackInstructionDecoder::DoStartField() {
+  if (field_ == instruction_->fields.end()) {
+    // Completed decoding this instruction.
+
+    if (!delegate_->OnInstructionDecoded(instruction_)) {
+      error_detected_ = true;
+      return;
+    }
+
+    state_ = State::kStartInstruction;
+    return;
+  }
+
+  switch (field_->type) {
+    case QpackInstructionFieldType::kStaticBit:
+    case QpackInstructionFieldType::kName:
+    case QpackInstructionFieldType::kValue:
+      state_ = State::kReadBit;
+      return;
+    case QpackInstructionFieldType::kVarint:
+      state_ = State::kVarintStart;
+      return;
+  }
+}
+
+void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  switch (field_->type) {
+    case QpackInstructionFieldType::kStaticBit: {
+      const uint8_t bitmask = field_->param;
+      is_static_ = (data[0] & bitmask) == bitmask;
+
+      ++field_;
+      state_ = State::kStartField;
+
+      return;
+    }
+    case QpackInstructionFieldType::kName:
+    case QpackInstructionFieldType::kValue: {
+      const uint8_t prefix_length = field_->param;
+      DCHECK_GE(7, prefix_length);
+      const uint8_t bitmask = 1 << prefix_length;
+      is_huffman_encoded_ = (data[0] & bitmask) == bitmask;
+
+      state_ = State::kVarintStart;
+
+      return;
+    }
+    default:
+      DCHECK(false);
+  }
+}
+
+size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+         field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+  http2::DecodeStatus status =
+      varint_decoder_.Start(data[0], field_->param, &buffer);
+
+  size_t bytes_consumed = 1 + buffer.Offset();
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kVarintDone;
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeInProgress:
+      state_ = State::kVarintResume;
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Encoded integer too large.");
+      return bytes_consumed;
+  }
+}
+
+size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+         field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  http2::DecodeBuffer buffer(data);
+  http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
+
+  size_t bytes_consumed = buffer.Offset();
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kVarintDone;
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK_EQ(bytes_consumed, data.size());
+      DCHECK(buffer.Empty());
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Encoded integer too large.");
+      return bytes_consumed;
+  }
+}
+
+void QpackInstructionDecoder::DoVarintDone() {
+  DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+         field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  if (field_->type == QpackInstructionFieldType::kVarint) {
+    varint_ = varint_decoder_.value();
+
+    ++field_;
+    state_ = State::kStartField;
+    return;
+  }
+
+  string_length_ = varint_decoder_.value();
+  if (string_length_ > kStringLiteralLengthLimit) {
+    OnError("String literal too long.");
+    return;
+  }
+
+  QuicString* const string =
+      (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+  string->clear();
+
+  if (string_length_ == 0) {
+    ++field_;
+    state_ = State::kStartField;
+    return;
+  }
+
+  string->reserve(string_length_);
+
+  state_ = State::kReadString;
+}
+
+size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  QuicString* const string =
+      (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+  DCHECK_LT(string->size(), string_length_);
+
+  size_t bytes_consumed =
+      std::min(string_length_ - string->size(), data.size());
+  string->append(data.data(), bytes_consumed);
+
+  DCHECK_LE(string->size(), string_length_);
+  if (string->size() == string_length_) {
+    state_ = State::kReadStringDone;
+  }
+  return bytes_consumed;
+}
+
+void QpackInstructionDecoder::DoReadStringDone() {
+  DCHECK(field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  QuicString* const string =
+      (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+  DCHECK_EQ(string->size(), string_length_);
+
+  if (is_huffman_encoded_) {
+    huffman_decoder_.Reset();
+    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
+    QuicString decoded_value;
+    huffman_decoder_.Decode(*string, &decoded_value);
+    if (!huffman_decoder_.InputProperlyTerminated()) {
+      OnError("Error in Huffman-encoded string.");
+      return;
+    }
+    *string = std::move(decoded_value);
+  }
+
+  ++field_;
+  state_ = State::kStartField;
+}
+
+const QpackInstruction* QpackInstructionDecoder::LookupOpcode(
+    uint8_t byte) const {
+  for (const auto* instruction : *language_) {
+    if ((byte & instruction->opcode.mask) == instruction->opcode.value) {
+      return instruction;
+    }
+  }
+  // |language_| should be defined such that instruction opcodes cover every
+  // possible input.
+  DCHECK(false);
+  return nullptr;
+}
+
+void QpackInstructionDecoder::OnError(QuicStringPiece error_message) {
+  DCHECK(!error_detected_);
+
+  error_detected_ = true;
+  delegate_->OnError(error_message);
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/qpack/qpack_instruction_decoder.h b/net/third_party/quic/core/qpack/qpack_instruction_decoder.h
new file mode 100644
index 0000000..69c4d85e
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_instruction_decoder.h
@@ -0,0 +1,143 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
+#define NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
+
+#include <cstddef>
+
+#include "net/third_party/http2/hpack/huffman/hpack_huffman_decoder.h"
+#include "net/third_party/http2/hpack/varint/hpack_varint_decoder.h"
+#include "net/third_party/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_string.h"
+#include "net/third_party/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Generic instruction decoder class.  Takes a QpackLanguage that describes a
+// language, that is, a set of instruction opcodes together with a list of
+// fields that follow each instruction.
+class QUIC_EXPORT_PRIVATE QpackInstructionDecoder {
+ public:
+  // Delegate is notified each time an instruction is decoded or when an error
+  // occurs.
+  class QUIC_EXPORT_PRIVATE Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Called when an instruction (including all its fields) is decoded.
+    // |instruction| points to an entry in |language|.
+    // Returns true if decoded fields are valid.
+    // Returns false otherwise, in which case QpackInstructionDecoder stops
+    // decoding: Delegate methods will not be called, and Decode() must not be
+    // called.
+    virtual bool OnInstructionDecoded(const QpackInstruction* instruction) = 0;
+
+    // Called by QpackInstructionDecoder if an error has occurred.
+    // No more data is processed afterwards.
+    virtual void OnError(QuicStringPiece error_message) = 0;
+  };
+
+  // Both |*language| and |*delegate| must outlive this object.
+  QpackInstructionDecoder(const QpackLanguage* language, Delegate* delegate);
+  QpackInstructionDecoder() = delete;
+  QpackInstructionDecoder(const QpackInstructionDecoder&) = delete;
+  QpackInstructionDecoder& operator=(const QpackInstructionDecoder&) = delete;
+
+  // Provide a data fragment to decode.  Must not be called after an error has
+  // occurred.  Must not be called with empty |data|.
+  void Decode(QuicStringPiece data);
+
+  // Returns true if no decoding has taken place yet or if the last instruction
+  // has been entirely parsed.
+  bool AtInstructionBoundary() const;
+
+  // Accessors for decoded values.  Should only be called for fields that are
+  // part of the most recently decoded instruction, and only after |this| calls
+  // Delegate::OnInstructionDecoded() but before Decode() is called again.
+  bool is_static() const { return is_static_; }
+  size_t varint() const { return varint_; }
+  const QuicString& name() const { return name_; }
+  const QuicString& value() const { return value_; }
+
+ private:
+  enum class State {
+    // Identify instruction.
+    kStartInstruction,
+    // Start decoding next field.
+    kStartField,
+    // Read a single bit.
+    kReadBit,
+    // Start reading integer.
+    kVarintStart,
+    // Resume reading integer.
+    kVarintResume,
+    // Done reading integer.
+    kVarintDone,
+    // Read string.
+    kReadString,
+    // Done reading string.
+    kReadStringDone
+  };
+
+  // One method for each state.  Some take input data and return the number of
+  // octets processed.  Some take input data but do have void return type
+  // because they not consume any bytes.  Some do not take any arguments because
+  // they only change internal state.
+  void DoStartInstruction(QuicStringPiece data);
+  void DoStartField();
+  void DoReadBit(QuicStringPiece data);
+  size_t DoVarintStart(QuicStringPiece data);
+  size_t DoVarintResume(QuicStringPiece data);
+  void DoVarintDone();
+  size_t DoReadString(QuicStringPiece data);
+  void DoReadStringDone();
+
+  // Identify instruction based on opcode encoded in |byte|.
+  // Returns a pointer to an element of |*language_|.
+  const QpackInstruction* LookupOpcode(uint8_t byte) const;
+
+  // Stops decoding and calls Delegate::OnError().
+  void OnError(QuicStringPiece error_message);
+
+  // Describes the language used for decoding.
+  const QpackLanguage* const language_;
+
+  // The Delegate to notify of decoded instructions and errors.
+  Delegate* const delegate_;
+
+  // Storage for decoded field values.
+  bool is_static_;
+  size_t varint_;
+  QuicString name_;
+  QuicString value_;
+  // Whether the currently decoded header name or value is Huffman encoded.
+  bool is_huffman_encoded_;
+  // Length of string being read into |name_| or |value_|.
+  size_t string_length_;
+
+  // Decoder instance for decoding integers.
+  http2::HpackVarintDecoder varint_decoder_;
+
+  // Decoder instance for decoding Huffman encoded strings.
+  http2::HpackHuffmanDecoder huffman_decoder_;
+
+  // True if a decoding error has been detected either by
+  // QpackInstructionDecoder or by Delegate.
+  bool error_detected_;
+
+  // Decoding state.
+  State state_;
+
+  // Instruction currently being decoded.
+  const QpackInstruction* instruction_;
+
+  // Field currently being decoded.
+  QpackInstructionFields::const_iterator field_;
+};
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
diff --git a/net/third_party/quic/core/qpack/qpack_instruction_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_instruction_decoder_test.cc
new file mode 100644
index 0000000..4b2e5b8
--- /dev/null
+++ b/net/third_party/quic/core/qpack/qpack_instruction_decoder_test.cc
@@ -0,0 +1,157 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quic/core/qpack/qpack_instruction_decoder.h"
+
+#include "base/logging.h"
+#include "net/third_party/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quic/platform/api/quic_test.h"
+#include "net/third_party/quic/platform/api/quic_text_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Expectation;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+// This instruction has two fields: a static bit and a 6-bit prefix encoded
+// index.
+const QpackInstruction* TestInstruction1() {
+  static const QpackInstruction* const instruction = []() {
+    auto* instruction =
+        new QpackInstruction{QpackInstructionOpcode{0x00, 0x80},
+                             {{QpackInstructionFieldType::kStaticBit, 0x40},
+                              {QpackInstructionFieldType::kVarint, 6}}};
+    return instruction;
+  }();
+  return instruction;
+}
+
+// This instruction has two fields: a header name with a 6-bit prefix, and a
+// header value with a 7-bit prefix, both preceded by a Huffman bit.
+const QpackInstruction* TestInstruction2() {
+  static const QpackInstruction* const instruction = []() {
+    auto* instruction =
+        new QpackInstruction{QpackInstructionOpcode{0x80, 0x80},
+                             {{QpackInstructionFieldType::kName, 6},
+                              {QpackInstructionFieldType::kValue, 7}}};
+    return instruction;
+  }();
+  return instruction;
+}
+
+const QpackLanguage* TestLanguage() {
+  static const QpackLanguage* const language = []() {
+    auto* language = new QpackLanguage{TestInstruction1(), TestInstruction2()};
+    return language;
+  }();
+  return language;
+}
+
+class MockDelegate : public QpackInstructionDecoder::Delegate {
+ public:
+  MockDelegate() {
+    ON_CALL(*this, OnInstructionDecoded(_)).WillByDefault(Return(true));
+  }
+
+  MockDelegate(const MockDelegate&) = delete;
+  MockDelegate& operator=(const MockDelegate&) = delete;
+  ~MockDelegate() override = default;
+
+  MOCK_METHOD1(OnInstructionDecoded, bool(const QpackInstruction* instruction));
+  MOCK_METHOD1(OnError, void(QuicStringPiece error_message));
+};
+
+class QpackInstructionDecoderTest : public QuicTest {
+ public:
+  QpackInstructionDecoderTest() : decoder_(TestLanguage(), &delegate_) {}
+
+ protected:
+  // Decode one full instruction byte by byte.  Verifies that
+  // AtInstructionBoundary() returns true before and after the instruction, and
+  // returns false in while decoding is in progress.
+  void DecodeInstruction(QuicStringPiece data) {
+    for (QuicStringPiece::size_type i = 0; i < data.size(); ++i) {
+      if (i == 0) {
+        EXPECT_TRUE(decoder_.AtInstructionBoundary());
+      } else {
+        EXPECT_FALSE(decoder_.AtInstructionBoundary());
+      }
+      decoder_.Decode(data.substr(i, 1));
+    }
+    EXPECT_TRUE(decoder_.AtInstructionBoundary());
+  }
+
+  StrictMock<MockDelegate> delegate_;
+  QpackInstructionDecoder decoder_;
+};
+
+TEST_F(QpackInstructionDecoderTest, StaticBitAndIndex) {
+  EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
+  DecodeInstruction(QuicTextUtils::HexDecode("7f01"));
+
+  EXPECT_TRUE(decoder_.is_static());
+  EXPECT_EQ(64u, decoder_.varint());
+
+  EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
+  DecodeInstruction(QuicTextUtils::HexDecode("05"));
+
+  EXPECT_FALSE(decoder_.is_static());
+  EXPECT_EQ(5u, decoder_.varint());
+}
+
+TEST_F(QpackInstructionDecoderTest, NameAndValue) {
+  EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+  DecodeInstruction(QuicTextUtils::HexDecode("83666f6f03626172"));
+
+  EXPECT_EQ("foo", decoder_.name());
+  EXPECT_EQ("bar", decoder_.value());
+
+  EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+  DecodeInstruction(QuicTextUtils::HexDecode("8000"));
+
+  EXPECT_EQ("", decoder_.name());
+  EXPECT_EQ("", decoder_.value());
+
+  EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+  DecodeInstruction(QuicTextUtils::HexDecode("c294e7838c767f"));
+
+  EXPECT_EQ("foo", decoder_.name());
+  EXPECT_EQ("bar", decoder_.value());
+}
+
+TEST_F(QpackInstructionDecoderTest, InvalidHuffmanEncoding) {
+  EXPECT_CALL(delegate_,
+              OnError(QuicStringPiece("Error in Huffman-encoded string.")));
+  decoder_.Decode(QuicTextUtils::HexDecode("c1ff"));
+}
+
+TEST_F(QpackInstructionDecoderTest, InvalidVarintEncoding) {
+  EXPECT_CALL(delegate_,
+              OnError(QuicStringPiece("Encoded integer too large.")));
+  decoder_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
+}
+
+TEST_F(QpackInstructionDecoderTest, DelegateSignalsError) {
+  // First instruction is valid.
+  Expectation first_call =
+      EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
+          .WillOnce(Return(true));
+  // Second instruction is invalid.  Decoding must halt.
+  EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
+      .After(first_call)
+      .WillOnce(Return(false));
+  decoder_.Decode(QuicTextUtils::HexDecode("0102030405"));
+
+  EXPECT_EQ(2u, decoder_.varint());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace quic
diff --git a/net/third_party/quic/core/quic_buffered_packet_store.cc b/net/third_party/quic/core/quic_buffered_packet_store.cc
index 78fffcbf..04b5eec 100644
--- a/net/third_party/quic/core/quic_buffered_packet_store.cc
+++ b/net/third_party/quic/core/quic_buffered_packet_store.cc
@@ -106,6 +106,7 @@
     undecryptable_packets_.emplace(
         std::make_pair(connection_id, BufferedPacketList()));
     undecryptable_packets_.back().second.ietf_quic = ietf_quic;
+    undecryptable_packets_.back().second.version = version;
   }
   CHECK(QuicContainsKey(undecryptable_packets_, connection_id));
   BufferedPacketList& queue =
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index 39968dc..5869ee7 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -346,8 +346,6 @@
       supports_release_time_(writer->SupportsReleaseTime()),
       release_time_into_future_(QuicTime::Delta::Zero()),
       donot_retransmit_old_window_updates_(false),
-      deprecate_post_process_after_data_(
-          GetQuicReloadableFlag(quic_deprecate_post_process_after_data)),
       no_version_negotiation_(supported_versions.size() == 1),
       decrypt_packets_on_key_change_(
           GetQuicReloadableFlag(quic_decrypt_packets_on_key_change)) {
@@ -901,9 +899,6 @@
     return false;
   }
   visitor_->OnStreamFrame(frame);
-  if (!deprecate_post_process_after_data_) {
-    visitor_->PostProcessAfterData();
-  }
   stats_.stream_bytes_received += frame.data_length;
   should_last_packet_instigate_acks_ = true;
   return connected_;
@@ -1153,9 +1148,6 @@
                   << " with error: "
                   << QuicRstStreamErrorCodeToString(frame.error_code);
   visitor_->OnRstStream(frame);
-  if (!deprecate_post_process_after_data_) {
-    visitor_->PostProcessAfterData();
-  }
   should_last_packet_instigate_acks_ = true;
   return connected_;
 }
@@ -1249,9 +1241,6 @@
                   << " and reason: " << frame.reason_phrase;
 
   visitor_->OnGoAway(frame);
-  if (!deprecate_post_process_after_data_) {
-    visitor_->PostProcessAfterData();
-  }
   should_last_packet_instigate_acks_ = true;
   return connected_;
 }
@@ -1270,9 +1259,6 @@
                   << frame.stream_id
                   << " with byte offset: " << frame.byte_offset;
   visitor_->OnWindowUpdateFrame(frame);
-  if (!deprecate_post_process_after_data_) {
-    visitor_->PostProcessAfterData();
-  }
   should_last_packet_instigate_acks_ = true;
   return connected_;
 }
@@ -1319,9 +1305,6 @@
   QUIC_DLOG(INFO) << ENDPOINT
                   << "BLOCKED_FRAME received for stream: " << frame.stream_id;
   visitor_->OnBlockedFrame(frame);
-  if (!deprecate_post_process_after_data_) {
-    visitor_->PostProcessAfterData();
-  }
   stats_.blocked_frames_received++;
   should_last_packet_instigate_acks_ = true;
   return connected_;
@@ -1826,9 +1809,6 @@
   {
     ScopedPacketFlusher flusher(this, SEND_ACK_IF_QUEUED);
     visitor_->OnCanWrite();
-    if (!deprecate_post_process_after_data_) {
-      visitor_->PostProcessAfterData();
-    }
   }
 
   // After the visitor writes, it may have caused the socket to become write
@@ -2866,9 +2846,7 @@
 }
 
 void QuicConnection::SetPathDegradingAlarm() {
-  if (GetQuicReloadableFlag(quic_fix_path_degrading_alarm) &&
-      perspective_ == Perspective::IS_SERVER) {
-    QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_fix_path_degrading_alarm, 1, 2);
+  if (perspective_ == Perspective::IS_SERVER) {
     return;
   }
   const QuicTime::Delta delay = sent_packet_manager_.GetPathDegradingDelay();
@@ -3399,13 +3377,7 @@
 }
 
 void QuicConnection::MaybeSetPathDegradingAlarm(bool acked_new_packet) {
-  bool has_unacked_packets = !sent_packet_manager_.unacked_packets().empty();
-  if (GetQuicReloadableFlag(quic_fix_path_degrading_alarm)) {
-    QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_fix_path_degrading_alarm, 2, 2);
-    // If there in flight packets, an ACK is expected in the future.
-    has_unacked_packets = sent_packet_manager_.HasInFlightPackets();
-  }
-  if (!has_unacked_packets) {
+  if (!sent_packet_manager_.HasInFlightPackets()) {
     // There are no retransmittable packets on the wire, so it's impossible to
     // say if the connection has degraded.
     path_degrading_alarm_->Cancel();
diff --git a/net/third_party/quic/core/quic_connection.h b/net/third_party/quic/core/quic_connection.h
index 2ee6d7f..1f8fd84 100644
--- a/net/third_party/quic/core/quic_connection.h
+++ b/net/third_party/quic/core/quic_connection.h
@@ -150,11 +150,6 @@
   // Called when the peer seems unreachable over the current path.
   virtual void OnPathDegrading() = 0;
 
-  // Called after OnStreamFrame, OnRstStream, OnGoAway, OnWindowUpdateFrame,
-  // OnBlockedFrame, and OnCanWrite to allow post-processing once the work has
-  // been done.
-  virtual void PostProcessAfterData() = 0;
-
   // Called when the connection sends ack after
   // max_consecutive_num_packets_with_no_retransmittable_frames_ consecutive not
   // retransmittable packets sent. To instigate an ack from peer, a
@@ -816,10 +811,6 @@
     donot_retransmit_old_window_updates_ = value;
   }
 
-  bool deprecate_post_process_after_data() const {
-    return deprecate_post_process_after_data_;
-  }
-
   // Attempts to process any queued undecryptable packets.
   void MaybeProcessUndecryptablePackets();
 
@@ -1364,9 +1355,6 @@
   // quic_reloadable_flag_quic_donot_retransmit_old_window_update.
   bool donot_retransmit_old_window_updates_;
 
-  // Latched value of quic_reloadable_flag_quic_move_post_process_after_data.
-  const bool deprecate_post_process_after_data_;
-
   // Indicates whether server connection does version negotiation. Server
   // connection does not support version negotiation if a single version is
   // provided in constructor.
diff --git a/net/third_party/quic/core/quic_connection_test.cc b/net/third_party/quic/core/quic_connection_test.cc
index be4bbe8e..88aaf32 100644
--- a/net/third_party/quic/core/quic_connection_test.cc
+++ b/net/third_party/quic/core/quic_connection_test.cc
@@ -51,6 +51,7 @@
 using testing::DoAll;
 using testing::Exactly;
 using testing::Ge;
+using testing::IgnoreResult;
 using testing::InSequence;
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
@@ -867,7 +868,6 @@
     } else {
       EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber());
     }
-    EXPECT_CALL(visitor_, PostProcessAfterData()).Times(AnyNumber());
     EXPECT_CALL(visitor_, HasOpenDynamicStreams())
         .WillRepeatedly(Return(false));
     EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber());
@@ -6890,7 +6890,6 @@
 }
 
 TEST_P(QuicConnectionTest, NoPathDegradingOnServer) {
-  SetQuicReloadableFlag(quic_fix_path_degrading_alarm, true);
   set_perspective(Perspective::IS_SERVER);
   QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
 
@@ -6914,8 +6913,6 @@
 }
 
 TEST_P(QuicConnectionTest, NoPathDegradingAfterSendingAck) {
-  SetQuicReloadableFlag(quic_fix_path_degrading_alarm, true);
-
   SendAckPacketToPeer();
   EXPECT_FALSE(connection_.sent_packet_manager().unacked_packets().empty());
   EXPECT_FALSE(connection_.sent_packet_manager().HasInFlightPackets());
diff --git a/net/third_party/quic/core/quic_constants.h b/net/third_party/quic/core/quic_constants.h
index a91c72b..1012ac2 100644
--- a/net/third_party/quic/core/quic_constants.h
+++ b/net/third_party/quic/core/quic_constants.h
@@ -45,6 +45,9 @@
 // Used in QUIC for congestion window computations in bytes.
 const QuicByteCount kDefaultTCPMSS = 1460;
 const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS;
+// The minimum size of a packet which can elicit a version negotiation packet,
+// as per section 8.1 of the QUIC spec.
+const QuicByteCount kMinPacketSizeForVersionNegotiation = 1200;
 
 // We match SPDY's use of 32 (since we'd compete with SPDY).
 const QuicPacketCount kInitialCongestionWindow = 32;
@@ -200,6 +203,7 @@
 const uint64_t kMaxIetfVarInt = UINT64_C(0x3fffffffffffffff);
 
 // The maximum stream id value that is supported - (2^32)-1
+// TODO(fkastenholz): Should update this to 64 bits for IETF Quic.
 const QuicStreamId kMaxQuicStreamId = 0xffffffff;
 
 // Number of bytes reserved for packet header type.
@@ -224,10 +228,6 @@
 QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd;
 QUIC_EXPORT_PRIVATE extern const char* const kEPIDGoogleFrontEnd0;
 
-// Maximum Stream ID value that the implementation supports
-// TODO(fkastenholz): Should update this to 64 bits for IETF Quic.
-const QuicStreamId kMaximumStreamIdSupported = 0xffffffff;
-
 }  // namespace quic
 
 #endif  // NET_THIRD_PARTY_QUIC_CORE_QUIC_CONSTANTS_H_
diff --git a/net/third_party/quic/core/quic_crypto_client_handshaker.h b/net/third_party/quic/core/quic_crypto_client_handshaker.h
index bfb789b..26c68a47 100644
--- a/net/third_party/quic/core/quic_crypto_client_handshaker.h
+++ b/net/third_party/quic/core/quic_crypto_client_handshaker.h
@@ -51,6 +51,13 @@
   // From QuicCryptoHandshaker
   void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;
 
+ protected:
+  // Returns the QuicSession that this stream belongs to.
+  QuicSession* session() const { return session_; }
+
+  // Send either InchoateClientHello or ClientHello message to the server.
+  void DoSendCHLO(QuicCryptoClientConfig::CachedState* cached);
+
  private:
   // ChannelIDSourceCallbackImpl is passed as the callback method to
   // GetChannelIDKey. The ChannelIDSource calls this class with the result of
@@ -118,9 +125,6 @@
   // Start the handshake process.
   void DoInitialize(QuicCryptoClientConfig::CachedState* cached);
 
-  // Send either InchoateClientHello or ClientHello message to the server.
-  void DoSendCHLO(QuicCryptoClientConfig::CachedState* cached);
-
   // Process REJ message from the server.
   void DoReceiveREJ(const CryptoHandshakeMessage* in,
                     QuicCryptoClientConfig::CachedState* cached);
@@ -159,9 +163,6 @@
   // and the client config settings also allow sending a ChannelID.
   bool RequiresChannelID(QuicCryptoClientConfig::CachedState* cached);
 
-  // Returns the QuicSession that this stream belongs to.
-  QuicSession* session() const { return session_; }
-
   QuicCryptoClientStream* stream_;
 
   QuicSession* session_;
diff --git a/net/third_party/quic/core/quic_crypto_client_stream.h b/net/third_party/quic/core/quic_crypto_client_stream.h
index b04047de8..215b6c2b 100644
--- a/net/third_party/quic/core/quic_crypto_client_stream.h
+++ b/net/third_party/quic/core/quic_crypto_client_stream.h
@@ -16,6 +16,7 @@
 #include "net/third_party/quic/core/quic_crypto_handshaker.h"
 #include "net/third_party/quic/core/quic_crypto_stream.h"
 #include "net/third_party/quic/core/quic_server_id.h"
+#include "net/third_party/quic/core/quic_session.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 
@@ -162,6 +163,11 @@
 
   QuicString chlo_hash() const;
 
+ protected:
+  void set_handshaker(std::unique_ptr<HandshakerDelegate> handshaker) {
+    handshaker_ = std::move(handshaker);
+  }
+
  private:
   std::unique_ptr<HandshakerDelegate> handshaker_;
 };
diff --git a/net/third_party/quic/core/quic_crypto_server_handshaker.h b/net/third_party/quic/core/quic_crypto_server_handshaker.h
index 17a3be6d..92f0bd6 100644
--- a/net/third_party/quic/core/quic_crypto_server_handshaker.h
+++ b/net/third_party/quic/core/quic_crypto_server_handshaker.h
@@ -76,6 +76,17 @@
   // Returns client address used to generate and validate source address token.
   virtual const QuicSocketAddress GetClientAddress();
 
+  // Returns the QuicSession that this stream belongs to.
+  QuicSession* session() const { return session_; }
+
+  void set_encryption_established(bool encryption_established) {
+    encryption_established_ = encryption_established;
+  }
+
+  void set_handshake_confirmed(bool handshake_confirmed) {
+    handshake_confirmed_ = handshake_confirmed;
+  }
+
  private:
   friend class test::QuicCryptoServerStreamPeer;
 
@@ -145,9 +156,6 @@
   // if |use_stateless_rejects| is true. Returns 0 otherwise.
   QuicConnectionId GenerateConnectionIdForReject(bool use_stateless_rejects);
 
-  // Returns the QuicSession that this stream belongs to.
-  QuicSession* session() const { return session_; }
-
   // Returns the QuicTransportVersion of the connection.
   QuicTransportVersion transport_version() const {
     return session_->connection()->transport_version();
diff --git a/net/third_party/quic/core/quic_dispatcher.cc b/net/third_party/quic/core/quic_dispatcher.cc
index ef32d29..84a4c91 100644
--- a/net/third_party/quic/core/quic_dispatcher.cc
+++ b/net/third_party/quic/core/quic_dispatcher.cc
@@ -11,6 +11,7 @@
 #include "net/third_party/quic/core/crypto/crypto_protocol.h"
 #include "net/third_party/quic/core/crypto/quic_random.h"
 #include "net/third_party/quic/core/quic_time_wait_list_manager.h"
+#include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/core/stateless_rejector.h"
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
@@ -346,7 +347,8 @@
   }
 
   if (buffered_packets_.HasChloForConnection(connection_id)) {
-    BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET);
+    BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET,
+                      header.version);
     return false;
   }
 
@@ -355,7 +357,8 @@
       temporarily_buffered_connections_.end()) {
     // This packet was received while the a CHLO for the same connection ID was
     // being processed.  Buffer it.
-    BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET);
+    BufferEarlyPacket(connection_id, header.form != GOOGLE_QUIC_PACKET,
+                      header.version);
     return false;
   }
 
@@ -392,11 +395,17 @@
       if (ShouldCreateSessionForUnknownVersion(framer_.last_version_label())) {
         return true;
       }
-      // Since the version is not supported, send a version negotiation
-      // packet and stop processing the current packet.
-      time_wait_list_manager()->SendVersionNegotiationPacket(
-          connection_id, header.form != GOOGLE_QUIC_PACKET,
-          GetSupportedVersions(), current_self_address_, current_peer_address_);
+      if (!GetQuicReloadableFlag(quic_limit_version_negotiation) ||
+          current_packet_->length() >= kMinPacketSizeForVersionNegotiation) {
+        // Since the version is not supported, send a version negotiation
+        // packet and stop processing the current packet.
+        time_wait_list_manager()->SendVersionNegotiationPacket(
+            connection_id, header.form != GOOGLE_QUIC_PACKET,
+            GetSupportedVersions(), current_self_address_,
+            current_peer_address_);
+      } else {
+        QUIC_FLAG_COUNT(quic_reloadable_flag_quic_limit_version_negotiation);
+      }
       return false;
     }
     version = packet_version;
@@ -413,11 +422,12 @@
   if (fate == kFateProcess) {
     // Execute stateless rejection logic to determine the packet fate, then
     // invoke ProcessUnauthenticatedHeaderFate.
-    MaybeRejectStatelessly(connection_id, header.version, header.form);
+    MaybeRejectStatelessly(connection_id, header.form, header.version);
   } else {
     // If the fate is already known, process it without executing stateless
     // rejection logic.
-    ProcessUnauthenticatedHeaderFate(fate, connection_id, header.form);
+    ProcessUnauthenticatedHeaderFate(fate, connection_id, header.form,
+                                     header.version);
   }
 
   return false;
@@ -426,10 +436,11 @@
 void QuicDispatcher::ProcessUnauthenticatedHeaderFate(
     QuicPacketFate fate,
     QuicConnectionId connection_id,
-    PacketHeaderFormat form) {
+    PacketHeaderFormat form,
+    ParsedQuicVersion version) {
   switch (fate) {
     case kFateProcess: {
-      ProcessChlo(form);
+      ProcessChlo(form, version);
       break;
     }
     case kFateTimeWait:
@@ -441,8 +452,10 @@
         // future packets.
         QUIC_DLOG(INFO) << "Adding connection ID " << connection_id
                         << "to time-wait list.";
+        QUIC_CODE_COUNT(quic_reject_fate_time_wait);
         StatelesslyTerminateConnection(
-            connection_id, form, QUIC_HANDSHAKE_FAILED, "Reject connection",
+            connection_id, form, version, QUIC_HANDSHAKE_FAILED,
+            "Reject connection",
             quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
       }
       DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
@@ -459,7 +472,7 @@
       // This packet is a non-CHLO packet which has arrived before the
       // corresponding CHLO, *or* this packet was received while the
       // corresponding CHLO was being processed.  Buffer it.
-      BufferEarlyPacket(connection_id, form != GOOGLE_QUIC_PACKET);
+      BufferEarlyPacket(connection_id, form != GOOGLE_QUIC_PACKET, version);
       break;
     case kFateDrop:
       // Do nothing with the packet.
@@ -642,9 +655,63 @@
 void QuicDispatcher::StatelesslyTerminateConnection(
     QuicConnectionId connection_id,
     PacketHeaderFormat format,
+    ParsedQuicVersion version,
     QuicErrorCode error_code,
     const QuicString& error_details,
     QuicTimeWaitListManager::TimeWaitAction action) {
+  if (GetQuicReloadableFlag(quic_fix_reject_by_session_type)) {
+    if (format != IETF_QUIC_LONG_HEADER_PACKET) {
+      QUIC_DVLOG(1) << "Statelessly terminating " << connection_id
+                    << " based on a non-ietf-long packet, action:" << action
+                    << ", error_code:" << error_code
+                    << ", error_details:" << error_details;
+      time_wait_list_manager_->AddConnectionIdToTimeWait(
+          connection_id, format != GOOGLE_QUIC_PACKET, action,
+          /*termination_packets=*/nullptr);
+      return;
+    }
+
+    // If the version is known and supported by framer, send a connection close.
+    if (framer_.IsSupportedVersion(version)) {
+      QUIC_DVLOG(1)
+          << "Statelessly terminating " << connection_id
+          << " based on an ietf-long packet, which has a supported version:"
+          << version << ", error_code:" << error_code
+          << ", error_details:" << error_details;
+      // Set framer_ to the packet's version such that the connection close can
+      // be processed by the client.
+      ParsedQuicVersion original_version = framer_.version();
+      framer_.set_version(version);
+
+      StatelessConnectionTerminator terminator(connection_id, &framer_,
+                                               helper_.get(),
+                                               time_wait_list_manager_.get());
+      // This also adds the connection to time wait list.
+      terminator.CloseConnection(error_code, error_details, true);
+
+      // Restore framer_ to the original version, as if nothing changed in it.
+      framer_.set_version(original_version);
+      return;
+    }
+
+    QUIC_DVLOG(1)
+        << "Statelessly terminating " << connection_id
+        << " based on an ietf-long packet, which has an unsupported version:"
+        << version << ", error_code:" << error_code
+        << ", error_details:" << error_details;
+    // Version is unknown or unsupported by framer, send a version negotiation
+    // with an empty version list, which can be understood by the client.
+    std::vector<std::unique_ptr<QuicEncryptedPacket>> termination_packets;
+    termination_packets.push_back(QuicFramer::BuildVersionNegotiationPacket(
+        connection_id, /*ietf_quic=*/true,
+        ParsedQuicVersionVector{UnsupportedQuicVersion()}));
+    time_wait_list_manager()->AddConnectionIdToTimeWait(
+        connection_id, /*ietf_quic=*/true,
+        QuicTimeWaitListManager::SEND_TERMINATION_PACKETS,
+        &termination_packets);
+    return;
+  }
+
   if (format == IETF_QUIC_LONG_HEADER_PACKET) {
     // Send connection close for IETF long header packet, and this also adds
     // connection to time wait list.
@@ -852,11 +919,13 @@
 void QuicDispatcher::OnExpiredPackets(
     QuicConnectionId connection_id,
     BufferedPacketList early_arrived_packets) {
+  QUIC_CODE_COUNT(quic_reject_buffered_packets_expired);
   StatelesslyTerminateConnection(
       connection_id,
       early_arrived_packets.ietf_quic ? IETF_QUIC_LONG_HEADER_PACKET
                                       : GOOGLE_QUIC_PACKET,
-      QUIC_HANDSHAKE_FAILED, "Packets buffered for too long",
+      early_arrived_packets.version, QUIC_HANDSHAKE_FAILED,
+      "Packets buffered for too long",
       quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
 }
 
@@ -917,27 +986,33 @@
 }
 
 void QuicDispatcher::BufferEarlyPacket(QuicConnectionId connection_id,
-                                       bool ietf_quic) {
+                                       bool ietf_quic,
+                                       ParsedQuicVersion version) {
   bool is_new_connection = !buffered_packets_.HasBufferedPackets(connection_id);
   if (is_new_connection &&
       !ShouldCreateOrBufferPacketForConnection(connection_id, ietf_quic)) {
     return;
   }
-  ParsedQuicVersion version(PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED);
+
   EnqueuePacketResult rs = buffered_packets_.EnqueuePacket(
       connection_id, ietf_quic, *current_packet_, current_self_address_,
       current_peer_address_, /*is_chlo=*/false,
-      /*alpn=*/"", version);
+      /*alpn=*/"",
+      GetQuicReloadableFlag(quic_fix_reject_by_session_type)
+          ? version
+          : UnsupportedQuicVersion());
   if (rs != EnqueuePacketResult::SUCCESS) {
     OnBufferPacketFailure(rs, connection_id);
   }
 }
 
-void QuicDispatcher::ProcessChlo(quic::PacketHeaderFormat form) {
+void QuicDispatcher::ProcessChlo(PacketHeaderFormat form,
+                                 ParsedQuicVersion version) {
   if (!accept_new_connections_) {
     // Don't any create new connection.
+    QUIC_CODE_COUNT(quic_reject_stop_accepting_new_connections);
     StatelesslyTerminateConnection(
-        current_connection_id(), form, QUIC_HANDSHAKE_FAILED,
+        current_connection_id(), form, version, QUIC_HANDSHAKE_FAILED,
         "Stop accepting new connections",
         quic::QuicTimeWaitListManager::SEND_STATELESS_RESET);
     // Time wait list will reject the packet correspondingly.
@@ -1040,10 +1115,12 @@
 };
 
 void QuicDispatcher::MaybeRejectStatelessly(QuicConnectionId connection_id,
-                                            ParsedQuicVersion version,
-                                            PacketHeaderFormat form) {
+
+                                            PacketHeaderFormat form,
+                                            ParsedQuicVersion version) {
   if (version.handshake_protocol == PROTOCOL_TLS1_3) {
-    ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form);
+    ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form,
+                                     version);
     return;
     // TODO(nharper): Support buffering non-ClientHello packets when using TLS.
   }
@@ -1059,11 +1136,13 @@
                                 config_.create_session_tag_indicators(),
                                 &alpn_extractor)) {
       // Buffer non-CHLO packets.
-      ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form);
+      ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form,
+                                       version);
       return;
     }
     current_alpn_ = alpn_extractor.ConsumeAlpn();
-    ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form);
+    ProcessUnauthenticatedHeaderFate(kFateProcess, connection_id, form,
+                                     version);
     return;
   }
 
@@ -1078,19 +1157,21 @@
   if (!ChloExtractor::Extract(*current_packet_, GetSupportedVersions(),
                               config_.create_session_tag_indicators(),
                               &validator)) {
-    ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form);
+    ProcessUnauthenticatedHeaderFate(kFateBuffer, connection_id, form, version);
     return;
   }
   current_alpn_ = validator.ConsumeAlpn();
 
   if (!validator.can_accept()) {
     // This CHLO is prohibited by policy.
+    QUIC_CODE_COUNT(quic_reject_cant_accept_chlo);
     StatelessConnectionTerminator terminator(connection_id, &framer_, helper(),
                                              time_wait_list_manager_.get());
     terminator.CloseConnection(QUIC_HANDSHAKE_FAILED, validator.error_details(),
                                form != GOOGLE_QUIC_PACKET);
     OnConnectionClosedStatelessly(QUIC_HANDSHAKE_FAILED);
-    ProcessUnauthenticatedHeaderFate(kFateTimeWait, connection_id, form);
+    ProcessUnauthenticatedHeaderFate(kFateTimeWait, connection_id, form,
+                                     version);
     return;
   }
 
@@ -1167,6 +1248,7 @@
   switch (rejector->state()) {
     case StatelessRejector::FAILED: {
       // There was an error processing the client hello.
+      QUIC_CODE_COUNT(quic_reject_error_processing_chlo);
       StatelessConnectionTerminator terminator(rejector->connection_id(),
                                                &framer_, helper(),
                                                time_wait_list_manager_.get());
@@ -1207,7 +1289,8 @@
       fate = kFateDrop;
       break;
   }
-  ProcessUnauthenticatedHeaderFate(fate, rejector->connection_id(), form);
+  ProcessUnauthenticatedHeaderFate(fate, rejector->connection_id(), form,
+                                   rejector->version());
 }
 
 const QuicTransportVersionVector&
diff --git a/net/third_party/quic/core/quic_dispatcher.h b/net/third_party/quic/core/quic_dispatcher.h
index 1acb05e..c8bf492 100644
--- a/net/third_party/quic/core/quic_dispatcher.h
+++ b/net/third_party/quic/core/quic_dispatcher.h
@@ -226,11 +226,13 @@
 
   // Called when |connection_id| doesn't have an open connection yet, to buffer
   // |current_packet_| until it can be delivered to the connection.
-  void BufferEarlyPacket(QuicConnectionId connection_id, bool ietf_quic);
+  void BufferEarlyPacket(QuicConnectionId connection_id,
+                         bool ietf_quic,
+                         ParsedQuicVersion version);
 
   // Called when |current_packet_| is a CHLO packet. Creates a new connection
   // and delivers any buffered packets for that connection id.
-  void ProcessChlo(PacketHeaderFormat form);
+  void ProcessChlo(PacketHeaderFormat form, ParsedQuicVersion version);
 
   // Returns the actual client address of the current packet.
   // This function should only be called once per packet at the very beginning
@@ -332,6 +334,7 @@
   void StatelesslyTerminateConnection(
       QuicConnectionId connection_id,
       PacketHeaderFormat format,
+      ParsedQuicVersion version,
       QuicErrorCode error_code,
       const QuicString& error_details,
       QuicTimeWaitListManager::TimeWaitAction action);
@@ -360,8 +363,8 @@
   // fate which describes what subsequent processing should be performed on the
   // packets, like ValidityChecks, and invokes ProcessUnauthenticatedHeaderFate.
   void MaybeRejectStatelessly(QuicConnectionId connection_id,
-                              ParsedQuicVersion version,
-                              PacketHeaderFormat form);
+                              PacketHeaderFormat form,
+                              ParsedQuicVersion version);
 
   // Deliver |packets| to |session| for further processing.
   void DeliverPacketsToSession(
@@ -372,7 +375,8 @@
   // either process, buffer, or drop it.
   void ProcessUnauthenticatedHeaderFate(QuicPacketFate fate,
                                         QuicConnectionId connection_id,
-                                        PacketHeaderFormat form);
+                                        PacketHeaderFormat form,
+                                        ParsedQuicVersion version);
 
   // Invoked when StatelessRejector::Process completes. |first_version| is the
   // version of the packet which initiated the stateless reject.
diff --git a/net/third_party/quic/core/quic_dispatcher_test.cc b/net/third_party/quic/core/quic_dispatcher_test.cc
index cb969816..be07953 100644
--- a/net/third_party/quic/core/quic_dispatcher_test.cc
+++ b/net/third_party/quic/core/quic_dispatcher_test.cc
@@ -462,12 +462,15 @@
 }
 
 TEST_F(QuicDispatcherTest, StatelessVersionNegotiation) {
+  CreateTimeWaitListManager();
+  SetQuicReloadableFlag(quic_limit_version_negotiation, false);
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
   server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
 
-  EXPECT_CALL(*dispatcher_,
-              CreateQuicSession(1, client_address, QuicStringPiece("hq"), _))
-      .Times(0);
+  EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_,
+              SendVersionNegotiationPacket(_, _, _, _, _))
+      .Times(1);
   QuicTransportVersion version =
       static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1);
   ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version);
@@ -475,6 +478,27 @@
                 PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER, 1);
 }
 
+TEST_F(QuicDispatcherTest, NoVersionNegotiationWithSmallPacket) {
+  CreateTimeWaitListManager();
+  SetQuicReloadableFlag(quic_limit_version_negotiation, true);
+  QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
+  server_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 5);
+
+  EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_,
+              SendVersionNegotiationPacket(_, _, _, _, _))
+      .Times(0);
+  QuicTransportVersion version =
+      static_cast<QuicTransportVersion>(QuicTransportVersionMin() - 1);
+  ParsedQuicVersion parsed_version(PROTOCOL_QUIC_CRYPTO, version);
+  QuicString chlo = SerializeCHLO();
+  // Truncate to 1100 bytes of payload which results in a packet just
+  // under 1200 bytes after framing, packet, and encryption overhead.
+  QuicString truncated_chlo = chlo.substr(0, 1100);
+  ProcessPacket(client_address, 1, true, parsed_version, truncated_chlo,
+                PACKET_8BYTE_CONNECTION_ID, PACKET_4BYTE_PACKET_NUMBER, 1);
+}
+
 TEST_F(QuicDispatcherTest, Shutdown) {
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
 
diff --git a/net/third_party/quic/core/quic_flow_controller.h b/net/third_party/quic/core/quic_flow_controller.h
index fc5deab..0dfd253 100644
--- a/net/third_party/quic/core/quic_flow_controller.h
+++ b/net/third_party/quic/core/quic_flow_controller.h
@@ -49,6 +49,7 @@
                      bool should_auto_tune_receive_window,
                      QuicFlowControllerInterface* session_flow_controller);
   QuicFlowController(const QuicFlowController&) = delete;
+  QuicFlowController(QuicFlowController&&) = default;
   QuicFlowController& operator=(const QuicFlowController&) = delete;
 
   ~QuicFlowController() override {}
diff --git a/net/third_party/quic/core/quic_flow_controller_test.cc b/net/third_party/quic/core/quic_flow_controller_test.cc
index b59fac99..c00fc5da 100644
--- a/net/third_party/quic/core/quic_flow_controller_test.cc
+++ b/net/third_party/quic/core/quic_flow_controller_test.cc
@@ -130,6 +130,26 @@
             QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
 }
 
+TEST_F(QuicFlowControllerTest, Move) {
+  Initialize();
+
+  flow_controller_->AddBytesSent(send_window_ / 2);
+  EXPECT_FALSE(flow_controller_->IsBlocked());
+  EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize());
+
+  EXPECT_TRUE(
+      flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2));
+  EXPECT_FALSE(flow_controller_->FlowControlViolation());
+  EXPECT_EQ((receive_window_ / 2) - 1,
+            QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+  QuicFlowController flow_controller2(std::move(*flow_controller_));
+  EXPECT_EQ(send_window_ / 2, flow_controller2.SendWindowSize());
+  EXPECT_FALSE(flow_controller2.FlowControlViolation());
+  EXPECT_EQ((receive_window_ / 2) - 1,
+            QuicFlowControllerPeer::ReceiveWindowSize(&flow_controller2));
+}
+
 TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) {
   Initialize();
 
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc
index eb2d698..54b50359d 100644
--- a/net/third_party/quic/core/quic_framer.cc
+++ b/net/third_party/quic/core/quic_framer.cc
@@ -3236,7 +3236,7 @@
                                      bool no_message_length,
                                      QuicMessageFrame* frame) {
   if (no_message_length) {
-    frame->message_data = reader->ReadRemainingPayload();
+    frame->message_data = QuicString(reader->ReadRemainingPayload());
     return true;
   }
 
@@ -3245,11 +3245,15 @@
     set_detailed_error("Unable to read message length");
     return false;
   }
-  if (!reader->ReadStringPiece(&frame->message_data, message_length)) {
+
+  QuicStringPiece message_piece;
+  if (!reader->ReadStringPiece(&message_piece, message_length)) {
     set_detailed_error("Unable to read message data");
     return false;
   }
 
+  frame->message_data = QuicString(message_piece);
+
   return true;
 }
 
diff --git a/net/third_party/quic/core/quic_session.cc b/net/third_party/quic/core/quic_session.cc
index 11f3bc3..0d9259f 100644
--- a/net/third_party/quic/core/quic_session.cc
+++ b/net/third_party/quic/core/quic_session.cc
@@ -90,11 +90,9 @@
   if (faster_get_stream_) {
     QUIC_FLAG_COUNT(quic_reloadable_flag_quic_session_faster_get_stream);
   }
-  if (connection_->deprecate_post_process_after_data()) {
-    closed_streams_clean_up_alarm_ =
-        QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm(
-            new ClosedStreamsCleanUpDelegate(this)));
-  }
+  closed_streams_clean_up_alarm_ =
+      QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm(
+          new ClosedStreamsCleanUpDelegate(this)));
 }
 
 void QuicSession::Initialize() {
@@ -239,9 +237,7 @@
     zombie_streams_.erase(it);
   }
 
-  if (deprecate_post_process_after_data()) {
-    closed_streams_clean_up_alarm_->Cancel();
-  }
+  closed_streams_clean_up_alarm_->Cancel();
 
   if (visitor_) {
     visitor_->OnConnectionClosed(connection_->connection_id(), error,
@@ -503,9 +499,7 @@
     control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written);
     connection_->OnStreamReset(id, error);
   }
-  if (GetQuicReloadableFlag(quic_fix_reset_zombie_streams) &&
-      error != QUIC_STREAM_NO_ERROR && QuicContainsKey(zombie_streams_, id)) {
-    QUIC_FLAG_COUNT(quic_reloadable_flag_quic_fix_reset_zombie_streams);
+  if (error != QUIC_STREAM_NO_ERROR && QuicContainsKey(zombie_streams_, id)) {
     OnStreamDoneWaitingForAcks(id);
     return;
   }
@@ -578,8 +572,7 @@
     closed_streams_.push_back(std::move(it->second));
     // Do not retransmit data of a closed stream.
     streams_with_pending_retransmission_.erase(stream_id);
-    if (deprecate_post_process_after_data() &&
-        !closed_streams_clean_up_alarm_->IsSet()) {
+    if (!closed_streams_clean_up_alarm_->IsSet()) {
       closed_streams_clean_up_alarm_->Set(
           connection_->clock()->ApproximateNow());
     }
@@ -1119,11 +1112,6 @@
          control_frame_manager_.WillingToWrite();
 }
 
-void QuicSession::PostProcessAfterData() {
-  DCHECK(!deprecate_post_process_after_data());
-  closed_streams_.clear();
-}
-
 void QuicSession::OnAckNeedsRetransmittableFrame() {
   flow_controller_.SendWindowUpdate();
 }
@@ -1182,8 +1170,7 @@
   }
 
   closed_streams_.push_back(std::move(it->second));
-  if (deprecate_post_process_after_data() &&
-      !closed_streams_clean_up_alarm_->IsSet()) {
+  if (!closed_streams_clean_up_alarm_->IsSet()) {
     closed_streams_clean_up_alarm_->Set(connection_->clock()->ApproximateNow());
   }
   zombie_streams_.erase(it);
@@ -1449,9 +1436,6 @@
 }
 
 void QuicSession::CleanUpClosedStreams() {
-  DCHECK(deprecate_post_process_after_data());
-  QUIC_FLAG_COUNT_N(quic_reloadable_flag_quic_deprecate_post_process_after_data,
-                    1, 3);
   closed_streams_.clear();
 }
 
@@ -1463,10 +1447,6 @@
   return connection_->GetLargestMessagePayload();
 }
 
-bool QuicSession::deprecate_post_process_after_data() const {
-  return connection_->deprecate_post_process_after_data();
-}
-
 void QuicSession::SendStopSending(uint16_t code, QuicStreamId stream_id) {
   control_frame_manager_.WriteOrBufferStopSending(code, stream_id);
 }
diff --git a/net/third_party/quic/core/quic_session.h b/net/third_party/quic/core/quic_session.h
index f10679b2..57bce8b 100644
--- a/net/third_party/quic/core/quic_session.h
+++ b/net/third_party/quic/core/quic_session.h
@@ -110,9 +110,6 @@
   void OnCanWrite() override;
   void OnCongestionWindowChange(QuicTime /*now*/) override {}
   void OnConnectionMigration(AddressChangeType type) override {}
-  // Deletes streams that are safe to be deleted now that it's safe to do so (no
-  // other operations are being done on the streams at this time).
-  void PostProcessAfterData() override;
   // Adds a connection level WINDOW_UPDATE frame.
   void OnAckNeedsRetransmittableFrame() override;
   void SendPing() override;
@@ -161,10 +158,10 @@
   // includes the message status and message ID (valid if the write succeeds).
   // SendMessage flushes a message packet even it is not full. If the
   // application wants to bundle other data in the same packet, please consider
-  // adding a packet flusher around the SendMessage and/or WritevData calls. If
-  // a packet flusher is added, ensure the |message|'s buffer is not modified or
-  // deleted before exiting the flusher's scope. OnMessageAcked and
-  // OnMessageLost are called when a particular message gets acked or lost.
+  // adding a packet flusher around the SendMessage and/or WritevData calls.
+  //
+  // OnMessageAcked and OnMessageLost are called when a particular message gets
+  // acked or lost.
   //
   // Note that SendMessage will fail with status = MESSAGE_STATUS_BLOCKED
   // if connection is congestion control blocked or underlying socket is write
@@ -360,8 +357,6 @@
 
   bool session_decides_what_to_write() const;
 
-  bool deprecate_post_process_after_data() const;
-
   const ParsedQuicVersionVector& supported_versions() const {
     return supported_versions_;
   }
diff --git a/net/third_party/quic/core/quic_session_test.cc b/net/third_party/quic/core/quic_session_test.cc
index 87f4f50..422aec3 100644
--- a/net/third_party/quic/core/quic_session_test.cc
+++ b/net/third_party/quic/core/quic_session_test.cc
@@ -19,6 +19,7 @@
 #include "net/third_party/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quic/platform/api/quic_str_cat.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
@@ -251,7 +252,6 @@
   using QuicSession::ActivateStream;
   using QuicSession::closed_streams;
   using QuicSession::next_outgoing_stream_id;
-  using QuicSession::PostProcessAfterData;
   using QuicSession::zombie_streams;
 
  private:
@@ -1063,9 +1063,6 @@
   QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(),
                                QUIC_STREAM_CANCELLED, kByteOffset);
   session_.OnRstStream(rst_frame);
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
   EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
 }
 
@@ -1081,9 +1078,6 @@
       kInitialSessionFlowControlWindowForTest / 2 - 1;
   QuicStreamFrame frame(stream->id(), true, kByteOffset, ".");
   session_.OnStreamFrame(frame);
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
   EXPECT_TRUE(connection_->connected());
 
   EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed());
@@ -1263,12 +1257,6 @@
   // Create one more data streams to exceed limit of open stream.
   QuicStreamFrame data1(kFinalStreamId, false, 0, QuicStringPiece("HT"));
   session_.OnStreamFrame(data1);
-
-  // Called after any new data is received by the session, and triggers the
-  // call to close the connection.
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
 }
 
 TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpenedOutgoing) {
@@ -1309,12 +1297,6 @@
     session_.StreamDraining(i);
     EXPECT_EQ(0u, session_.GetNumOpenIncomingStreams());
   }
-
-  // Called after any new data is received by the session, and triggers the call
-  // to close the connection.
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
 }
 
 TEST_P(QuicSessionTestServer, TestMaxIncomingAndOutgoingStreamsAllowed) {
@@ -1380,10 +1362,6 @@
   stream->Reset(QUIC_STREAM_CANCELLED);
   EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream));
 
-  // Allow the session to delete the stream object.
-  if (!session_.deprecate_post_process_after_data()) {
-    session_.PostProcessAfterData();
-  }
   EXPECT_TRUE(connection_->connected());
   EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id));
   EXPECT_FALSE(QuicSessionPeer::IsStreamCreated(&session_, stream_id));
@@ -1697,22 +1675,14 @@
   EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _));
   stream2->Reset(QUIC_STREAM_CANCELLED);
 
-  if (GetQuicReloadableFlag(quic_fix_reset_zombie_streams)) {
-    // Verify stream 2 gets closed.
-    EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
-    EXPECT_TRUE(session_.IsClosedStream(stream2->id()));
-    EXPECT_CALL(*stream2, OnCanWrite()).Times(0);
-  } else {
-    EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
-    EXPECT_CALL(*stream2, OnCanWrite());
-  }
+  // Verify stream 2 gets closed.
+  EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+  EXPECT_TRUE(session_.IsClosedStream(stream2->id()));
+  EXPECT_CALL(*stream2, OnCanWrite()).Times(0);
   session_.OnCanWrite();
 }
 
 TEST_P(QuicSessionTestServer, CleanUpClosedStreamsAlarm) {
-  if (!GetQuicReloadableFlag(quic_deprecate_post_process_after_data)) {
-    return;
-  }
   EXPECT_FALSE(
       QuicSessionPeer::GetCleanUpClosedStreamsAlarm(&session_)->IsSet());
 
diff --git a/net/third_party/quic/core/quic_stream.h b/net/third_party/quic/core/quic_stream.h
index db540ec..5241a50 100644
--- a/net/third_party/quic/core/quic_stream.h
+++ b/net/third_party/quic/core/quic_stream.h
@@ -43,7 +43,8 @@
 
 class QuicSession;
 
-class QUIC_EXPORT_PRIVATE QuicStream {
+class QUIC_EXPORT_PRIVATE QuicStream
+    : public QuicStreamSequencer::StreamInterface {
  public:
   // This is somewhat arbitrary.  It's possible, but unlikely, we will either
   // fail to set a priority client-side, or cancel a stream before stripping the
@@ -71,6 +72,29 @@
   // Not in use currently.
   void SetFromConfig();
 
+  // QuicStreamSequencer::StreamInterface implementation.
+  QuicStreamId id() const override { return id_; }
+  // Called by the stream subclass after it has consumed the final incoming
+  // data.
+  void OnFinRead() override;
+
+  // Called by the subclass or the sequencer to reset the stream from this
+  // end.
+  void Reset(QuicRstStreamErrorCode error) override;
+
+  // Called by the subclass or the sequencer to close the entire connection from
+  // this end.
+  void CloseConnectionWithDetails(QuicErrorCode error,
+                                  const QuicString& details) override;
+
+  // Called by the stream sequencer as bytes are consumed from the buffer.
+  // If the receive window has dropped below the threshold, then send a
+  // WINDOW_UPDATE frame.
+  void AddBytesConsumed(QuicByteCount bytes) override;
+
+  // Get peer IP of the lastest packet which connection is dealing/delt with.
+  const QuicSocketAddress& PeerAddressOfLatestPacket() const override;
+
   // Called by the session when a (potentially duplicate) stream frame has been
   // received for this stream.
   virtual void OnStreamFrame(const QuicStreamFrame& frame);
@@ -94,24 +118,6 @@
   virtual void OnConnectionClosed(QuicErrorCode error,
                                   ConnectionCloseSource source);
 
-  // Called by the stream subclass after it has consumed the final incoming
-  // data.
-  virtual void OnFinRead();
-
-  // Called when new data is available from the sequencer.  Subclasses must
-  // actively retrieve the data using the sequencer's Readv() or
-  // GetReadableRegions() method.
-  virtual void OnDataAvailable() = 0;
-
-  // Called by the subclass or the sequencer to reset the stream from this
-  // end.
-  virtual void Reset(QuicRstStreamErrorCode error);
-
-  // Called by the subclass or the sequencer to close the entire connection from
-  // this end.
-  virtual void CloseConnectionWithDetails(QuicErrorCode error,
-                                          const QuicString& details);
-
   spdy::SpdyPriority priority() const;
 
   // Sets priority_ to priority.  This should only be called before bytes are
@@ -127,8 +133,6 @@
   // Number of bytes available to read.
   size_t ReadableBytes() const;
 
-  QuicStreamId id() const { return id_; }
-
   QuicRstStreamErrorCode stream_error() const { return stream_error_; }
   QuicErrorCode connection_error() const { return connection_error_; }
 
@@ -173,10 +177,6 @@
   bool MaybeIncreaseHighestReceivedOffset(QuicStreamOffset new_offset);
   // Called when bytes are sent to the peer.
   void AddBytesSent(QuicByteCount bytes);
-  // Called by the stream sequencer as bytes are consumed from the buffer.
-  // If the receive window has dropped below the threshold, then send a
-  // WINDOW_UPDATE frame.
-  void AddBytesConsumed(QuicByteCount bytes);
 
   // Updates the flow controller's send window offset and calls OnCanWrite if
   // it was blocked before.
@@ -209,9 +209,6 @@
   // stop sending stream-level flow-control updates when this end sends FIN.
   virtual void StopReading();
 
-  // Get peer IP of the lastest packet which connection is dealing/delt with.
-  virtual const QuicSocketAddress& PeerAddressOfLatestPacket() const;
-
   // Sends as much of 'data' to the connection as the connection will consume,
   // and then buffers any remaining data in queued_data_.
   // If fin is true: if it is immediately passed on to the session,
diff --git a/net/third_party/quic/core/quic_stream_id_manager.cc b/net/third_party/quic/core/quic_stream_id_manager.cc
index 94a9bb90..34f197c5 100644
--- a/net/third_party/quic/core/quic_stream_id_manager.cc
+++ b/net/third_party/quic/core/quic_stream_id_manager.cc
@@ -186,7 +186,7 @@
   // If the stream is inbound, we can increase the stream ID limit and maybe
   // advertise the new limit to the peer.
   if (actual_max_allowed_incoming_stream_id_ >=
-      (kMaximumStreamIdSupported - kV99StreamIdIncrement)) {
+      (kMaxQuicStreamId - kV99StreamIdIncrement)) {
     // Reached the maximum stream id value that the implementation
     // supports. Nothing can be done here.
     return;
diff --git a/net/third_party/quic/core/quic_stream_sequencer.cc b/net/third_party/quic/core/quic_stream_sequencer.cc
index ead9e4a..102a0a2 100644
--- a/net/third_party/quic/core/quic_stream_sequencer.cc
+++ b/net/third_party/quic/core/quic_stream_sequencer.cc
@@ -22,7 +22,7 @@
 
 namespace quic {
 
-QuicStreamSequencer::QuicStreamSequencer(QuicStream* quic_stream)
+QuicStreamSequencer::QuicStreamSequencer(StreamInterface* quic_stream)
     : stream_(quic_stream),
       buffered_frames_(kStreamReceiveWindowLimit),
       close_offset_(std::numeric_limits<QuicStreamOffset>::max()),
diff --git a/net/third_party/quic/core/quic_stream_sequencer.h b/net/third_party/quic/core/quic_stream_sequencer.h
index 538ccb7..ab9edc0 100644
--- a/net/third_party/quic/core/quic_stream_sequencer.h
+++ b/net/third_party/quic/core/quic_stream_sequencer.h
@@ -20,14 +20,40 @@
 class QuicStreamSequencerPeer;
 }  // namespace test
 
-class QuicStream;
-
 // Buffers frames until we have something which can be passed
 // up to the next layer.
 class QUIC_EXPORT_PRIVATE QuicStreamSequencer {
  public:
-  explicit QuicStreamSequencer(QuicStream* quic_stream);
+  // Interface that thie Sequencer uses to communicate with the Stream.
+  class StreamInterface {
+   public:
+    virtual ~StreamInterface() = default;
+
+    // Called when new data is available to be read from the sequencer.
+    virtual void OnDataAvailable() = 0;
+    // Called when the end of the stream has been read.
+    virtual void OnFinRead() = 0;
+    // Called when bytes have been consumed from the sequencer.
+    virtual void AddBytesConsumed(QuicByteCount bytes) = 0;
+    // TODO(rch): Clean up this interface via OnUnrecoverableError and
+    // remove PeerAddressOfLatestPacket().
+    // Called when an error has occurred which should result in the stream
+    // being reset.
+    virtual void Reset(QuicRstStreamErrorCode error) = 0;
+    // Called when an error has occurred which should result in the connection
+    // being closed.
+    virtual void CloseConnectionWithDetails(QuicErrorCode error,
+                                            const QuicString& details) = 0;
+
+    // Returns the stream id of this stream.
+    virtual QuicStreamId id() const = 0;
+    // Returns the peer address of the last packet received for this stream.
+    virtual const QuicSocketAddress& PeerAddressOfLatestPacket() const = 0;
+  };
+
+  explicit QuicStreamSequencer(StreamInterface* quic_stream);
   QuicStreamSequencer(const QuicStreamSequencer&) = delete;
+  QuicStreamSequencer(QuicStreamSequencer&&) = default;
   QuicStreamSequencer& operator=(const QuicStreamSequencer&) = delete;
   virtual ~QuicStreamSequencer();
 
@@ -131,7 +157,7 @@
   bool MaybeCloseStream();
 
   // The stream which owns this sequencer.
-  QuicStream* stream_;
+  StreamInterface* stream_;
 
   // Stores received data in offset order.
   QuicStreamSequencerBuffer buffered_frames_;
diff --git a/net/third_party/quic/core/quic_stream_sequencer_buffer.h b/net/third_party/quic/core/quic_stream_sequencer_buffer.h
index 85db9d2a..950a347 100644
--- a/net/third_party/quic/core/quic_stream_sequencer_buffer.h
+++ b/net/third_party/quic/core/quic_stream_sequencer_buffer.h
@@ -90,6 +90,7 @@
 
   explicit QuicStreamSequencerBuffer(size_t max_capacity_bytes);
   QuicStreamSequencerBuffer(const QuicStreamSequencerBuffer&) = delete;
+  QuicStreamSequencerBuffer(QuicStreamSequencerBuffer&&) = default;
   QuicStreamSequencerBuffer& operator=(const QuicStreamSequencerBuffer&) =
       delete;
   ~QuicStreamSequencerBuffer();
diff --git a/net/third_party/quic/core/quic_stream_sequencer_buffer_test.cc b/net/third_party/quic/core/quic_stream_sequencer_buffer_test.cc
index e42f5c61..7c14502 100644
--- a/net/third_party/quic/core/quic_stream_sequencer_buffer_test.cc
+++ b/net/third_party/quic/core/quic_stream_sequencer_buffer_test.cc
@@ -127,6 +127,31 @@
   EXPECT_TRUE(helper_->IsBufferAllocated());
 }
 
+TEST_F(QuicStreamSequencerBufferTest, Move) {
+  EXPECT_FALSE(helper_->IsBufferAllocated());
+  QuicString source(1024, 'a');
+  size_t written;
+  EXPECT_EQ(QUIC_NO_ERROR,
+            buffer_->OnStreamData(800, source, &written, &error_details_));
+  BufferBlock* block_ptr = helper_->GetBlock(0);
+  for (size_t i = 0; i < source.size(); ++i) {
+    ASSERT_EQ('a', block_ptr->buffer[helper_->GetInBlockOffset(800) + i]);
+  }
+
+  QuicStreamSequencerBuffer buffer2(std::move(*buffer_));
+  QuicStreamSequencerBufferPeer helper2(&buffer2);
+
+  EXPECT_FALSE(helper_->IsBufferAllocated());
+
+  EXPECT_EQ(2, helper2.IntervalSize());
+  EXPECT_EQ(0u, helper2.ReadableBytes());
+  EXPECT_EQ(1u, helper2.bytes_received().Size());
+  EXPECT_EQ(800u, helper2.bytes_received().begin()->min());
+  EXPECT_EQ(1824u, helper2.bytes_received().begin()->max());
+  EXPECT_TRUE(helper2.CheckBufferInvariants());
+  EXPECT_TRUE(helper2.IsBufferAllocated());
+}
+
 TEST_F(QuicStreamSequencerBufferTest, OnStreamDataInvalidSource) {
   // Pass in an invalid source, expects to return error.
   QuicStringPiece source;
diff --git a/net/third_party/quic/core/quic_stream_sequencer_test.cc b/net/third_party/quic/core/quic_stream_sequencer_test.cc
index 63d9e1f..32c7ee3 100644
--- a/net/third_party/quic/core/quic_stream_sequencer_test.cc
+++ b/net/third_party/quic/core/quic_stream_sequencer_test.cc
@@ -18,8 +18,6 @@
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
-#include "net/third_party/quic/test_tools/mock_clock.h"
-#include "net/third_party/quic/test_tools/quic_spdy_session_peer.h"
 #include "net/third_party/quic/test_tools/quic_stream_sequencer_peer.h"
 #include "net/third_party/quic/test_tools/quic_test_utils.h"
 
@@ -30,17 +28,17 @@
 namespace quic {
 namespace test {
 
-class MockStream : public QuicStream {
+class MockStream : public QuicStreamSequencer::StreamInterface {
  public:
-  MockStream(QuicSession* session, QuicStreamId id)
-      : QuicStream(id, session, /*is_static=*/false, BIDIRECTIONAL) {}
-
   MOCK_METHOD0(OnFinRead, void());
   MOCK_METHOD0(OnDataAvailable, void());
   MOCK_METHOD2(CloseConnectionWithDetails,
                void(QuicErrorCode error, const QuicString& details));
   MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error));
   MOCK_METHOD0(OnCanWrite, void());
+  MOCK_METHOD1(AddBytesConsumed, void(QuicByteCount bytes));
+
+  QuicStreamId id() const override { return 1; }
 
   const QuicSocketAddress& PeerAddressOfLatestPacket() const override {
     return peer_address_;
@@ -69,33 +67,48 @@
 
  protected:
   QuicStreamSequencerTest()
-      : connection_(new MockQuicConnection(&helper_,
-                                           &alarm_factory_,
-                                           Perspective::IS_CLIENT)),
-        session_(connection_),
-        stream_(&session_, 1),
-        sequencer_(new QuicStreamSequencer(&stream_)) {}
+      : stream_(), sequencer_(new QuicStreamSequencer(&stream_)) {}
 
   // Verify that the data in first region match with the expected[0].
   bool VerifyReadableRegion(const std::vector<QuicString>& expected) {
+    return VerifyReadableRegion(*sequencer_, expected);
+  }
+
+  // Verify that the data in each of currently readable regions match with each
+  // item given in |expected|.
+  bool VerifyReadableRegions(const std::vector<QuicString>& expected) {
+    return VerifyReadableRegions(*sequencer_, expected);
+  }
+
+  bool VerifyIovecs(iovec* iovecs,
+                    size_t num_iovecs,
+                    const std::vector<QuicString>& expected) {
+    return VerifyIovecs(*sequencer_, iovecs, num_iovecs, expected);
+  }
+
+  bool VerifyReadableRegion(const QuicStreamSequencer& sequencer,
+                            const std::vector<QuicString>& expected) {
     iovec iovecs[1];
-    if (sequencer_->GetReadableRegions(iovecs, 1)) {
-      return (VerifyIovecs(iovecs, 1, std::vector<QuicString>{expected[0]}));
+    if (sequencer.GetReadableRegions(iovecs, 1)) {
+      return (VerifyIovecs(sequencer, iovecs, 1,
+                           std::vector<QuicString>{expected[0]}));
     }
     return false;
   }
 
   // Verify that the data in each of currently readable regions match with each
   // item given in |expected|.
-  bool VerifyReadableRegions(const std::vector<QuicString>& expected) {
+  bool VerifyReadableRegions(const QuicStreamSequencer& sequencer,
+                             const std::vector<QuicString>& expected) {
     iovec iovecs[5];
     size_t num_iovecs =
-        sequencer_->GetReadableRegions(iovecs, QUIC_ARRAYSIZE(iovecs));
-    return VerifyReadableRegion(expected) &&
-           VerifyIovecs(iovecs, num_iovecs, expected);
+        sequencer.GetReadableRegions(iovecs, QUIC_ARRAYSIZE(iovecs));
+    return VerifyReadableRegion(sequencer, expected) &&
+           VerifyIovecs(sequencer, iovecs, num_iovecs, expected);
   }
 
-  bool VerifyIovecs(iovec* iovecs,
+  bool VerifyIovecs(const QuicStreamSequencer& sequencer,
+                    iovec* iovecs,
                     size_t num_iovecs,
                     const std::vector<QuicString>& expected) {
     int start_position = 0;
@@ -147,10 +160,6 @@
     return QuicStreamSequencerPeer::GetNumBufferedBytes(sequencer_.get());
   }
 
-  MockQuicConnectionHelper helper_;
-  MockAlarmFactory alarm_factory_;
-  MockQuicConnection* connection_;
-  MockQuicSpdySession session_;
   testing::StrictMock<MockStream> stream_;
   std::unique_ptr<QuicStreamSequencer> sequencer_;
 };
@@ -158,6 +167,7 @@
 // TODO(rch): reorder these tests so they build on each other.
 
 TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(3);
   }));
@@ -166,7 +176,6 @@
 
   EXPECT_EQ(0u, NumBufferedBytes());
   EXPECT_EQ(3u, sequencer_->NumBytesConsumed());
-  EXPECT_EQ(3u, stream_.flow_controller()->bytes_consumed());
   // Ignore this - it matches a past packet number and we should not see it
   // again.
   OnFrame(0, "def");
@@ -187,6 +196,7 @@
 }
 
 TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(3);
   }));
@@ -203,6 +213,7 @@
   EXPECT_EQ(3u, NumBufferedBytes());
   EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
 
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(3);
   }));
@@ -210,6 +221,7 @@
   EXPECT_EQ(0u, NumBufferedBytes());
   EXPECT_EQ(3u, sequencer_->NumBytesConsumed());
 
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(3);
   }));
@@ -225,6 +237,7 @@
   EXPECT_EQ(3u, NumBufferedBytes());
   EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
 
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(3);
   }));
@@ -251,6 +264,7 @@
 }
 
 TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) {
+  EXPECT_CALL(stream_, AddBytesConsumed(2));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(2);
   }));
@@ -286,6 +300,7 @@
   EXPECT_EQ(0u, sequencer_->NumBytesConsumed());
   EXPECT_EQ(6u, sequencer_->NumBytesBuffered());
 
+  EXPECT_CALL(stream_, AddBytesConsumed(9));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(9);
   }));
@@ -304,6 +319,7 @@
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(3);
   }));
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   OnFinFrame(0, "abc");
 
   EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
@@ -314,6 +330,7 @@
   EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
 
   OnFrame(3, "def");
+  EXPECT_CALL(stream_, AddBytesConsumed(6));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(6);
   }));
@@ -326,6 +343,7 @@
   OnFinFrame(3, "");
   EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
 
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   EXPECT_CALL(stream_, OnDataAvailable()).WillOnce(testing::Invoke([this]() {
     ConsumeData(3);
   }));
@@ -345,6 +363,7 @@
   EXPECT_CALL(stream_, OnDataAvailable());
   OnFrame(0, "abc");
 
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   iovec iov = {&buffer[0], 3};
   int bytes_read = sequencer_->Readv(&iov, 1);
   EXPECT_EQ(3, bytes_read);
@@ -415,11 +434,17 @@
 // All frames are processed as soon as we have sequential data.
 // Infinite buffering, so all frames are acked right away.
 TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) {
-  InSequence s;
   EXPECT_CALL(stream_, OnDataAvailable())
       .Times(AnyNumber())
       .WillRepeatedly(
           Invoke(this, &QuicSequencerRandomTest::ReadAvailableData));
+  QuicByteCount total_bytes_consumed = 0;
+  EXPECT_CALL(stream_, AddBytesConsumed(_))
+      .Times(AnyNumber())
+      .WillRepeatedly(
+          testing::Invoke([&total_bytes_consumed](QuicByteCount bytes) {
+            total_bytes_consumed += bytes;
+          }));
 
   while (!list_.empty()) {
     int index = OneToN(list_.size()) - 1;
@@ -431,6 +456,7 @@
 
   ASSERT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, output_.size());
   EXPECT_EQ(kPayload, output_);
+  EXPECT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, total_bytes_consumed);
 }
 
 TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingBackup) {
@@ -442,6 +468,13 @@
   iov[1].iov_len = 5;
 
   EXPECT_CALL(stream_, OnDataAvailable()).Times(AnyNumber());
+  QuicByteCount total_bytes_consumed = 0;
+  EXPECT_CALL(stream_, AddBytesConsumed(_))
+      .Times(AnyNumber())
+      .WillRepeatedly(
+          testing::Invoke([&total_bytes_consumed](QuicByteCount bytes) {
+            total_bytes_consumed += bytes;
+          }));
 
   while (output_.size() != QUIC_ARRAYSIZE(kPayload) - 1) {
     if (!list_.empty() && OneToN(2) == 1) {  // Send data
@@ -476,6 +509,7 @@
   }
   EXPECT_EQ(QuicString(kPayload), output_);
   EXPECT_EQ(QuicString(kPayload), peeked_);
+  EXPECT_EQ(QUIC_ARRAYSIZE(kPayload) - 1, total_bytes_consumed);
 }
 
 // Same as above, just using a different method for reading.
@@ -495,24 +529,24 @@
   ASSERT_TRUE(VerifyReadableRegions(expected));
 
   // Consume 1 byte.
+  EXPECT_CALL(stream_, AddBytesConsumed(1));
   sequencer_->MarkConsumed(1);
-  EXPECT_EQ(1u, stream_.flow_controller()->bytes_consumed());
   // Verify data.
   std::vector<QuicString> expected2 = {"bcdefghi"};
   ASSERT_TRUE(VerifyReadableRegions(expected2));
   EXPECT_EQ(8u, sequencer_->NumBytesBuffered());
 
   // Consume 2 bytes.
+  EXPECT_CALL(stream_, AddBytesConsumed(2));
   sequencer_->MarkConsumed(2);
-  EXPECT_EQ(3u, stream_.flow_controller()->bytes_consumed());
   // Verify data.
   std::vector<QuicString> expected3 = {"defghi"};
   ASSERT_TRUE(VerifyReadableRegions(expected3));
   EXPECT_EQ(6u, sequencer_->NumBytesBuffered());
 
   // Consume 5 bytes.
+  EXPECT_CALL(stream_, AddBytesConsumed(5));
   sequencer_->MarkConsumed(5);
-  EXPECT_EQ(8u, stream_.flow_controller()->bytes_consumed());
   // Verify data.
   std::vector<QuicString> expected4{"i"};
   ASSERT_TRUE(VerifyReadableRegions(expected4));
@@ -550,14 +584,33 @@
   std::vector<QuicString> expected = {"abcdef"};
   ASSERT_TRUE(VerifyReadableRegions(expected));
 
+  EXPECT_CALL(stream_, AddBytesConsumed(6));
   sequencer_->MarkConsumed(6);
 }
 
+TEST_F(QuicStreamSequencerTest, Move) {
+  InSequence s;
+  EXPECT_CALL(stream_, OnDataAvailable());
+
+  OnFrame(0, "abc");
+  OnFrame(3, "def");
+  OnFrame(6, "ghi");
+
+  // abcdefghi buffered.
+  EXPECT_EQ(9u, sequencer_->NumBytesBuffered());
+
+  // Peek into the data.
+  std::vector<QuicString> expected = {"abcdefghi"};
+  ASSERT_TRUE(VerifyReadableRegions(expected));
+
+  QuicStreamSequencer sequencer2(std::move(*sequencer_));
+  ASSERT_TRUE(VerifyReadableRegions(sequencer2, expected));
+}
+
 TEST_F(QuicStreamSequencerTest, OverlappingFramesReceived) {
   // The peer should never send us non-identical stream frames which contain
   // overlapping byte ranges - if they do, we close the connection.
-  QuicStreamId id =
-      QuicSpdySessionPeer::GetNthClientInitiatedStreamId(session_, 0);
+  QuicStreamId id = 1;
 
   QuicStreamFrame frame1(id, false, 1, QuicStringPiece("hello"));
   sequencer_->OnStreamFrame(frame1);
@@ -570,8 +623,7 @@
 }
 
 TEST_F(QuicStreamSequencerTest, DataAvailableOnOverlappingFrames) {
-  QuicStreamId id =
-      QuicSpdySessionPeer::GetNthClientInitiatedStreamId(session_, 0);
+  QuicStreamId id = 1;
   const QuicString data(1000, '.');
 
   // Received [0, 1000).
@@ -579,6 +631,7 @@
   EXPECT_CALL(stream_, OnDataAvailable());
   sequencer_->OnStreamFrame(frame1);
   // Consume [0, 500).
+  EXPECT_CALL(stream_, AddBytesConsumed(500));
   QuicStreamSequencerTest::ConsumeData(500);
   EXPECT_EQ(500u, sequencer_->NumBytesConsumed());
   EXPECT_EQ(500u, sequencer_->NumBytesBuffered());
@@ -589,6 +642,7 @@
   EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
   sequencer_->OnStreamFrame(frame2);
   // Consume [1000, 1500).
+  EXPECT_CALL(stream_, AddBytesConsumed(1000));
   QuicStreamSequencerTest::ConsumeData(1000);
   EXPECT_EQ(1500u, sequencer_->NumBytesConsumed());
   EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
@@ -597,6 +651,7 @@
   QuicStreamFrame frame3(id, false, 1498, QuicStringPiece("hello"));
   EXPECT_CALL(stream_, OnDataAvailable());
   sequencer_->OnStreamFrame(frame3);
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   QuicStreamSequencerTest::ConsumeData(3);
   EXPECT_EQ(1503u, sequencer_->NumBytesConsumed());
   EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
@@ -611,8 +666,7 @@
 
 TEST_F(QuicStreamSequencerTest, OnDataAvailableWhenReadableBytesIncrease) {
   sequencer_->set_level_triggered(true);
-  QuicStreamId id =
-      QuicSpdySessionPeer::GetNthClientInitiatedStreamId(session_, 0);
+  QuicStreamId id = 1;
 
   // Received [0, 5).
   QuicStreamFrame frame1(id, false, 0, "hello");
@@ -640,10 +694,10 @@
   EXPECT_CALL(stream_, OnDataAvailable());
   OnFrame(0u, "abc");
   QuicString actual;
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   sequencer_->Read(&actual);
   EXPECT_EQ("abc", actual);
   EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
-  EXPECT_EQ(3u, stream_.flow_controller()->bytes_consumed());
 }
 
 TEST_F(QuicStreamSequencerTest, ReadMultipleFramesWithMissingFrame) {
@@ -653,10 +707,10 @@
   OnFrame(6u, "ghi");
   OnFrame(10u, "xyz");  // Byte 9 is missing.
   QuicString actual;
+  EXPECT_CALL(stream_, AddBytesConsumed(9));
   sequencer_->Read(&actual);
   EXPECT_EQ("abcdefghi", actual);
   EXPECT_EQ(3u, sequencer_->NumBytesBuffered());
-  EXPECT_EQ(9u, stream_.flow_controller()->bytes_consumed());
 }
 
 TEST_F(QuicStreamSequencerTest, ReadAndAppendToString) {
@@ -664,28 +718,35 @@
   OnFrame(0u, "def");
   OnFrame(3u, "ghi");
   QuicString actual = "abc";
+  EXPECT_CALL(stream_, AddBytesConsumed(6));
   sequencer_->Read(&actual);
   EXPECT_EQ("abcdefghi", actual);
   EXPECT_EQ(0u, sequencer_->NumBytesBuffered());
-  EXPECT_EQ(6u, stream_.flow_controller()->bytes_consumed());
 }
 
 TEST_F(QuicStreamSequencerTest, StopReading) {
   EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
   EXPECT_CALL(stream_, OnFinRead());
 
+  EXPECT_CALL(stream_, AddBytesConsumed(0));
   sequencer_->StopReading();
 
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   OnFrame(0u, "abc");
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   OnFrame(3u, "def");
+  EXPECT_CALL(stream_, AddBytesConsumed(3));
   OnFinFrame(6u, "ghi");
 }
 
 TEST_F(QuicStreamSequencerTest, StopReadingWithLevelTriggered) {
   if (GetQuicReloadableFlag(quic_stop_reading_when_level_triggered)) {
+    EXPECT_CALL(stream_, AddBytesConsumed(0));
+    EXPECT_CALL(stream_, AddBytesConsumed(3)).Times(3);
     EXPECT_CALL(stream_, OnDataAvailable()).Times(0);
     EXPECT_CALL(stream_, OnFinRead());
   } else {
+    EXPECT_CALL(stream_, AddBytesConsumed(0));
     EXPECT_CALL(stream_, OnDataAvailable()).Times(3);
   }
 
diff --git a/net/third_party/quic/core/quic_stream_test.cc b/net/third_party/quic/core/quic_stream_test.cc
index 25092ef..9d1be8f 100644
--- a/net/third_party/quic/core/quic_stream_test.cc
+++ b/net/third_party/quic/core/quic_stream_test.cc
@@ -14,6 +14,7 @@
 #include "net/third_party/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
+#include "net/third_party/quic/platform/api/quic_mem_slice_storage.h"
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
diff --git a/net/third_party/quic/core/quic_versions.cc b/net/third_party/quic/core/quic_versions.cc
index 8c613946..0cd476d 100644
--- a/net/third_party/quic/core/quic_versions.cc
+++ b/net/third_party/quic/core/quic_versions.cc
@@ -326,5 +326,11 @@
   return result;
 }
 
+ParsedQuicVersion UnsupportedQuicVersion() {
+  static const ParsedQuicVersion kUnsupportedQuicVersion(
+      PROTOCOL_UNSUPPORTED, QUIC_VERSION_UNSUPPORTED);
+  return kUnsupportedQuicVersion;
+}
+
 #undef RETURN_STRING_LITERAL  // undef for jumbo builds
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_versions.h b/net/third_party/quic/core/quic_versions.h
index 22a613ad..37676d2 100644
--- a/net/third_party/quic/core/quic_versions.h
+++ b/net/third_party/quic/core/quic_versions.h
@@ -145,6 +145,8 @@
   }
 };
 
+QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion();
+
 QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                              const ParsedQuicVersion& version);
 
diff --git a/net/third_party/quic/core/stateless_rejector.h b/net/third_party/quic/core/stateless_rejector.h
index 6904bb8..5b95feb 100644
--- a/net/third_party/quic/core/stateless_rejector.h
+++ b/net/third_party/quic/core/stateless_rejector.h
@@ -56,6 +56,9 @@
   static void Process(std::unique_ptr<StatelessRejector> rejector,
                       std::unique_ptr<ProcessDoneCallback> done_cb);
 
+  // Return the version of the CHLO.
+  ParsedQuicVersion version() const { return version_; }
+
   // Returns the state of the rejector after OnChlo() has been called.
   State state() const { return state_; }
 
diff --git a/net/third_party/quic/quartc/quartc_factory.cc b/net/third_party/quic/quartc/quartc_factory.cc
index f2e90b5..c10a13d 100644
--- a/net/third_party/quic/quartc/quartc_factory.cc
+++ b/net/third_party/quic/quartc/quartc_factory.cc
@@ -32,15 +32,12 @@
   // Fixes behavior of StopReading() with level-triggered stream sequencers.
   SetQuicReloadableFlag(quic_stop_reading_when_level_triggered, true);
 
-  // Quartc uses zombie streams -- we close the stream for read as soon as we
-  // create a stream -- this makes the stream a zombie stream. b/115323618
-  // revealed that closing zombie streams is problematic, and enabling this flag
-  // fixes it.
-  SetQuicReloadableFlag(quic_fix_reset_zombie_streams, true);
-
   // Fix b/110259444.
   SetQuicReloadableFlag(quic_fix_spurious_ack_alarm, true);
 
+  // Enable version 45+ to enable SendMessage API.
+  SetQuicReloadableFlag(quic_enable_version_46, true);
+
   std::unique_ptr<QuicConnection> quic_connection =
       CreateQuicConnection(perspective, writer.get());
 
diff --git a/net/third_party/quic/quartc/quartc_session.cc b/net/third_party/quic/quartc/quartc_session.cc
index a5b2745..fdb37ba 100644
--- a/net/third_party/quic/quartc/quartc_session.cc
+++ b/net/third_party/quic/quartc/quartc_session.cc
@@ -181,6 +181,80 @@
                                              QuicStream::kDefaultPriority));
 }
 
+bool QuartcSession::SendOrQueueMessage(QuicString message) {
+  if (!CanSendMessage()) {
+    QUIC_LOG(ERROR) << "Quic session does not support SendMessage";
+    return false;
+  }
+
+  if (message.size() > GetLargestMessagePayload()) {
+    QUIC_LOG(ERROR) << "Message is too big, message_size=" << message.size()
+                    << ", GetLargestMessagePayload="
+                    << GetLargestMessagePayload();
+    return false;
+  }
+
+  // There may be other messages in send queue, so we have to add message
+  // to the queue and call queue processing helper.
+  send_message_queue_.emplace_back(std::move(message));
+
+  ProcessSendMessageQueue();
+
+  return true;
+}
+
+void QuartcSession::ProcessSendMessageQueue() {
+  while (!send_message_queue_.empty()) {
+    MessageResult result = SendMessage(send_message_queue_.front());
+
+    const size_t message_size = send_message_queue_.front().size();
+
+    // Handle errors.
+    switch (result.status) {
+      case MESSAGE_STATUS_SUCCESS:
+        QUIC_VLOG(1) << "Quartc message sent, message_id=" << result.message_id
+                     << ", message_size=" << message_size;
+        break;
+
+      // If connection is congestion controlled or not writable yet, stop
+      // send loop and we'll retry again when we get OnCanWrite notification.
+      case MESSAGE_STATUS_ENCRYPTION_NOT_ESTABLISHED:
+      case MESSAGE_STATUS_BLOCKED:
+        QUIC_VLOG(1) << "Quartc message not sent because connection is blocked"
+                     << ", message will be retried later, status="
+                     << result.status << ", message_size=" << message_size;
+
+        return;
+
+      // Other errors are unexpected. We do not propagate error to Quartc,
+      // because writes can be delayed.
+      case MESSAGE_STATUS_UNSUPPORTED:
+      case MESSAGE_STATUS_TOO_LARGE:
+      case MESSAGE_STATUS_INTERNAL_ERROR:
+        QUIC_DLOG(DFATAL)
+            << "Failed to send quartc message due to unexpected error"
+            << ", message will not be retried, status=" << result.status
+            << ", message_size=" << message_size;
+        break;
+    }
+
+    send_message_queue_.pop_front();
+  }
+}
+
+void QuartcSession::OnCanWrite() {
+  // TODO(b/119640244): Since we currently use messages for audio and streams
+  // for video, it makes sense to process queued messages first, then call quic
+  // core OnCanWrite, which will resend queued streams. Long term we may need
+  // better solution especially if quic connection is used for both data and
+  // media.
+
+  // Process quartc messages that were previously blocked.
+  ProcessSendMessageQueue();
+
+  QuicSession::OnCanWrite();
+}
+
 void QuartcSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
   QuicSession::OnCryptoHandshakeEvent(event);
   if (event == HANDSHAKE_CONFIRMED) {
@@ -298,6 +372,10 @@
                    packet);
 }
 
+void QuartcSession::OnMessageReceived(QuicStringPiece message) {
+  session_delegate_->OnMessageReceived(message);
+}
+
 void QuartcSession::OnProofValid(
     const QuicCryptoClientConfig::CachedState& cached) {
   // TODO(zhihuang): Handle the proof verification.
diff --git a/net/third_party/quic/quartc/quartc_session.h b/net/third_party/quic/quartc/quartc_session.h
index 23cbb22..ac976d6 100644
--- a/net/third_party/quic/quartc/quartc_session.h
+++ b/net/third_party/quic/quartc/quartc_session.h
@@ -5,11 +5,15 @@
 #ifndef NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_H_
 #define NET_THIRD_PARTY_QUIC_QUARTC_QUARTC_SESSION_H_
 
+#include <memory>
+#include <string>
+
 #include "net/third_party/quic/core/quic_crypto_client_stream.h"
 #include "net/third_party/quic/core/quic_crypto_server_stream.h"
 #include "net/third_party/quic/core/quic_crypto_stream.h"
 #include "net/third_party/quic/core/quic_error_codes.h"
 #include "net/third_party/quic/core/quic_session.h"
+#include "net/third_party/quic/core/quic_types.h"
 #include "net/third_party/quic/platform/api/quic_export.h"
 #include "net/third_party/quic/quartc/quartc_packet_writer.h"
 #include "net/third_party/quic/quartc/quartc_stream.h"
@@ -54,11 +58,38 @@
 
   QuartcStream* CreateOutgoingBidirectionalStream();
 
+  // Sends short unreliable message using quic message frame (message must fit
+  // in one quic packet). If connection is blocked by congestion control,
+  // message will be queued and resent later after receiving an OnCanWrite
+  // notification.
+  //
+  // Message size must be <= GetLargestMessagePayload().
+  //
+  // Supported in quic version 45 or later.
+  //
+  // Returns false and logs error if message is too long or session does not
+  // support SendMessage API. Other unexpected errors during send will not be
+  // returned, because messages can be sent later if connection is congestion
+  // controlled.
+  bool SendOrQueueMessage(QuicString message);
+
+  // Returns largest message payload acceptable in SendQuartcMessage.
+  QuicPacketLength GetLargestMessagePayload() const {
+    return connection()->GetLargestMessagePayload();
+  }
+
+  // Return true if transport support message frame.
+  bool CanSendMessage() const {
+    return connection()->transport_version() >= QUIC_VERSION_45;
+  }
+
   void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
 
   // QuicConnectionVisitorInterface overrides.
   void OnCongestionWindowChange(QuicTime now) override;
 
+  void OnCanWrite() override;
+
   void OnConnectionClosed(QuicErrorCode error,
                           const QuicString& error_details,
                           ConnectionCloseSource source) override;
@@ -112,6 +143,9 @@
                                     const QuicString& error_details,
                                     ConnectionCloseSource source) = 0;
 
+    // Called when message (sent as SendMessage) is received.
+    virtual void OnMessageReceived(QuicStringPiece message) = 0;
+
     // TODO(zhihuang): Add proof verification.
   };
 
@@ -125,6 +159,8 @@
   // QuicConnection.
   void OnTransportReceived(const char* data, size_t data_len) override;
 
+  void OnMessageReceived(QuicStringPiece message) override;
+
   // ProofHandler overrides.
   void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
 
@@ -134,6 +170,11 @@
   void OnProofVerifyDetailsAvailable(
       const ProofVerifyDetails& verify_details) override;
 
+  // Returns number of queued (not sent) messages submitted by
+  // SendOrQueueMessage. Messages are queued if connection is congestion
+  // controlled.
+  size_t send_message_queue_size() const { return send_message_queue_.size(); }
+
  protected:
   // QuicSession override.
   QuicStream* CreateIncomingStream(QuicStreamId id) override;
@@ -147,6 +188,8 @@
   void ResetStream(QuicStreamId stream_id, QuicRstStreamErrorCode error);
 
  private:
+  void ProcessSendMessageQueue();
+
   // For crypto handshake.
   std::unique_ptr<QuicCryptoStream> crypto_stream_;
   const QuicString unique_remote_server_id_;
@@ -173,6 +216,11 @@
   std::unique_ptr<QuicCryptoClientConfig> quic_crypto_client_config_;
   // Config for QUIC crypto server stream, used by the server.
   std::unique_ptr<QuicCryptoServerConfig> quic_crypto_server_config_;
+
+  // Queue of pending messages sent by SendQuartcMessage that were not sent
+  // yet or blocked by congestion control. Messages are queued in the order
+  // of sent by SendOrQueueMessage().
+  QuicDeque<QuicString> send_message_queue_;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/quartc/quartc_session_test.cc b/net/third_party/quic/quartc/quartc_session_test.cc
index 4607a7c5..f72f51f 100644
--- a/net/third_party/quic/quartc/quartc_session_test.cc
+++ b/net/third_party/quic/quartc/quartc_session_test.cc
@@ -11,6 +11,7 @@
 #include "net/third_party/quic/core/tls_client_handshaker.h"
 #include "net/third_party/quic/core/tls_server_handshaker.h"
 #include "net/third_party/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quic/platform/api/quic_string_utils.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 #include "net/third_party/quic/platform/api/quic_test_mem_slice_vector.h"
 #include "net/third_party/quic/quartc/counting_packet_filter.h"
@@ -52,16 +53,26 @@
     last_incoming_stream_->SetDelegate(stream_delegate_);
   }
 
+  void OnMessageReceived(QuicStringPiece message) override {
+    incoming_messages_.emplace_back(message);
+  }
+
   void OnCongestionControlChange(QuicBandwidth bandwidth_estimate,
                                  QuicBandwidth pacing_rate,
                                  QuicTime::Delta latest_rtt) override {}
 
-  QuartcStream* incoming_stream() { return last_incoming_stream_; }
+  QuartcStream* last_incoming_stream() { return last_incoming_stream_; }
+
+  // Returns all received messages.
+  const std::vector<QuicString>& incoming_messages() {
+    return incoming_messages_;
+  }
 
   bool connected() { return connected_; }
 
  private:
   QuartcStream* last_incoming_stream_;
+  std::vector<QuicString> incoming_messages_;
   bool connected_ = true;
   QuartcStream::Delegate* stream_delegate_;
 };
@@ -102,6 +113,9 @@
   ~QuartcSessionTest() override {}
 
   void Init() {
+    // To enable SendMessage.
+    SetQuicReloadableFlag(quic_enable_version_45, true);
+
     client_transport_ =
         QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
             &simulator_, "client_transport", "server_transport",
@@ -152,6 +166,7 @@
         CreateConnection(perspective, writer.get());
     QuicString remote_fingerprint_value = "value";
     QuicConfig config;
+
     return QuicMakeUnique<QuartcSession>(
         std::move(quic_connection), config, CurrentSupportedVersions(),
         remote_fingerprint_value, perspective, &simulator_,
@@ -179,7 +194,7 @@
 
   // Test handshake establishment and sending/receiving of data for two
   // directions.
-  void TestStreamConnection() {
+  void TestSendReceiveStreams() {
     ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed());
     ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed());
     ASSERT_TRUE(server_peer_->IsEncryptionEstablished());
@@ -204,7 +219,7 @@
     // Wait for peer 2 to receive messages.
     ASSERT_TRUE(client_stream_delegate_->has_data());
 
-    QuartcStream* incoming = client_session_delegate_->incoming_stream();
+    QuartcStream* incoming = client_session_delegate_->last_incoming_stream();
     ASSERT_TRUE(incoming);
     EXPECT_EQ(incoming->id(), stream_id);
     EXPECT_TRUE(client_peer_->HasOpenDynamicStreams());
@@ -222,6 +237,95 @@
     EXPECT_EQ(server_stream_delegate_->data()[stream_id], kTestResponse);
   }
 
+  // Test sending/receiving of messages for two directions.
+  void TestSendReceiveMessage() {
+    ASSERT_TRUE(server_peer_->CanSendMessage());
+    ASSERT_TRUE(client_peer_->CanSendMessage());
+
+    // Send message from peer 1 to peer 2.
+    ASSERT_TRUE(server_peer_->SendOrQueueMessage("Message from server"));
+
+    // First message in each direction should not be queued.
+    EXPECT_EQ(server_peer_->send_message_queue_size(), 0u);
+
+    // Wait for peer 2 to receive message.
+    RunTasks();
+
+    EXPECT_THAT(client_session_delegate_->incoming_messages(),
+                testing::ElementsAre("Message from server"));
+
+    // Send message from peer 2 to peer 1.
+    ASSERT_TRUE(client_peer_->SendOrQueueMessage("Message from client"));
+
+    // First message in each direction should not be queued.
+    EXPECT_EQ(client_peer_->send_message_queue_size(), 0u);
+
+    // Wait for peer 1 to receive message.
+    RunTasks();
+
+    EXPECT_THAT(server_session_delegate_->incoming_messages(),
+                testing::ElementsAre("Message from client"));
+  }
+
+  // Test for sending multiple messages that also result in queueing.
+  // This is one-way test, which is run in given direction.
+  void TestSendReceiveQueuedMessages(bool direction_from_server) {
+    // Send until queue_size number of messages are queued.
+    constexpr size_t queue_size = 10;
+
+    ASSERT_TRUE(server_peer_->CanSendMessage());
+    ASSERT_TRUE(client_peer_->CanSendMessage());
+
+    QuartcSession* const peer_sending =
+        direction_from_server ? server_peer_.get() : client_peer_.get();
+
+    FakeQuartcSessionDelegate* const delegate_receiving =
+        direction_from_server ? client_session_delegate_.get()
+                              : server_session_delegate_.get();
+
+    // There should be no messages in the queue before we start sending.
+    EXPECT_EQ(peer_sending->send_message_queue_size(), 0u);
+
+    // Send messages from peer 1 to peer 2 until required number of messages
+    // are queued in unsent message queue.
+    std::vector<QuicString> sent_messages;
+    while (peer_sending->send_message_queue_size() < queue_size) {
+      sent_messages.push_back(
+          QuicStrCat("Sending message, index=", sent_messages.size()));
+      ASSERT_TRUE(peer_sending->SendOrQueueMessage(sent_messages.back()));
+    }
+
+    // Wait for peer 2 to receive all messages.
+    RunTasks();
+
+    EXPECT_EQ(delegate_receiving->incoming_messages(), sent_messages);
+  }
+
+  // Test sending long messages:
+  // - message of maximum allowed length should succeed
+  // - message of > maximum allowed length should fail.
+  void TestSendLongMessage() {
+    ASSERT_TRUE(server_peer_->CanSendMessage());
+    ASSERT_TRUE(client_peer_->CanSendMessage());
+
+    // Send message of maximum allowed length.
+    QuicString message_max_long =
+        QuicString(server_peer_->GetLargestMessagePayload(), 'A');
+    ASSERT_TRUE(server_peer_->SendOrQueueMessage(message_max_long));
+
+    // Send long message which should fail.
+    QuicString message_too_long =
+        QuicString(server_peer_->GetLargestMessagePayload() + 1, 'B');
+    ASSERT_FALSE(server_peer_->SendOrQueueMessage(message_too_long));
+
+    // Wait for peer 2 to receive message.
+    RunTasks();
+
+    // Client should only receive one message of allowed length.
+    EXPECT_THAT(client_session_delegate_->incoming_messages(),
+                testing::ElementsAre(message_max_long));
+  }
+
   // Test that client and server are not connected after handshake failure.
   void TestDisconnectAfterFailedHandshake() {
     EXPECT_TRUE(!client_session_delegate_->connected());
@@ -253,10 +357,29 @@
   std::unique_ptr<FakeQuartcSessionDelegate> server_session_delegate_;
 };
 
-TEST_F(QuartcSessionTest, StreamConnection) {
+TEST_F(QuartcSessionTest, SendReceiveStreams) {
   CreateClientAndServerSessions();
   StartHandshake();
-  TestStreamConnection();
+  TestSendReceiveStreams();
+}
+
+TEST_F(QuartcSessionTest, SendReceiveMessages) {
+  CreateClientAndServerSessions();
+  StartHandshake();
+  TestSendReceiveMessage();
+}
+
+TEST_F(QuartcSessionTest, SendReceiveQueuedMessages) {
+  CreateClientAndServerSessions();
+  StartHandshake();
+  TestSendReceiveQueuedMessages(/*direction_from_server=*/true);
+  TestSendReceiveQueuedMessages(/*direction_from_server=*/false);
+}
+
+TEST_F(QuartcSessionTest, SendMessageFails) {
+  CreateClientAndServerSessions();
+  StartHandshake();
+  TestSendLongMessage();
 }
 
 TEST_F(QuartcSessionTest, PreSharedKeyHandshake) {
@@ -264,7 +387,8 @@
   client_peer_->SetPreSharedKey("foo");
   server_peer_->SetPreSharedKey("foo");
   StartHandshake();
-  TestStreamConnection();
+  TestSendReceiveStreams();
+  TestSendReceiveMessage();
 }
 
 // Test that data streams are not created before handshake.
diff --git a/net/third_party/quic/quartc/quartc_stream.cc b/net/third_party/quic/quartc/quartc_stream.cc
index 37ea7fc..ce846427a 100644
--- a/net/third_party/quic/quartc/quartc_stream.cc
+++ b/net/third_party/quic/quartc/quartc_stream.cc
@@ -72,12 +72,15 @@
                                      bool fin_lost) {
   QuicStream::OnStreamFrameLost(offset, data_length, fin_lost);
 
+  ++total_frames_lost_;
+
   DCHECK(delegate_);
   delegate_->OnBufferChanged(this);
 }
 
 void QuartcStream::OnCanWrite() {
-  if (cancel_on_loss_ && HasPendingRetransmission()) {
+  if (total_frames_lost_ > max_frame_retransmission_count_ &&
+      HasPendingRetransmission()) {
     Reset(QUIC_STREAM_CANCELLED);
     return;
   }
@@ -85,15 +88,28 @@
 }
 
 bool QuartcStream::cancel_on_loss() {
-  return cancel_on_loss_;
+  return max_frame_retransmission_count_ == 0;
 }
 
 void QuartcStream::set_cancel_on_loss(bool cancel_on_loss) {
-  cancel_on_loss_ = cancel_on_loss;
+  if (cancel_on_loss) {
+    max_frame_retransmission_count_ = 0;
+  } else {
+    max_frame_retransmission_count_ = std::numeric_limits<int>::max();
+  }
+}
+
+int QuartcStream::max_frame_retransmission_count() const {
+  return max_frame_retransmission_count_;
+}
+
+void QuartcStream::set_max_frame_retransmission_count(
+    int max_frame_retransmission_count) {
+  max_frame_retransmission_count_ = max_frame_retransmission_count;
 }
 
 QuicByteCount QuartcStream::BytesPendingRetransmission() {
-  if (cancel_on_loss_) {
+  if (total_frames_lost_ > max_frame_retransmission_count_) {
     return 0;  // Lost bytes will never be retransmitted.
   }
   QuicByteCount bytes = 0;
diff --git a/net/third_party/quic/quartc/quartc_stream.h b/net/third_party/quic/quartc/quartc_stream.h
index 2f9598da..12a1d147 100644
--- a/net/third_party/quic/quartc/quartc_stream.h
+++ b/net/third_party/quic/quartc/quartc_stream.h
@@ -49,10 +49,24 @@
 
   // Whether the stream should be cancelled instead of retransmitted on loss.
   // If set to true, the stream will reset itself instead of retransmitting lost
-  // stream frames.  Defaults to false.
+  // stream frames.  Defaults to false.  Setting it to true is equivalent to
+  // setting |max_frame_retransmission_count| to zero.
   bool cancel_on_loss();
   void set_cancel_on_loss(bool cancel_on_loss);
 
+  // Maximum number of stream frames which may be retransmitted.  Up to this
+  // number of stream frames may be retransmitted.  If any stream frames in
+  // excess of this amount would be retransmitted, the stream will reset itself.
+  // Setting it to zero disables retransmissions.
+  //
+  // Ideally, the stream would support a maximum retransmission count per frame,
+  // allowing each frame to be retransmitted up to N times.  However, this
+  // requires complex bookkeeping (tracking retransmission count on a per-frame
+  // basis).  This feature provides a simple way to limit retransmissions on
+  // streams that are expected to fit within one frame (eg. small messages).
+  int max_frame_retransmission_count() const;
+  void set_max_frame_retransmission_count(int max_frame_retransmission_count);
+
   QuicByteCount BytesPendingRetransmission();
 
   // Marks this stream as finished writing.  Asynchronously sends a FIN and
@@ -94,8 +108,11 @@
  private:
   Delegate* delegate_ = nullptr;
 
-  // Whether the stream should cancel itself instead of retransmitting frames.
-  bool cancel_on_loss_ = false;
+  // Maximum number of frames which may be retransmitted on this stream.
+  int max_frame_retransmission_count_ = std::numeric_limits<int>::max();
+
+  // Total number of stream frames detected as lost.
+  int total_frames_lost_ = 0;
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/quartc/quartc_stream_test.cc b/net/third_party/quic/quartc/quartc_stream_test.cc
index 2c8d434..731bd4b 100644
--- a/net/third_party/quic/quartc/quartc_stream_test.cc
+++ b/net/third_party/quic/quartc/quartc_stream_test.cc
@@ -409,6 +409,53 @@
   EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
 }
 
+TEST_F(QuartcStreamTest, TestMaxRetransmissionsAbsent) {
+  CreateReliableQuicStream();
+
+  // This should be the default state.
+  EXPECT_EQ(stream_->max_frame_retransmission_count(),
+            std::numeric_limits<int>::max());
+
+  char message[] = "Foo bar";
+  test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+  stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+  EXPECT_EQ("Foo bar", write_buffer_);
+
+  stream_->OnStreamFrameLost(0, 7, false);
+  stream_->OnCanWrite();
+
+  EXPECT_EQ("Foo barFoo bar", write_buffer_);
+  EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_NO_ERROR);
+}
+
+TEST_F(QuartcStreamTest, TestMaxRetransmissionsSet) {
+  CreateReliableQuicStream();
+  stream_->set_max_frame_retransmission_count(2);
+
+  char message[] = "Foo bar";
+  test::QuicTestMemSliceVector data({std::make_pair(message, 7)});
+  stream_->WriteMemSlices(data.span(), /*fin=*/false);
+
+  EXPECT_EQ("Foo bar", write_buffer_);
+
+  stream_->OnStreamFrameLost(0, 7, false);
+  stream_->OnCanWrite();
+
+  EXPECT_EQ("Foo barFoo bar", write_buffer_);
+
+  stream_->OnStreamFrameLost(0, 7, false);
+  stream_->OnCanWrite();
+
+  EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+
+  stream_->OnStreamFrameLost(0, 7, false);
+  stream_->OnCanWrite();
+
+  EXPECT_EQ("Foo barFoo barFoo bar", write_buffer_);
+  EXPECT_EQ(stream_->stream_error(), QUIC_STREAM_CANCELLED);
+}
+
 TEST_F(QuartcStreamTest, TestBytesPendingRetransmission) {
   CreateReliableQuicStream();
   stream_->set_cancel_on_loss(false);
diff --git a/net/third_party/quic/test_tools/quic_test_utils.h b/net/third_party/quic/test_tools/quic_test_utils.h
index 90158df0..5e03954 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.h
+++ b/net/third_party/quic/test_tools/quic_test_utils.h
@@ -372,7 +372,6 @@
                void(const QuicSocketAddress& self_address,
                     const QuicSocketAddress& peer_address));
   MOCK_METHOD0(OnConfigNegotiated, void());
-  MOCK_METHOD0(PostProcessAfterData, void());
   MOCK_METHOD0(OnAckNeedsRetransmittableFrame, void());
   MOCK_METHOD0(SendPing, void());
   MOCK_CONST_METHOD0(AllowSelfAddressChange, bool());
diff --git a/net/third_party/quic/test_tools/simulator/quic_endpoint.h b/net/third_party/quic/test_tools/simulator/quic_endpoint.h
index f24e30a..9224ae5 100644
--- a/net/third_party/quic/test_tools/simulator/quic_endpoint.h
+++ b/net/third_party/quic/test_tools/simulator/quic_endpoint.h
@@ -102,7 +102,6 @@
   void OnCongestionWindowChange(QuicTime now) override {}
   void OnConnectionMigration(AddressChangeType type) override {}
   void OnPathDegrading() override {}
-  void PostProcessAfterData() override {}
   void OnAckNeedsRetransmittableFrame() override {}
   void SendPing() override {}
   bool AllowSelfAddressChange() const override;
diff --git a/net/third_party/quic/tools/quic_simple_server_stream.cc b/net/third_party/quic/tools/quic_simple_server_stream.cc
index c15dec9..d6ac2e57 100644
--- a/net/third_party/quic/tools/quic_simple_server_stream.cc
+++ b/net/third_party/quic/tools/quic_simple_server_stream.cc
@@ -56,7 +56,7 @@
   SendErrorResponse();
 }
 
-void QuicSimpleServerStream::OnDataAvailable() {
+void QuicSimpleServerStream::OnBodyAvailable() {
   while (HasBytesToRead()) {
     struct iovec iov;
     if (GetReadableRegions(&iov, 1) == 0) {
diff --git a/net/third_party/quic/tools/quic_simple_server_stream.h b/net/third_party/quic/tools/quic_simple_server_stream.h
index 09c3ae05..cbff606 100644
--- a/net/third_party/quic/tools/quic_simple_server_stream.h
+++ b/net/third_party/quic/tools/quic_simple_server_stream.h
@@ -42,7 +42,7 @@
 
   // QuicStream implementation called by the sequencer when there is
   // data (or a FIN) to be read.
-  void OnDataAvailable() override;
+  void OnBodyAvailable() override;
 
   // Make this stream start from as if it just finished parsing an incoming
   // request whose headers are equivalent to |push_request_headers|.
diff --git a/net/url_request/url_request_filter.cc b/net/url_request/url_request_filter.cc
index 628a912..f2cf018e 100644
--- a/net/url_request/url_request_filter.cc
+++ b/net/url_request/url_request_filter.cc
@@ -29,9 +29,9 @@
 
 // When removing interceptors, DCHECK that this function returns true.
 bool OnMessageLoopForInterceptorRemoval() {
-  // Checking for a MessageLoopForIO is a best effort at determining whether the
-  // current thread is a networking thread.
-  return base::MessageLoopForIO::IsCurrent();
+  // Checking for a MessageLoopCurrentForIO is a best effort at determining
+  // whether the current thread is a networking thread.
+  return base::MessageLoopCurrentForIO::IsSet();
 }
 
 }  // namespace
diff --git a/remoting/host/desktop_process.cc b/remoting/host/desktop_process.cc
index 02989595..73ac698 100644
--- a/remoting/host/desktop_process.cc
+++ b/remoting/host/desktop_process.cc
@@ -41,7 +41,7 @@
       daemon_channel_handle_(std::move(daemon_channel_handle)),
       weak_factory_(this) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 }
 
 DesktopProcess::~DesktopProcess() {
diff --git a/remoting/host/mac/permission_utils.mm b/remoting/host/mac/permission_utils.mm
index 6cb20cb..f293042 100644
--- a/remoting/host/mac/permission_utils.mm
+++ b/remoting/host/mac/permission_utils.mm
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
@@ -22,7 +23,7 @@
 constexpr NSString* kServiceScriptName = @"org.chromium.chromoting.me2me.sh";
 
 void ShowPermissionDialog() {
-  NSAlert* alert = [[NSAlert alloc] init];
+  base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
   [alert setMessageText:l10n_util::GetNSString(
                             IDS_ACCESSIBILITY_PERMISSION_DIALOG_TITLE)];
   [alert setInformativeText:
@@ -42,13 +43,14 @@
   // Increase the alert width so the title doesn't wrap and the body text is
   // less scrunched.  Note that we only want to set a min-width, we don't
   // want to shrink the dialog if it is already larger than our min value.
-  NSRect frame = [alert.window frame];
+  NSWindow* alert_window = [alert window];
+  NSRect frame = [alert_window frame];
   if (frame.size.width < kMinDialogWidthPx)
     frame.size.width = kMinDialogWidthPx;
-  [alert.window setFrame:frame display:YES];
+  [alert_window setFrame:frame display:YES];
 
   [alert setAlertStyle:NSAlertStyleWarning];
-  [alert.window makeKeyWindow];
+  [alert_window makeKeyWindow];
   if ([alert runModal] == NSAlertFirstButtonReturn) {
     // Launch the Security and Preferences pane with Accessibility selected.
     [[NSWorkspace sharedWorkspace]
diff --git a/remoting/host/win/rdp_client.cc b/remoting/host/win/rdp_client.cc
index e7b57d2..01a7caf 100644
--- a/remoting/host/win/rdp_client.cc
+++ b/remoting/host/win/rdp_client.cc
@@ -139,7 +139,7 @@
     return;
   }
 
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(!rdp_client_window_);
   DCHECK(!self_.get());
 
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 238f76e..86a1b09 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -55,6 +55,7 @@
     deps += [
       "//services/data_decoder/public/cpp/android:safe_json_java",
       "//services/device:java",
+      "//services/media_session/public/cpp/android:media_session_java",
 
       # Some tests make network requests.
       "//net/android:net_java",
diff --git a/services/device/screen_orientation/screen_orientation_listener_android.cc b/services/device/screen_orientation/screen_orientation_listener_android.cc
index 776a065..9927b33 100644
--- a/services/device/screen_orientation/screen_orientation_listener_android.cc
+++ b/services/device/screen_orientation/screen_orientation_listener_android.cc
@@ -23,7 +23,7 @@
     : listeners_count_(0) {}
 
 ScreenOrientationListenerAndroid::~ScreenOrientationListenerAndroid() {
-  DCHECK(base::MessageLoopForIO::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForIO::IsSet());
   if (listeners_count_ > 0) {
     Java_ScreenOrientationListener_startAccurateListening(
         base::android::AttachCurrentThread());
@@ -31,7 +31,7 @@
 }
 
 void ScreenOrientationListenerAndroid::Start() {
-  DCHECK(base::MessageLoopForIO::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForIO::IsSet());
   ++listeners_count_;
   if (listeners_count_ == 1) {
     // Ask the ScreenOrientationListener (Java) to start accurately listening to
@@ -43,7 +43,7 @@
 }
 
 void ScreenOrientationListenerAndroid::Stop() {
-  DCHECK(base::MessageLoopForIO::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForIO::IsSet());
   DCHECK(listeners_count_ > 0);
   --listeners_count_;
   if (listeners_count_ == 0) {
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc
index f3a2a989..8f0f97b5 100644
--- a/services/identity/public/cpp/identity_manager.cc
+++ b/services/identity/public/cpp/identity_manager.cc
@@ -247,10 +247,8 @@
   }
 }
 
-// Populates and returns an AccountInfo object corresponding to |account_id|,
-// which must be an account with a refresh token.
 AccountInfo IdentityManager::GetAccountInfoForAccountWithRefreshToken(
-    std::string account_id) const {
+    const std::string& account_id) const {
   DCHECK(HasAccountWithRefreshToken(account_id));
 
   AccountInfo account_info =
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index c8d82f3..d126526c 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -344,7 +344,7 @@
   // Populates and returns an AccountInfo object corresponding to |account_id|,
   // which must be an account with a refresh token.
   AccountInfo GetAccountInfoForAccountWithRefreshToken(
-      std::string account_id) const;
+      const std::string& account_id) const;
 
   // SigninManagerBase::Observer:
   void GoogleSigninSucceeded(const AccountInfo& account_info) override;
diff --git a/services/identity/public/objc/identity_manager_observer_bridge.h b/services/identity/public/objc/identity_manager_observer_bridge.h
index 32c5ae65..262d6e5 100644
--- a/services/identity/public/objc/identity_manager_observer_bridge.h
+++ b/services/identity/public/objc/identity_manager_observer_bridge.h
@@ -23,7 +23,10 @@
 // these semantics.
 
 - (void)onPrimaryAccountSet:(const AccountInfo&)primaryAccountInfo;
+- (void)onPrimaryAccountSet:(const AccountInfo&)primaryAccountInfo
+               withPassword:(const std::string&)password;
 - (void)onPrimaryAccountCleared:(const AccountInfo&)previousPrimaryAccountInfo;
+- (void)onPrimaryAccountSigninFailed:(const GoogleServiceAuthError&)error;
 - (void)onRefreshTokenUpdatedForAccount:(const AccountInfo&)accountInfo
                                   valid:(BOOL)isValid;
 - (void)onRefreshTokenRemovedForAccount:(const std::string&)accountId;
@@ -47,8 +50,12 @@
 
   // IdentityManager::Observer.
   void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSetWithPassword(const AccountInfo& primary_account_info,
+                                       const std::string& password) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
+  void OnPrimaryAccountSigninFailed(
+      const GoogleServiceAuthError& error) override;
   void OnRefreshTokenUpdatedForAccount(const AccountInfo& account_info,
                                        bool is_valid) override;
   void OnRefreshTokenRemovedForAccount(const std::string& account_id) override;
diff --git a/services/identity/public/objc/identity_manager_observer_bridge.mm b/services/identity/public/objc/identity_manager_observer_bridge.mm
index 0f4d265..aaf0b6b 100644
--- a/services/identity/public/objc/identity_manager_observer_bridge.mm
+++ b/services/identity/public/objc/identity_manager_observer_bridge.mm
@@ -28,6 +28,15 @@
   }
 }
 
+void IdentityManagerObserverBridge::OnPrimaryAccountSetWithPassword(
+    const AccountInfo& primary_account_info,
+    const std::string& password) {
+  if ([delegate_ respondsToSelector:@selector(onPrimaryAccountSet:
+                                                     withPassword:)]) {
+    [delegate_ onPrimaryAccountSet:primary_account_info withPassword:password];
+  }
+}
+
 void IdentityManagerObserverBridge::OnPrimaryAccountCleared(
     const AccountInfo& previous_primary_account_info) {
   if ([delegate_ respondsToSelector:@selector(onPrimaryAccountCleared:)]) {
@@ -35,6 +44,13 @@
   }
 }
 
+void IdentityManagerObserverBridge::OnPrimaryAccountSigninFailed(
+    const GoogleServiceAuthError& error) {
+  if ([delegate_ respondsToSelector:@selector(onPrimaryAccountSigninFailed:)]) {
+    [delegate_ onPrimaryAccountSigninFailed:error];
+  }
+}
+
 void IdentityManagerObserverBridge::OnRefreshTokenUpdatedForAccount(
     const AccountInfo& account_info,
     bool is_valid) {
diff --git a/services/media_session/DEPS b/services/media_session/DEPS
new file mode 100644
index 0000000..b8a225f
--- /dev/null
+++ b/services/media_session/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+jni",
+  "+ui/gfx",
+]
diff --git a/services/media_session/public/cpp/BUILD.gn b/services/media_session/public/cpp/BUILD.gn
index 0f6b88d7..3b90b616 100644
--- a/services/media_session/public/cpp/BUILD.gn
+++ b/services/media_session/public/cpp/BUILD.gn
@@ -6,15 +6,24 @@
   output_name = "media_session_cpp"
 
   sources = [
+    "media_metadata.cc",
+    "media_metadata.h",
     "switches.cc",
     "switches.h",
   ]
 
   deps = [
     "//base",
+    "//ui/gfx/geometry",
+    "//url",
   ]
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
+  if (is_android) {
+    sources += [ "media_metadata_android.cc" ]
+    deps += [ "android:media_session_jni_headers" ]
+  }
+
   defines = [ "IS_MEDIA_SESSION_CPP_IMPL" ]
 }
diff --git a/services/media_session/public/cpp/OWNERS b/services/media_session/public/cpp/OWNERS
new file mode 100644
index 0000000..7aebc8abb
--- /dev/null
+++ b/services/media_session/public/cpp/OWNERS
@@ -0,0 +1,4 @@
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/services/media_session/public/cpp/android/BUILD.gn b/services/media_session/public/cpp/android/BUILD.gn
new file mode 100644
index 0000000..c7de4d2a
--- /dev/null
+++ b/services/media_session/public/cpp/android/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+_jni_sources =
+    [ "java/src/org/chromium/services/media_session/MediaMetadata.java" ]
+
+generate_jni("media_session_jni_headers") {
+  sources = _jni_sources
+  jni_package = "media_metadata"
+}
+
+if (current_toolchain == default_toolchain) {
+  android_library("media_session_java") {
+    deps = [
+      "//base:base_java",
+    ]
+    java_files = _jni_sources
+  }
+}
diff --git a/services/media_session/public/cpp/android/java/src/org/chromium/services/media_session/MediaMetadata.java b/services/media_session/public/cpp/android/java/src/org/chromium/services/media_session/MediaMetadata.java
new file mode 100644
index 0000000..ce06aae
--- /dev/null
+++ b/services/media_session/public/cpp/android/java/src/org/chromium/services/media_session/MediaMetadata.java
@@ -0,0 +1,232 @@
+// 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.services.media_session;
+
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The MediaMetadata class carries information related to a media session. It is
+ * the Java counterpart of media_session::MediaMetadata.
+ */
+@JNINamespace("media_session")
+public final class MediaMetadata {
+    /**
+     * The MediaImage class carries the artwork information in MediaMetadata. It is the Java
+     * counterpart of media_session::MediaMetadata::MediaImage.
+     */
+    public static final class MediaImage {
+        @NonNull
+        private String mSrc;
+
+        private String mType;
+
+        @NonNull
+        private List<Rect> mSizes = new ArrayList<Rect>();
+
+        /**
+         * Creates a new MediaImage.
+         */
+        public MediaImage(@NonNull String src, @NonNull String type, @NonNull List<Rect> sizes) {
+            mSrc = src;
+            mType = type;
+            mSizes = sizes;
+        }
+
+        /**
+         * @return The URL of this MediaImage.
+         */
+        @NonNull
+        public String getSrc() {
+            return mSrc;
+        }
+
+        /**
+         * @return The MIME type of this MediaImage.
+         */
+        public String getType() {
+            return mType;
+        }
+
+        /**
+         * @return The hinted sizes of this MediaImage.
+         */
+        public List<Rect> getSizes() {
+            return mSizes;
+        }
+
+        /**
+         * Sets the URL of this MediaImage.
+         */
+        public void setSrc(@NonNull String src) {
+            mSrc = src;
+        }
+
+        /**
+         * Sets the MIME type of this MediaImage.
+         */
+        public void setType(@NonNull String type) {
+            mType = type;
+        }
+
+        /**
+         * Sets the sizes of this MediaImage.
+         */
+        public void setSizes(@NonNull List<Rect> sizes) {
+            mSizes = sizes;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) return true;
+            if (!(obj instanceof MediaImage)) return false;
+
+            MediaImage other = (MediaImage) obj;
+            return TextUtils.equals(mSrc, other.mSrc) && TextUtils.equals(mType, other.mType)
+                    && mSizes.equals(other.mSizes);
+        }
+
+        /**
+         * @return The hash code of this {@link MediaImage}. The method uses the same algorithm in
+         * {@link java.util.List} for combinine hash values.
+         */
+        @Override
+        public int hashCode() {
+            int result = mSrc.hashCode();
+            result = 31 * result + mType.hashCode();
+            result = 31 * result + mSizes.hashCode();
+            return result;
+        }
+    }
+
+    @NonNull
+    private String mTitle;
+
+    @NonNull
+    private String mArtist;
+
+    @NonNull
+    private String mAlbum;
+
+    @NonNull
+    private List<MediaImage> mArtwork = new ArrayList<MediaImage>();
+
+    /**
+     * Returns the title associated with the media session.
+     */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Returns the artist name associated with the media session.
+     */
+    public String getArtist() {
+        return mArtist;
+    }
+
+    /**
+     * Returns the album name associated with the media session.
+     */
+    public String getAlbum() {
+        return mAlbum;
+    }
+
+    public List<MediaImage> getArtwork() {
+        return mArtwork;
+    }
+
+    /**
+     * Sets the title associated with the media session.
+     * @param title The title to use for the media session.
+     */
+    public void setTitle(@NonNull String title) {
+        mTitle = title;
+    }
+
+    /**
+     * Sets the arstist name associated with the media session.
+     * @param arstist The artist name to use for the media session.
+     */
+    public void setArtist(@NonNull String artist) {
+        mArtist = artist;
+    }
+
+    /**
+     * Sets the album name associated with the media session.
+     * @param album The album name to use for the media session.
+     */
+    public void setAlbum(@NonNull String album) {
+        mAlbum = album;
+    }
+
+    /**
+     * Create a new {@link MediaImage} from the C++ code, and add it to the Metadata.
+     * @param src The URL of the image.
+     * @param type The MIME type of the image.
+     * @param flattenedSizes The flattened array of image sizes. In native code, it is of type
+     *         `std::vector<gfx::Size>` before flattening.
+     */
+    @CalledByNative
+    private void createAndAddMediaImage(String src, String type, int[] flattenedSizes) {
+        assert(flattenedSizes.length % 2) == 0;
+        List<Rect> sizes = new ArrayList<Rect>();
+        for (int i = 0; (i + 1) < flattenedSizes.length; i += 2) {
+            sizes.add(new Rect(0, 0, flattenedSizes[i], flattenedSizes[i + 1]));
+        }
+        mArtwork.add(new MediaImage(src, type, sizes));
+    }
+
+    /**
+     * Creates a new MediaMetadata from the C++ code. This is exactly like the
+     * constructor below apart that it can be called by native code.
+     */
+    @CalledByNative
+    private static MediaMetadata create(String title, String artist, String album) {
+        return new MediaMetadata(title, artist, album);
+    }
+
+    /**
+     * Creates a new MediaMetadata.
+     */
+    public MediaMetadata(@NonNull String title, @NonNull String artist, @NonNull String album) {
+        mTitle = title;
+        mArtist = artist;
+        mAlbum = album;
+    }
+
+    /**
+     * Comparing MediaMetadata is expensive and should be used sparingly
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) return true;
+        if (!(obj instanceof MediaMetadata)) return false;
+
+        MediaMetadata other = (MediaMetadata) obj;
+        return TextUtils.equals(mTitle, other.mTitle) && TextUtils.equals(mArtist, other.mArtist)
+                && TextUtils.equals(mAlbum, other.mAlbum) && mArtwork.equals(other.mArtwork);
+    }
+
+    /**
+     * @return The hash code of this {@link MediaMetadata}. The method uses the same algorithm in
+     * {@link java.util.List} for combinine hash values.
+     */
+    @Override
+    public int hashCode() {
+        int result = mTitle.hashCode();
+        result = 31 * result + mArtist.hashCode();
+        result = 31 * result + mAlbum.hashCode();
+        result = 31 * result + mArtwork.hashCode();
+        return result;
+    }
+}
diff --git a/services/media_session/public/cpp/media_metadata.cc b/services/media_session/public/cpp/media_metadata.cc
new file mode 100644
index 0000000..79468f9
--- /dev/null
+++ b/services/media_session/public/cpp/media_metadata.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/media_session/public/cpp/media_metadata.h"
+
+#include <algorithm>
+#include <iterator>
+
+namespace media_session {
+
+MediaMetadata::MediaImage::MediaImage() = default;
+
+MediaMetadata::MediaImage::MediaImage(const MediaImage& other) = default;
+
+MediaMetadata::MediaImage::~MediaImage() = default;
+
+bool MediaMetadata::MediaImage::operator==(
+    const MediaMetadata::MediaImage& other) const {
+  return src == other.src && type == other.type && sizes == other.sizes;
+}
+
+MediaMetadata::MediaMetadata() = default;
+
+MediaMetadata::~MediaMetadata() = default;
+
+MediaMetadata::MediaMetadata(const MediaMetadata& other) = default;
+
+bool MediaMetadata::operator==(const MediaMetadata& other) const {
+  return title == other.title && artist == other.artist &&
+         album == other.album && artwork == other.artwork;
+}
+
+bool MediaMetadata::operator!=(const MediaMetadata& other) const {
+  return !(*this == other);
+}
+
+}  // namespace media_session
diff --git a/services/media_session/public/cpp/media_metadata.h b/services/media_session/public/cpp/media_metadata.h
new file mode 100644
index 0000000..471737fa
--- /dev/null
+++ b/services/media_session/public/cpp/media_metadata.h
@@ -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.
+
+#ifndef SERVICES_MEDIA_SESSION_PUBLIC_CPP_MEDIA_METADATA_H_
+#define SERVICES_MEDIA_SESSION_PUBLIC_CPP_MEDIA_METADATA_H_
+
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+#include "ui/gfx/geometry/size.h"
+#include "url/gurl.h"
+
+#if defined(OS_ANDROID)
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+
+#endif  // defined(OS_ANDROID)
+
+namespace media_session {
+
+// The MediaMetadata is a structure carrying information associated to a
+// MediaSession.
+struct COMPONENT_EXPORT(MEDIA_SESSION_CPP) MediaMetadata {
+  // Structure representing an MediaImage as per the MediaSession API, see:
+  // https://wicg.github.io/mediasession/#dictdef-mediaimage
+  struct COMPONENT_EXPORT(MEDIA_SESSION_CPP) MediaImage {
+    MediaImage();
+    MediaImage(const MediaImage& other);
+    ~MediaImage();
+
+    bool operator==(const MediaImage& other) const;
+
+    // MUST be a valid url. If an icon doesn't have a valid URL, it will not be
+    // successfully parsed, thus will not be represented in the Manifest.
+    GURL src;
+
+    // Empty if the parsing failed or the field was not present. The type can be
+    // any string and doesn't have to be a valid image MIME type at this point.
+    // It is up to the consumer of the object to check if the type matches a
+    // supported type.
+    base::string16 type;
+
+    // Empty if the parsing failed, the field was not present or empty.
+    // The special value "any" is represented by gfx::Size(0, 0).
+    std::vector<gfx::Size> sizes;
+  };
+
+  MediaMetadata();
+  ~MediaMetadata();
+
+  MediaMetadata(const MediaMetadata& other);
+
+  bool operator==(const MediaMetadata& other) const;
+  bool operator!=(const MediaMetadata& other) const;
+
+#if defined(OS_ANDROID)
+  // Creates a Java MediaMetadata instance and returns the JNI ref.
+  base::android::ScopedJavaLocalRef<jobject> CreateJavaObject(JNIEnv* env);
+#endif
+
+  // Title associated to the MediaSession.
+  base::string16 title;
+
+  // Artist associated to the MediaSession.
+  base::string16 artist;
+
+  // Album associated to the MediaSession.
+  base::string16 album;
+
+  // Artwork associated to the MediaSession.
+  std::vector<MediaImage> artwork;
+};
+
+}  // namespace media_session
+
+#endif  // SERVICES_MEDIA_SESSION_PUBLIC_CPP_MEDIA_METADATA_H_
diff --git a/services/media_session/public/cpp/media_metadata_android.cc b/services/media_session/public/cpp/media_metadata_android.cc
new file mode 100644
index 0000000..346ebf2
--- /dev/null
+++ b/services/media_session/public/cpp/media_metadata_android.cc
@@ -0,0 +1,60 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/media_session/public/cpp/media_metadata.h"
+
+#include <string>
+#include <vector>
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "jni/MediaMetadata_jni.h"
+
+using base::android::ScopedJavaLocalRef;
+
+namespace media_session {
+
+namespace {
+
+std::vector<int> GetFlattenedSizeArray(const std::vector<gfx::Size>& sizes) {
+  std::vector<int> flattened_array;
+  flattened_array.reserve(2 * sizes.size());
+  for (const auto& size : sizes) {
+    flattened_array.push_back(size.width());
+    flattened_array.push_back(size.height());
+  }
+  return flattened_array;
+}
+
+}  // anonymous namespace
+
+base::android::ScopedJavaLocalRef<jobject> MediaMetadata::CreateJavaObject(
+    JNIEnv* env) {
+  ScopedJavaLocalRef<jstring> j_title(
+      base::android::ConvertUTF16ToJavaString(env, title));
+  ScopedJavaLocalRef<jstring> j_artist(
+      base::android::ConvertUTF16ToJavaString(env, artist));
+  ScopedJavaLocalRef<jstring> j_album(
+      base::android::ConvertUTF16ToJavaString(env, album));
+
+  ScopedJavaLocalRef<jobject> j_metadata =
+      Java_MediaMetadata_create(env, j_title, j_artist, j_album);
+
+  for (const auto& image : artwork) {
+    std::string src = image.src.spec();
+    ScopedJavaLocalRef<jstring> j_src(
+        base::android::ConvertUTF8ToJavaString(env, src));
+    ScopedJavaLocalRef<jstring> j_type(
+        base::android::ConvertUTF16ToJavaString(env, image.type));
+    ScopedJavaLocalRef<jintArray> j_sizes(
+        base::android::ToJavaIntArray(env, GetFlattenedSizeArray(image.sizes)));
+
+    Java_MediaMetadata_createAndAddMediaImage(env, j_metadata, j_src, j_type,
+                                              j_sizes);
+  }
+
+  return j_metadata;
+}
+
+}  // namespace media_session
diff --git a/services/media_session/public/cpp/media_session.typemap b/services/media_session/public/cpp/media_session.typemap
new file mode 100644
index 0000000..94b58a9
--- /dev/null
+++ b/services/media_session/public/cpp/media_session.typemap
@@ -0,0 +1,22 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//services/media_session/public/mojom/media_session.mojom"
+public_headers = [ "//services/media_session/public/cpp/media_metadata.h" ]
+traits_headers =
+    [ "//services/media_session/public/cpp/media_session_mojom_traits.h" ]
+public_deps = [
+  "//services/media_session/public/cpp",
+]
+deps = [
+  "//ui/gfx/geometry/mojo:struct_traits",
+]
+type_mappings = [
+  "media_session.mojom.MediaImage=media_session::MediaMetadata::MediaImage",
+  "media_session.mojom.MediaMetadata=media_session::MediaMetadata",
+]
+sources = [
+  "//services/media_session/public/cpp/media_session_mojom_traits.cc",
+  "//services/media_session/public/cpp/media_session_mojom_traits.h",
+]
diff --git a/services/media_session/public/cpp/media_session_mojom_traits.cc b/services/media_session/public/cpp/media_session_mojom_traits.cc
new file mode 100644
index 0000000..067c7a2
--- /dev/null
+++ b/services/media_session/public/cpp/media_session_mojom_traits.cc
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/media_session/public/cpp/media_session_mojom_traits.h"
+
+#include "mojo/public/cpp/base/string16_mojom_traits.h"
+#include "ui/gfx/geometry/mojo/geometry_struct_traits.h"
+#include "url/mojom/url_gurl_mojom_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<media_session::mojom::MediaImageDataView,
+                  media_session::MediaMetadata::MediaImage>::
+    Read(media_session::mojom::MediaImageDataView data,
+         media_session::MediaMetadata::MediaImage* out) {
+  if (!data.ReadSrc(&out->src))
+    return false;
+  if (!data.ReadType(&out->type))
+    return false;
+  if (!data.ReadSizes(&out->sizes))
+    return false;
+
+  return true;
+}
+
+// static
+bool StructTraits<media_session::mojom::MediaMetadataDataView,
+                  media_session::MediaMetadata>::
+    Read(media_session::mojom::MediaMetadataDataView data,
+         media_session::MediaMetadata* out) {
+  if (!data.ReadTitle(&out->title))
+    return false;
+  if (!data.ReadArtist(&out->artist))
+    return false;
+  if (!data.ReadAlbum(&out->album))
+    return false;
+  if (!data.ReadArtwork(&out->artwork))
+    return false;
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/services/media_session/public/cpp/media_session_mojom_traits.h b/services/media_session/public/cpp/media_session_mojom_traits.h
new file mode 100644
index 0000000..b49dfa7
--- /dev/null
+++ b/services/media_session/public/cpp/media_session_mojom_traits.h
@@ -0,0 +1,63 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_MEDIA_SESSION_PUBLIC_CPP_MEDIA_SESSION_MOJOM_TRAITS_H_
+#define SERVICES_MEDIA_SESSION_PUBLIC_CPP_MEDIA_SESSION_MOJOM_TRAITS_H_
+
+#include "services/media_session/public/mojom/media_session.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<media_session::mojom::MediaImageDataView,
+                    media_session::MediaMetadata::MediaImage> {
+  static const GURL& src(
+      const media_session::MediaMetadata::MediaImage& image) {
+    return image.src;
+  }
+
+  static const base::string16& type(
+      const media_session::MediaMetadata::MediaImage& image) {
+    return image.type;
+  }
+
+  static const std::vector<gfx::Size>& sizes(
+      const media_session::MediaMetadata::MediaImage& image) {
+    return image.sizes;
+  }
+
+  static bool Read(media_session::mojom::MediaImageDataView data,
+                   media_session::MediaMetadata::MediaImage* out);
+};
+
+template <>
+struct StructTraits<media_session::mojom::MediaMetadataDataView,
+                    media_session::MediaMetadata> {
+  static const base::string16& title(
+      const media_session::MediaMetadata& metadata) {
+    return metadata.title;
+  }
+
+  static const base::string16& artist(
+      const media_session::MediaMetadata& metadata) {
+    return metadata.artist;
+  }
+
+  static const base::string16& album(
+      const media_session::MediaMetadata& metadata) {
+    return metadata.album;
+  }
+
+  static const std::vector<media_session::MediaMetadata::MediaImage>& artwork(
+      const media_session::MediaMetadata& metadata) {
+    return metadata.artwork;
+  }
+
+  static bool Read(media_session::mojom::MediaMetadataDataView data,
+                   media_session::MediaMetadata* out);
+};
+
+}  // namespace mojo
+
+#endif  // SERVICES_MEDIA_SESSION_PUBLIC_CPP_MEDIA_SESSION_MOJOM_TRAITS_H_
diff --git a/services/media_session/public/cpp/typemaps.gni b/services/media_session/public/cpp/typemaps.gni
new file mode 100644
index 0000000..280e7e0
--- /dev/null
+++ b/services/media_session/public/cpp/typemaps.gni
@@ -0,0 +1,5 @@
+# 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.
+
+typemaps = [ "//services/media_session/public/cpp/media_session.typemap" ]
diff --git a/services/media_session/public/mojom/BUILD.gn b/services/media_session/public/mojom/BUILD.gn
index 21d319d..45b215a 100644
--- a/services/media_session/public/mojom/BUILD.gn
+++ b/services/media_session/public/mojom/BUILD.gn
@@ -4,10 +4,7 @@
 
 import("//mojo/public/tools/bindings/mojom.gni")
 
-mojom_component("mojom") {
-  output_prefix = "media_session_public_mojom"
-  macro_prefix = "MEDIA_SESSION_PUBLIC_MOJOM"
-
+mojom("mojom") {
   sources = [
     "audio_focus.mojom",
     "constants.mojom",
@@ -17,5 +14,15 @@
 
   public_deps = [
     "//mojo/public/mojom/base",
+    "//ui/gfx/geometry/mojo",
+    "//url/mojom:url_mojom_gurl",
   ]
+
+  # The blink variant of the Device Service mojom are depended on by the
+  # blink platform target. All blink variant mojoms use WTF types, which are
+  # part of the blink platform component. In order to avoid a dependency cycle,
+  # these targets must be part of that component.
+  export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
+  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+  export_header_blink = "third_party/blink/public/platform/web_common.h"
 }
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom
index da252ec..ba5cba6 100644
--- a/services/media_session/public/mojom/media_session.mojom
+++ b/services/media_session/public/mojom/media_session.mojom
@@ -4,7 +4,10 @@
 
 module media_session.mojom;
 
+import "mojo/public/mojom/base/string16.mojom";
 import "mojo/public/mojom/base/time.mojom";
+import "ui/gfx/geometry/mojo/geometry.mojom";
+import "url/mojom/url.mojom";
 
 // Next MinVersion: 2
 
@@ -25,6 +28,23 @@
   kSeekForward,
 };
 
+// Album art in MediaMetadata
+// Spec: https://wicg.github.io/mediasession/
+struct MediaImage {
+  url.mojom.Url src;
+  mojo_base.mojom.String16 type;
+  array<gfx.mojom.Size> sizes;
+};
+
+// MediaMetadata
+// Spec: https://wicg.github.io/mediasession/
+struct MediaMetadata {
+  mojo_base.mojom.String16 title;
+  mojo_base.mojom.String16 artist;
+  mojo_base.mojom.String16 album;
+  array<MediaImage> artwork;
+};
+
 // Contains state information about a MediaSession.
 struct MediaSessionInfo {
   [Extensible]
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 8ba9734e..d3a0ddfc 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -157,6 +157,10 @@
 #define SK_DISABLE_EXPLICIT_GPU_RESOURCE_ALLOCATION
 #endif
 
+#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
+#define SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
+#endif
+
 // Max. verb count for paths rendered by the edge-AA tessellating path renderer.
 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 100
 
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 17ff1580..0754f51 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -113,7 +113,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 25,
@@ -204,7 +204,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 7,
@@ -250,7 +250,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 7,
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 42b0c8e4..384be7e 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -31,7 +31,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -72,7 +72,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -113,7 +113,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -154,7 +154,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -197,7 +197,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 16,
@@ -244,7 +244,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 16,
@@ -291,7 +291,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 8,
@@ -334,7 +334,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -373,7 +373,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -412,7 +412,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -453,7 +453,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -494,7 +494,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -535,7 +535,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 5,
@@ -578,7 +578,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -617,7 +617,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -656,7 +656,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -696,7 +696,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 5,
@@ -749,7 +749,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 19,
@@ -795,7 +795,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -836,7 +836,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -877,7 +877,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -918,7 +918,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -959,7 +959,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1000,7 +1000,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1043,7 +1043,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 16,
@@ -1098,7 +1098,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1137,7 +1137,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1176,7 +1176,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1215,7 +1215,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1254,7 +1254,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1293,7 +1293,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1334,7 +1334,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 26,
@@ -1377,7 +1377,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1416,7 +1416,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1457,7 +1457,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 26,
@@ -1500,7 +1500,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1539,7 +1539,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1578,7 +1578,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1617,7 +1617,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1656,7 +1656,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1697,7 +1697,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 26,
@@ -1745,7 +1745,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1784,7 +1784,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1823,7 +1823,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1862,7 +1862,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 1,
@@ -1903,7 +1903,7 @@
             }
           ],
           "expiration": 7200,
-          "hard_timeout": 25200,
+          "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
           "shards": 26,
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index a1c7e481..481c0e7 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -37,7 +37,6 @@
 
 # https://crbug.com/721403
 -ContextMenuBrowserTest.DataSaverOpenOrigImageInNewTab
--DataReductionProxyBrowsertest.UMAMetricsRecorded
 
 # NOTE: if adding an exclusion for an existing failure (e.g. additional test for
 # feature X that is already not working), please add it beside the existing
diff --git a/testing/libfuzzer/clusterfuzz.md b/testing/libfuzzer/clusterfuzz.md
index 8349c659..4005a2a 100644
--- a/testing/libfuzzer/clusterfuzz.md
+++ b/testing/libfuzzer/clusterfuzz.md
@@ -8,7 +8,7 @@
 ## Status Links
 
 * [Buildbot] - status of all libFuzzer builds.
-* [ClusterFuzz Fuzzer Status] - fuzzing metrics.
+* [ClusterFuzz Fuzzer Stats] - fuzzing metrics.
 * [Code Coverage] - code coverage of libFuzzer targets in Chrome.
 * [ClusterFuzz libFuzzer Logs] - individual fuzz target run logs.
 * [Corpus GCS Bucket] - current corpus for each fuzz target. Can be used to
@@ -27,7 +27,7 @@
 * Fuzzing corpus is maintained for each fuzz target in [Corpus GCS Bucket]. Once
 a day, the corpus is minimized to reduce number of duplicates and/or reduce
 effect of parasitic coverage.
-* [ClusterFuzz Fuzzer Status] displays fuzzer runtime metrics as well as
+* [ClusterFuzz Fuzzer Stats] displays fuzzer runtime metrics as well as
 provides links to crashes and coverage reports.
 
 
diff --git a/third_party/blink/public/web/web_media_player_action.h b/third_party/blink/public/web/web_media_player_action.h
index 674b900b..95fd0f72 100644
--- a/third_party/blink/public/web/web_media_player_action.h
+++ b/third_party/blink/public/web/web_media_player_action.h
@@ -34,20 +34,21 @@
 namespace blink {
 
 struct WebMediaPlayerAction {
-  enum Type {
-    kUnknown,
+  enum class Type {
     kPlay,
     kMute,
     kLoop,
     kControls,
     kPictureInPicture,
-    kTypeLast = kPictureInPicture
+    kMaxValue = kPictureInPicture
   };
 
-  Type type;
-  bool enable;
+  Type type = Type::kMaxValue;
+  bool enable = false;
 
-  WebMediaPlayerAction() : type(kUnknown), enable(false) {}
+  // The default constructor is needed for IPC. If this is moved to mojo, we may
+  // be able to delete it and enforce that parameters are used.
+  WebMediaPlayerAction() = default;
   WebMediaPlayerAction(Type type, bool enable) : type(type), enable(enable) {}
 };
 
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 2d787fe..40e1f65 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
@@ -2498,23 +2498,23 @@
 
   HTMLMediaElement* media_element = ToHTMLMediaElement(node);
   switch (action.type) {
-    case WebMediaPlayerAction::kPlay:
+    case WebMediaPlayerAction::Type::kPlay:
       if (action.enable)
         media_element->Play();
       else
         media_element->pause();
       break;
-    case WebMediaPlayerAction::kMute:
+    case WebMediaPlayerAction::Type::kMute:
       media_element->setMuted(action.enable);
       break;
-    case WebMediaPlayerAction::kLoop:
+    case WebMediaPlayerAction::Type::kLoop:
       media_element->SetLoop(action.enable);
       break;
-    case WebMediaPlayerAction::kControls:
+    case WebMediaPlayerAction::Type::kControls:
       media_element->SetBooleanAttribute(html_names::kControlsAttr,
                                          action.enable);
       break;
-    case WebMediaPlayerAction::kPictureInPicture:
+    case WebMediaPlayerAction::Type::kPictureInPicture:
       DCHECK(media_element->IsHTMLVideoElement());
       if (action.enable) {
         PictureInPictureController::From(node->GetDocument())
@@ -2524,8 +2524,6 @@
             .ExitPictureInPicture(ToHTMLVideoElement(media_element), nullptr);
       }
       break;
-    default:
-      NOTREACHED();
   }
 }
 
diff --git a/third_party/blink/renderer/core/timing/performance_mark.idl b/third_party/blink/renderer/core/timing/performance_mark.idl
index 24439659..2dd8d60f 100644
--- a/third_party/blink/renderer/core/timing/performance_mark.idl
+++ b/third_party/blink/renderer/core/timing/performance_mark.idl
@@ -25,6 +25,7 @@
 
 // https://w3c.github.io/user-timing/#performancemark
 
+[Exposed=(Window,Worker)]
 interface PerformanceMark : PerformanceEntry {
     [CallWith=ScriptState, RuntimeEnabled=CustomUserTiming] readonly attribute any detail;
 };
diff --git a/third_party/blink/renderer/core/timing/performance_measure.idl b/third_party/blink/renderer/core/timing/performance_measure.idl
index b9546a6..2ee528c 100644
--- a/third_party/blink/renderer/core/timing/performance_measure.idl
+++ b/third_party/blink/renderer/core/timing/performance_measure.idl
@@ -25,6 +25,7 @@
 
 // https://w3c.github.io/user-timing/#performancemeasure
 
+[Exposed=(Window,Worker)]
 interface PerformanceMeasure : PerformanceEntry {
   [CallWith=ScriptState, RuntimeEnabled=CustomUserTiming] readonly attribute any detail;
 };
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index 8dfa49b0..1cb3c98 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -619,6 +619,17 @@
   return IsActive(play_state_);
 }
 
+base::Optional<double> WorkletAnimation::CurrentTime() const {
+  bool is_null;
+  double timeline_time = timeline_->currentTime(is_null);
+  if (is_null)
+    return base::nullopt;
+  if (timeline_->IsScrollTimeline())
+    return timeline_time;
+  DCHECK(start_time_);
+  return timeline_time - start_time_->InSecondsF();
+}
+
 void WorkletAnimation::UpdateInputState(
     AnimationWorkletDispatcherInput* input_state) {
   if (!running_on_main_thread_) {
@@ -632,8 +643,8 @@
   // ScrollTimeline animation doesn't require start_time_ to be set.
   DCHECK(start_time_ || timeline_->IsScrollTimeline());
   DCHECK(last_current_time_ || !was_active);
-  bool is_null;
-  double current_time = timeline_->currentTime(is_null);
+  double current_time =
+      CurrentTime().value_or(std::numeric_limits<double>::quiet_NaN());
 
   bool did_time_change =
       !last_current_time_ || current_time != last_current_time_->InSecondsF();
@@ -641,10 +652,7 @@
   // is resolved, we apply the last current time to the animation if the scroll
   // timeline becomes newly inactive. See https://crbug.com/906050.
   last_current_time_ = base::TimeDelta::FromSecondsD(current_time);
-
   if (!was_active && is_active) {
-    // TODO(yigu): current_time should be offset by start_time_ for animations
-    // with document timeline. https://crbug.com/905405.
     input_state->Add(
         {id_,
          std::string(animator_name_.Ascii().data(), animator_name_.length()),
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
index 015121b3..e680957 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
@@ -145,6 +145,7 @@
   void SetPlayState(const Animation::AnimationPlayState& state) {
     play_state_ = state;
   }
+  base::Optional<double> CurrentTime() const;
 
   unsigned sequence_number_;
 
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
index 225c08077..e9239db 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
@@ -10,7 +10,9 @@
 #include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/animation/keyframe_effect.h"
 #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h"
+#include "third_party/blink/renderer/core/animation/scroll_timeline.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 
@@ -39,12 +41,16 @@
   return KeyframeEffect::Create(element, CreateEffectModel(), timing);
 }
 
-WorkletAnimation* CreateWorkletAnimation(ScriptState* script_state,
-                                         Element* element) {
+WorkletAnimation* CreateWorkletAnimation(
+    ScriptState* script_state,
+    Element* element,
+    ScrollTimeline* scroll_timeline = nullptr) {
   AnimationEffectOrAnimationEffectSequence effects;
   AnimationEffect* effect = CreateKeyframeEffect(element);
   effects.SetAnimationEffect(effect);
   DocumentTimelineOrScrollTimeline timeline;
+  if (scroll_timeline)
+    timeline.SetScrollTimeline(scroll_timeline);
   scoped_refptr<SerializedScriptValue> options;
 
   ScriptState::Scope scope(script_state);
@@ -95,4 +101,84 @@
   EXPECT_EQ(true, style->HasCurrentOpacityAnimation());
 }
 
+TEST_F(WorkletAnimationTest,
+       CurrentTimeFromDocumentTimelineIsOffsetByStartTime) {
+  GetDocument().GetAnimationClock().ResetTimeForTesting();
+  double error = base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF();
+  WorkletAnimationId id = worklet_animation_->GetWorkletAnimationId();
+  base::TimeTicks first_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111);
+  base::TimeTicks second_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 123.4);
+
+  GetDocument().GetAnimationClock().UpdateTime(first_ticks);
+  DummyExceptionStateForTesting exception_state;
+  worklet_animation_->play(exception_state);
+  worklet_animation_->UpdateCompositingState();
+
+  std::unique_ptr<AnimationWorkletDispatcherInput> state =
+      std::make_unique<AnimationWorkletDispatcherInput>();
+  worklet_animation_->UpdateInputState(state.get());
+  // First state request sets the start time and thus current time should be 0.
+  std::unique_ptr<AnimationWorkletInput> input =
+      state->TakeWorkletState(id.scope_id);
+  EXPECT_NEAR(0, input->added_and_updated_animations[0].current_time, error);
+  state.reset(new AnimationWorkletDispatcherInput);
+  GetDocument().GetAnimationClock().UpdateTime(second_ticks);
+  worklet_animation_->UpdateInputState(state.get());
+  input = state->TakeWorkletState(id.scope_id);
+  EXPECT_NEAR(123.4, input->updated_animations[0].current_time, error);
+}
+
+TEST_F(WorkletAnimationTest,
+       CurrentTimeFromScrollTimelineNotOffsetByStartTime) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #scroller { overflow: scroll; width: 100px; height: 100px; }
+      #spacer { width: 200px; height: 200px; }
+    </style>
+    <div id='scroller'>
+      <div id ='spacer'></div>
+    </div>
+  )HTML");
+
+  LayoutBoxModelObject* scroller =
+      ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller"));
+  ASSERT_TRUE(scroller);
+  ASSERT_TRUE(scroller->HasOverflowClip());
+  PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea();
+  ASSERT_TRUE(scrollable_area);
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 20), kProgrammaticScroll);
+  ScrollTimelineOptions* options = ScrollTimelineOptions::Create();
+  DoubleOrScrollTimelineAutoKeyword time_range =
+      DoubleOrScrollTimelineAutoKeyword::FromDouble(100);
+  options->setTimeRange(time_range);
+  options->setScrollSource(GetElementById("scroller"));
+  ScrollTimeline* scroll_timeline =
+      ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION);
+  WorkletAnimation* worklet_animation =
+      CreateWorkletAnimation(GetScriptState(), element_, scroll_timeline);
+  WorkletAnimationId id = worklet_animation->GetWorkletAnimationId();
+
+  DummyExceptionStateForTesting exception_state;
+  worklet_animation->play(exception_state);
+  worklet_animation->UpdateCompositingState();
+
+  double error = base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF();
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 40), kProgrammaticScroll);
+  std::unique_ptr<AnimationWorkletDispatcherInput> state =
+      std::make_unique<AnimationWorkletDispatcherInput>();
+  worklet_animation->UpdateInputState(state.get());
+  std::unique_ptr<AnimationWorkletInput> input =
+      state->TakeWorkletState(id.scope_id);
+
+  EXPECT_NEAR(40, input->added_and_updated_animations[0].current_time, error);
+  state.reset(new AnimationWorkletDispatcherInput);
+
+  scrollable_area->SetScrollOffset(ScrollOffset(0, 70), kProgrammaticScroll);
+  worklet_animation->UpdateInputState(state.get());
+  input = state->TakeWorkletState(id.scope_id);
+  EXPECT_NEAR(70, input->updated_animations[0].current_time, error);
+}
+
 }  //  namespace blink
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index f781963..ba938f7 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -262,6 +262,7 @@
   visibility = [
     "//components/pdf/common:interfaces_blink",
     "//services/device/public/mojom:mojom_blink",
+    "//services/media_session/public/mojom:mojom_blink",
     "//third_party/blink/*",
     "//url/mojom:url_mojom_origin_blink",
     "//url/mojom:url_mojom_gurl_blink",
diff --git a/third_party/blink/renderer/platform/bindings/string_resource.cc b/third_party/blink/renderer/platform/bindings/string_resource.cc
index ea7f246..6ff600c 100644
--- a/third_party/blink/renderer/platform/bindings/string_resource.cc
+++ b/third_party/blink/renderer/platform/bindings/string_resource.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/bindings/string_resource.h"
 
+#include <type_traits>
+
 #include "third_party/blink/renderer/platform/bindings/v8_binding.h"
 
 namespace blink {
@@ -92,11 +94,28 @@
     v8::String::ExternalStringResourceBase* resource =
         v8_string->GetExternalStringResourceBase(&encoding);
     if (LIKELY(!!resource)) {
+      // Inheritance:
+      // - V8 side: v8::String::ExternalStringResourceBase
+      //   -> v8::External{One,}ByteStringResource
+      // - Both: StringResource{8,16}Base inherits from the matching v8 class.
+      static_assert(std::is_base_of<v8::String::ExternalOneByteStringResource,
+                                    StringResource8Base>::value,
+                    "");
+      static_assert(std::is_base_of<v8::String::ExternalStringResource,
+                                    StringResource16Base>::value,
+                    "");
+      static_assert(
+          std::is_base_of<StringResourceBase, StringResource8Base>::value, "");
+      static_assert(
+          std::is_base_of<StringResourceBase, StringResource16Base>::value, "");
+      // Then StringResource{8,16}Base allows to go from one ancestry path to
+      // the other one. Even though it's empty, removing it causes UB, see
+      // crbug.com/909796.
       StringResourceBase* base;
       if (encoding == v8::String::ONE_BYTE_ENCODING)
-        base = static_cast<StringResource8*>(resource);
+        base = static_cast<StringResource8Base*>(resource);
       else
-        base = static_cast<StringResource16*>(resource);
+        base = static_cast<StringResource16Base*>(resource);
       return StringTraits<StringType>::FromStringResource(base);
     }
   }
diff --git a/third_party/blink/renderer/platform/bindings/string_resource.h b/third_party/blink/renderer/platform/bindings/string_resource.h
index d6f50c1..7874392 100644
--- a/third_party/blink/renderer/platform/bindings/string_resource.h
+++ b/third_party/blink/renderer/platform/bindings/string_resource.h
@@ -114,18 +114,40 @@
   DISALLOW_COPY_AND_ASSIGN(StringResourceBase);
 };
 
-class StringResource16 final : public StringResourceBase,
-                               public v8::String::ExternalStringResource {
+// Even though StringResource{8,16}Base are effectively empty in release mode,
+// they are needed as they serve as a common ancestor to Parkable and regular
+// strings.
+//
+// See the comment in |ToBlinkString()|'s implementation for the rationale.
+class StringResource16Base : public StringResourceBase,
+                             public v8::String::ExternalStringResource {
  public:
-  explicit StringResource16(const String& string) : StringResourceBase(string) {
-    DCHECK(!string.Is8Bit());
-  }
-
-  explicit StringResource16(const AtomicString& string)
+  explicit StringResource16Base(const String& string)
       : StringResourceBase(string) {
     DCHECK(!string.Is8Bit());
   }
 
+  explicit StringResource16Base(const AtomicString& string)
+      : StringResourceBase(string) {
+    DCHECK(!string.Is8Bit());
+  }
+
+  explicit StringResource16Base(const ParkableString& parkable_string)
+      : StringResourceBase(parkable_string) {
+    DCHECK(!parkable_string.Is8Bit());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(StringResource16Base);
+};
+
+class StringResource16 final : public StringResource16Base {
+ public:
+  explicit StringResource16(const String& string)
+      : StringResource16Base(string) {}
+
+  explicit StringResource16(const AtomicString& string)
+      : StringResource16Base(string) {}
+
   size_t length() const override { return plain_string_.Impl()->length(); }
   const uint16_t* data() const override {
     return reinterpret_cast<const uint16_t*>(
@@ -135,34 +157,10 @@
   DISALLOW_COPY_AND_ASSIGN(StringResource16);
 };
 
-class StringResource8 final : public StringResourceBase,
-                              public v8::String::ExternalOneByteStringResource {
- public:
-  explicit StringResource8(const String& string) : StringResourceBase(string) {
-    DCHECK(string.Is8Bit());
-  }
-
-  explicit StringResource8(const AtomicString& string)
-      : StringResourceBase(string) {
-    DCHECK(string.Is8Bit());
-  }
-
-  size_t length() const override { return plain_string_.Impl()->length(); }
-  const char* data() const override {
-    return reinterpret_cast<const char*>(plain_string_.Impl()->Characters8());
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(StringResource8);
-};
-
-class ParkableStringResource16 final
-    : public StringResourceBase,
-      public v8::String::ExternalStringResource {
+class ParkableStringResource16 final : public StringResource16Base {
  public:
   explicit ParkableStringResource16(const ParkableString& string)
-      : StringResourceBase(string) {
-    DCHECK(!parkable_string_.Is8Bit());
-  }
+      : StringResource16Base(string) {}
 
   bool IsCacheable() const override {
     return !parkable_string_.may_be_parked();
@@ -181,14 +179,47 @@
   DISALLOW_COPY_AND_ASSIGN(ParkableStringResource16);
 };
 
-class ParkableStringResource8 final
-    : public StringResourceBase,
-      public v8::String::ExternalOneByteStringResource {
+class StringResource8Base : public StringResourceBase,
+                            public v8::String::ExternalOneByteStringResource {
+ public:
+  explicit StringResource8Base(const String& string)
+      : StringResourceBase(string) {
+    DCHECK(string.Is8Bit());
+  }
+
+  explicit StringResource8Base(const AtomicString& string)
+      : StringResourceBase(string) {
+    DCHECK(string.Is8Bit());
+  }
+
+  explicit StringResource8Base(const ParkableString& parkable_string)
+      : StringResourceBase(parkable_string) {
+    DCHECK(parkable_string.Is8Bit());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(StringResource8Base);
+};
+
+class StringResource8 final : public StringResource8Base {
+ public:
+  explicit StringResource8(const String& string)
+      : StringResource8Base(string) {}
+
+  explicit StringResource8(const AtomicString& string)
+      : StringResource8Base(string) {}
+
+  size_t length() const override { return plain_string_.Impl()->length(); }
+  const char* data() const override {
+    return reinterpret_cast<const char*>(plain_string_.Impl()->Characters8());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(StringResource8);
+};
+
+class ParkableStringResource8 final : public StringResource8Base {
  public:
   explicit ParkableStringResource8(const ParkableString& string)
-      : StringResourceBase(string) {
-    DCHECK(parkable_string_.Is8Bit());
-  }
+      : StringResource8Base(string) {}
 
   bool IsCacheable() const override {
     return !parkable_string_.may_be_parked();
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
index 8662b0d..12a4511 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
@@ -32,6 +32,7 @@
 
 #include <memory>
 
+#include "build/build_config.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
@@ -334,11 +335,13 @@
                         WTF::Partitions::FastFree);
 }
 
+#if !defined(OS_MACOSX)
 static void DeleteTypefaceStream(void* stream_asset_ptr) {
   SkStreamAsset* stream_asset =
       reinterpret_cast<SkStreamAsset*>(stream_asset_ptr);
   delete stream_asset;
 }
+#endif
 
 hb_face_t* HarfBuzzFace::CreateFace() {
   hb_face_t* face = nullptr;
@@ -347,6 +350,11 @@
                                   ("Blink.Fonts.HarfBuzzFaceZeroCopyAccess"));
   SkTypeface* typeface = platform_data_->Typeface();
   CHECK(typeface);
+  // The attempt of doing zero copy-mmaped memory access to the font blobs does
+  // not work efficiently on Mac, since what is returned from
+  // typeface->openStream is a synthesized font assembled from copying all font
+  // tables on Mac. See the implementation of SkTypeface_Mac::onOpenStream.
+#if !defined(OS_MACOSX)
   int ttc_index = 0;
   SkStreamAsset* typeface_stream = typeface->openStream(&ttc_index);
   if (typeface_stream && typeface_stream->getMemoryBase()) {
@@ -358,6 +366,7 @@
         hb_blob_destroy);
     face = hb_face_create(face_blob.get(), ttc_index);
   }
+#endif
 
   // Fallback to table copies if there is no in-memory access.
   if (!face) {
diff --git a/third_party/blink/renderer/platform/graphics/contiguous_container.h b/third_party/blink/renderer/platform/graphics/contiguous_container.h
index dafbd72..3430f74c 100644
--- a/third_party/blink/renderer/platform/graphics/contiguous_container.h
+++ b/third_party/blink/renderer/platform/graphics/contiguous_container.h
@@ -13,7 +13,6 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/compiler.h"
 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item_list.h b/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
index c45c6934..c6c87fb3 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
@@ -8,7 +8,6 @@
 #include "third_party/blink/renderer/platform/graphics/contiguous_container.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
 #include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h b/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h
index 024a318..51af360 100644
--- a/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h
+++ b/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h
@@ -11,7 +11,7 @@
 
 namespace blink {
 
-// A special DrawingDisplayItem containing hit test data.
+// A special DisplayItem containing hit test data.
 class PLATFORM_EXPORT HitTestDisplayItem final : public DisplayItem {
  public:
   HitTestDisplayItem(const DisplayItemClient& client,
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
index 4797ec2..469ae05 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -20,7 +20,6 @@
 #include "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
diff --git a/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h b/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
index f8134a0..70654e7 100644
--- a/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
+++ b/third_party/blink/renderer/platform/heap/sparse_heap_bitmap.h
@@ -11,7 +11,6 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
 #include "third_party/blink/renderer/platform/heap/heap_page.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/platform/transforms/transformation_matrix.h b/third_party/blink/renderer/platform/transforms/transformation_matrix.h
index 40d7cf7..816565a 100644
--- a/third_party/blink/renderer/platform/transforms/transformation_matrix.h
+++ b/third_party/blink/renderer/platform/transforms/transformation_matrix.h
@@ -34,7 +34,6 @@
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace gfx {
diff --git a/third_party/blink/renderer/platform/wtf/BUILD.gn b/third_party/blink/renderer/platform/wtf/BUILD.gn
index bd54b90..108a748 100644
--- a/third_party/blink/renderer/platform/wtf/BUILD.gn
+++ b/third_party/blink/renderer/platform/wtf/BUILD.gn
@@ -45,7 +45,6 @@
 jumbo_component("wtf") {
   sources = [
     "address_sanitizer.h",
-    "alignment.h",
     "allocator.cc",
     "allocator.h",
     "allocator/partition_allocator.cc",
diff --git a/third_party/blink/renderer/platform/wtf/alignment.h b/third_party/blink/renderer/platform/wtf/alignment.h
deleted file mode 100644
index f4d782f..0000000
--- a/third_party/blink/renderer/platform/wtf/alignment.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- *  You should have received a copy of the GNU Library General Public License
- *  along with this library; see the file COPYING.LIB.  If not, write to
- *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- *  Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALIGNMENT_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALIGNMENT_H_
-
-#include <stdint.h>
-#include "build/build_config.h"
-
-namespace WTF {
-
-#if defined(COMPILER_GCC)
-#define WTF_ALIGN_OF(type) __alignof__(type)
-#define WTF_ALIGNED(variable_type, variable, n) \
-  variable_type variable __attribute__((__aligned__(n)))
-#elif defined(COMPILER_MSVC)
-#define WTF_ALIGN_OF(type) __alignof(type)
-#define WTF_ALIGNED(variable_type, variable, n) \
-  __declspec(align(n)) variable_type variable
-#else
-#error WTF_ALIGN macros need alignment control.
-#endif
-
-template <uintptr_t mask>
-inline bool IsAlignedTo(const void* pointer) {
-  return !(reinterpret_cast<uintptr_t>(pointer) & mask);
-}
-
-}  // namespace WTF
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALIGNMENT_H_
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index b3fe06f..f99180d 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -26,7 +26,6 @@
 #include <memory>
 
 #include "base/numerics/checked_math.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
diff --git a/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h b/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
index 84b044c..96b56b4 100644
--- a/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
+++ b/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
@@ -24,7 +24,6 @@
 
 #include <stdint.h>
 #include "build/build_config.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 #include "third_party/blink/renderer/platform/wtf/cpu.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
@@ -117,7 +116,9 @@
   const uintptr_t kMemoryAccessMask = kMemoryAccessSize - 1;
 
   size_t i = 0;
-  for (; i < length && !IsAlignedTo<kMemoryAccessMask>(&source[i]); ++i) {
+  for (; i < length &&
+         reinterpret_cast<uintptr_t>(&source[i]) & kMemoryAccessMask;
+       ++i) {
     DCHECK(!(source[i] & 0xff00));
     destination[i] = static_cast<LChar>(source[i]);
   }
@@ -155,7 +156,7 @@
   if (length >= (2 * kMemoryAccessSize) - 1) {
     // Prefix: align dst on 64 bits.
     const uintptr_t kMemoryAccessMask = kMemoryAccessSize - 1;
-    while (!IsAlignedTo<kMemoryAccessMask>(destination))
+    while (reinterpret_cast<uintptr_t>(destination) & kMemoryAccessMask)
       *destination++ = static_cast<LChar>(*source++);
 
     // Vector interleaved unpack, we only store the lower 8 bits.
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index 4ead5355f..1646a56b 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -30,7 +30,6 @@
 #include "base/macros.h"
 #include "base/template_util.h"
 #include "build/build_config.h"
-#include "third_party/blink/renderer/platform/wtf/alignment.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
 #include "third_party/blink/renderer/platform/wtf/construct_traits.h"
 #include "third_party/blink/renderer/platform/wtf/container_annotations.h"
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 8ddad89..12fdeeb46 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -358,16 +358,6 @@
 crbug.com/591099 virtual/android/rootscroller/set-root-scroller.html [ Pass ]
 crbug.com/591099 virtual/android/rootscroller/set-rootscroller-before-load.html [ Pass ]
 crbug.com/591099 virtual/android/url-bar/bottom-and-top-fixed-sticks-to-top.html [ Failure ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/acquire-display-lock.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/acquire-on-no-containment-added.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/context-suspend-resume-empty.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/context-suspend-resume.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/context-two-suspend-resume.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/element-retained-by-context.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/multiple-schedules-recursive.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/multiple-schedules.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/simple-schedule-1.html [ Pass Timeout ]
-crbug.com/591099 virtual/display-lock/display-lock/lock-before-append/simple-schedule-2.html [ Pass Timeout ]
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/feature-policy-vibrate/ [ Skip ]
 crbug.com/591099 virtual/fractional_scrolling/fast/scrolling/fractional-scroll-offset-iframe-fixed-position.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService b/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService
index b26e9c8..3b51d5c09 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/blink/web_tests/FlagExpectations/enable-features=NetworkService
@@ -1,4 +1,4 @@
-# These tests currently fail when run with --enable-features=NetworkSerivce
+# These tests currently fail when run with --enable-features=NetworkService
 # See https://crbug.com/729849
 
 Bug(none) http/tests/security/cors-rfc1918 [ Crash Timeout ]
@@ -25,14 +25,16 @@
 crbug.com/898306 http/tests/inspector-protocol/network/raw-headers-for-protected-document.js [ Failure ]
 crbug.com/898306 http/tests/inspector-protocol/network/security-info-on-response.js [ Failure ]
 
-# Sheriff 2018-11-29
-crbug.com/910011 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-404.https.html [ Failure ]
-crbug.com/910011 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-cache-validation.https.html [ Failure ]
-crbug.com/910011 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-redirect.https.html [ Failure ]
-crbug.com/910011 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-subdomain-dns-failure.https.html [ Failure ]
-crbug.com/910011 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-success-with-subdomain-policy.https.html [ Failure ]
-crbug.com/910011 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-success.https.html [ Failure ]
-crbug.com/910011 virtual/outofblink-cors/http/tests/security/isolatedWorld/cross-origin-xhr.html [ Failure ]
-crbug.com/910011 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-report-only-sends-reports-on-violation.https.sub.html [ Failure ]
-crbug.com/910011 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-sends-reports-on-violation.https.sub.html [ Failure ]
-crbug.com/910011 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-works-on-frame-src.https.sub.html [ Failure ]
+# Reports aren't allowed under content_shell NS
+crbug.com/910212 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-404.https.html [ Failure ]
+crbug.com/910212 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-cache-validation.https.html [ Failure ]
+crbug.com/910212 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-redirect.https.html [ Failure ]
+crbug.com/910212 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-subdomain-dns-failure.https.html [ Failure ]
+crbug.com/910212 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-success-with-subdomain-policy.https.html [ Failure ]
+crbug.com/910212 virtual/network-error-logging/external/wpt/network-error-logging/sends-report-on-success.https.html [ Failure ]
+crbug.com/910212 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-report-only-sends-reports-on-violation.https.sub.html [ Failure ]
+crbug.com/910212 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-sends-reports-on-violation.https.sub.html [ Failure ]
+crbug.com/910212 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-works-on-frame-src.https.sub.html [ Failure ]
+
+# Uncategorized failure.
+Bug(none) virtual/outofblink-cors/http/tests/security/isolatedWorld/cross-origin-xhr.html [ Failure ]
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 4884794..d58aca11 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -87,6 +87,7 @@
 crbug.com/862029 [ Linux ] virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-window-filter.js [ Pass Leak ]
 
 crbug.com/733494 [ Linux ] media/autoplay/document-user-activation.html [ Pass Leak ]
+crbug.com/733494 [ Linux ] virtual/video-surface-layer/media/autoplay/document-user-activation.html [ Pass Leak ]
 
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7bd7573..76682e2 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1899,6 +1899,24 @@
 
 # ====== Out of Blink CORS related tests END ======
 
+
+# ====== Backdrop-filter related tests BEGIN ======
+# These are known failures - this feature is still being implemented
+crbug.com/497522 css3/filters/backdrop-filter-boundary.html [ Failure ]
+crbug.com/497522 css3/filters/backdrop-filter-bleeding.html [ Failure ]
+crbug.com/497522 css3/filters/backdrop-filter-svg.html [ Failure ]
+crbug.com/497522 css3/filters/backdrop-filter-plus-filter.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-border-radius.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-clip-rect.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-paint-order.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-basic-opacity.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html [ Failure ]
+crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html [ Failure ]
+# ====== Backdrop-filter related tests END ======
+
+
 crbug.com/492664 [ Linux ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vlr-005.xht [ Failure ]
 crbug.com/492664 [ Linux ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vrl-004.xht [ Failure ]
 crbug.com/492664 [ Mac ] external/wpt/css/css-writing-modes/bidi-embed-002.html [ Failure ]
@@ -2998,6 +3016,7 @@
 crbug.com/893480 external/wpt/infrastructure/testdriver/actions/multiDevice.html [ Failure Timeout ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-004.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/preload/dynamic-adding-preload-imagesrcset.tentative.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-text/text-transform/text-transform-capitalize-033.html [ Failure ]
 crbug.com/626703 virtual/outofblink-cors/external/wpt/fetch/content-type/response.window.html [ Timeout ]
@@ -4484,7 +4503,6 @@
 crbug.com/873454 css3/filters/effect-reference-image-hw.html [ Failure Pass ]
 
 # Sheriff failures 2018-08-15
-crbug.com/874500 [ Mac ] media/media-ended.html [ Pass Timeout ]
 crbug.com/874567 [ Mac ] svg/custom/getscreenctm-in-scrollable-div-area-nested.xhtml [ Pass Failure ]
 crbug.com/874837 [ Win ] ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling.htm [ Pass Failure ]
 crbug.com/874866 [ Mac ] virtual/new-remote-playback-pipeline/media/controls/modern/doubletap-to-jump-backwards-at-start.html [ Pass Timeout ]
@@ -5605,6 +5623,7 @@
 crbug.com/886566 http/tests/csspaint/invalidation-content-image.html [ Pass Timeout ]
 
 # Enable AnimationWorklet tests for mainthread
+crbug.com/785940 [ Release ] animations/animationworklet/worklet-animation-local-time-after-duration.html [ Pass Failure ]
 crbug.com/887659 virtual/threaded/animations/animationworklet/worklet-animation-local-time-after-duration.html [ Failure ]
 
 # Sheriff 2018-09-25
@@ -5704,17 +5723,6 @@
 # Test is flaky under load
 crbug.com/904389 http/tests/preload/delaying_onload_link_preload_after_discovery.html [ Failure Pass ]
 
-# These are known failures, being implemented
-crbug.com/497522 css3/filters/backdrop-filter-boundary.html [ Failure ]
-crbug.com/497522 css3/filters/backdrop-filter-bleeding.html [ Failure ]
-crbug.com/497522 css3/filters/backdrop-filter-svg.html [ Failure ]
-crbug.com/497522 css3/filters/backdrop-filter-plus-filter.html [ Failure ]
-crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html [ Failure ]
-crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-border-radius.html [ Failure ]
-crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-clip-rect.html [ Failure ]
-crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html [ Failure ]
-crbug.com/497522 external/wpt/css/filter-effects/backdrop-filter-paint-order.html [ Failure ]
-
 #Sheriff 2018-11-14
 crbug.com/905694 [ Linux ] virtual/threaded/fast/scroll-behavior/smooth-scroll/scroll-during-selection.html [ Failure Pass ]
 crbug.com/905772 [ Linux ] virtual/sampling-heap-profiler/inspector-protocol/memory/sampling-native-profile-partition-alloc.js [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 8ab9701..abfe2cd 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -52849,6 +52849,18 @@
      {}
     ]
    ],
+   "css/css-multicol/multicol-span-all-004.html": [
+    [
+     "/css/css-multicol/multicol-span-all-004.html",
+     [
+      [
+       "/css/css-multicol/multicol-span-all-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-span-all-block-sibling-003.xht": [
     [
      "/css/css-multicol/multicol-span-all-block-sibling-003.xht",
@@ -135907,6 +135919,11 @@
      {}
     ]
    ],
+   "css/css-multicol/multicol-span-all-004-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-span-all-block-sibling-3-ref.xht": [
     [
      {}
@@ -343238,6 +343255,14 @@
    "fb4a508f50a309fb44c8d653cf84d1abdb22d838",
    "reftest"
   ],
+  "css/css-multicol/multicol-span-all-004-ref.html": [
+   "beecede21082bf18500205e274a27ca53840594e",
+   "support"
+  ],
+  "css/css-multicol/multicol-span-all-004.html": [
+   "e9165658c11fea62e4f714fc4643f33853e95500",
+   "reftest"
+  ],
   "css/css-multicol/multicol-span-all-block-sibling-003.xht": [
    "bd2cdd6cdab0fc15c2e16e2237f6b025230332c4",
    "reftest"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-004-ref.html
new file mode 100644
index 0000000..beecede
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-004-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test Reference: column-span:all should act like column-span:none in different block formatting context</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+
+  <style>
+  #column {
+    column-count: 3;
+    column-rule: 6px solid;
+    width: 600px;
+    outline: 1px solid black;
+  }
+  h3 {
+    column-span: none;
+    outline: 1px solid blue;
+  }
+  </style>
+
+  <body onload="runTest();">
+    <article id="column">
+      <div>block1</div>
+      <div style="display: inline-block;">
+        <h3>non-spanner</h3>
+      </div>
+      <div style="overflow: hidden;">
+        <h3>non-spanner</h3>
+      </div>
+      <div style="column-span: all; outline: 1px solid green;">
+        Spanner
+        <h3>non-spanner in a spanner</h3>
+      </div>
+      <div>block2</div>
+    </article>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-004.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-004.html
new file mode 100644
index 0000000..e916565
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-004.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: column-span:all should act like column-span:none in different block formatting context</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-span">
+  <link rel="match" href="multicol-span-all-004-ref.html">
+  <meta name="assert" content="This test checks a column-span:all element should act like column-span: none if it's under different block formatting context.">
+
+  <style>
+  #column {
+    column-count: 3;
+    column-rule: 6px solid;
+    width: 600px;
+    outline: 1px solid black;
+  }
+  h3 {
+    column-span: all;
+    outline: 1px solid blue;
+  }
+  </style>
+
+  <body onload="runTest();">
+    <article id="column">
+      <div>block1</div>
+      <div style="display: inline-block;">
+        <h3>non-spanner</h3>
+      </div>
+      <div style="overflow: hidden;">
+        <h3>non-spanner</h3>
+      </div>
+      <div style="column-span: all; outline: 1px solid green;">
+        Spanner
+        <h3>non-spanner in a spanner</h3>
+      </div>
+      <div>block2</div>
+    </article>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-ref.html
index 26d7715..e0c76c0 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-ref.html
@@ -6,12 +6,11 @@
 
 
 <div>
-  <p>Expected: A green box and an overlapping black box, with a magenta<br>
-  overlapping region.</p>
+  <p>Expected: Just a single green box.</p>
 </div>
 <div class="colorbox"></div>
-<div class="box2outside"></div>
-<div class="box2inside"></div>
+
+
 
 <style>
 .colorbox {
@@ -22,21 +21,5 @@
     left: 10px;
     top: 100px;
 }
-.box2outside {
-    background: black;
-    position: absolute;
-    width: 100px;
-    height: 100px;
-    left: 60px;
-    top: 150px;
-}
-.box2inside {
-    background: #ff7fff;
-    position: absolute;
-    width: 50px;
-    height: 50px;
-    left: 60px;
-    top: 150px;
-}
 </style>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity.html
index e3ad8a20..4cc5f33 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity.html
@@ -6,8 +6,7 @@
 <link rel="match"  href="backdrop-filter-basic-opacity-ref.html">
 
 <div>
-  <p>Expected: A green box and an overlapping black box, with a magenta<br>
-  overlapping region.</p>
+  <p>Expected: Just a single green box.</p>
 </div>
 <div class="colorbox">
   <div class="filterbox"></div>
@@ -29,7 +28,7 @@
     left: 50px;
     top: 50px;
     backdrop-filter: invert(1);
-    opacity: 0.5;
+    opacity: 0;
 }
 </style>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-fixed-clip-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-fixed-clip-ref.html
new file mode 100644
index 0000000..1fa5967
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-fixed-clip-ref.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>backdrop-filter: Should not filter outside of clip/scroll.</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+
+
+
+<div style="width:600px;">
+  <p>Expected: A green box, overlapping red box, and a small, inset cyan box. The<br>
+  cyan should not extend to the bottom/right edges of the red box.</p>
+</div>
+
+<div id="A">
+  <div id="B">
+    <div id="F"></div>
+    <div id="T"></div>
+  </div>
+</div>
+
+<style>
+div {
+  position:absolute;
+  width: 200px;
+  height: 200px;
+}
+#A {
+  overflow:hidden;
+  top: 110px;
+  left: 10px;
+}
+#B {
+
+  background:green;
+}
+#F {
+  position:fixed;
+  background:#f008;
+  top:150px;
+  left:50px;
+}
+#T {
+  background:#c0e3ff;
+  top:90px;
+  left:90px;
+  width: 110px;
+  height: 110px;
+}
+</style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html
new file mode 100644
index 0000000..2550eb1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-fixed-clip.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>backdrop-filter: Should not filter outside of clip/scroll.</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
+<link rel="match"  href="backdrop-filter-fixed-clip-ref.html">
+
+<div style="width:600px;">
+  <p>Expected: A green box, overlapping red box, and a small, inset cyan box. The<br>
+  cyan should not extend to the bottom/right edges of the red box.</p>
+</div>
+
+<div id="A">
+  <div id="B">
+    <div id="F"></div>
+    <div id="T"></div>
+  </div>
+</div>
+
+<style>
+div {
+  position:absolute;
+  width: 200px;
+  height: 200px;
+}
+#A {
+  overflow:hidden;
+  top: 110px;
+  left: 10px;
+}
+#B {
+  opacity: 0.99999;
+  background:green;
+}
+#F {
+  position:fixed;
+  background:#f008;
+  top:150px;
+  left:50px;
+}
+#T {
+  background:#fff8;
+  top:90px;
+  left:90px;
+  backdrop-filter:invert(1);
+}
+</style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html
new file mode 100644
index 0000000..c812d1f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-fixed.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>backdrop-filter: Should not filter outside parent stacking context.</title>
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
+<link rel="match"  href="backdrop-filter-isolation-ref.html">
+
+<div class="outside">
+  <div class="stacking-context">
+    <div class="filter">
+    </div>
+  </div>
+</div>
+
+
+<style>
+div {
+  position: absolute;
+  width: 100px;
+  height: 100px;
+  background: green;
+}
+.outside {
+  top: 10px;
+  left: 10px;
+}
+.stacking-context {
+  position: fixed;
+  top: 10px;
+  left: 130px;
+}
+.filter {
+  width: 160px;
+  height: 160px;
+  top: 30px;
+  left: -90px;
+  opacity: 0.3;
+  backdrop-filter: invert(1);
+  background: yellow;
+}
+</style>
+
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
deleted file mode 100644
index e15018b..0000000
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
+++ /dev/null
@@ -1,70 +0,0 @@
-This is a testharness.js-based test.
-PASS idlharness
-PASS idl_test setup
-PASS Partial interface Performance: original interface defined
-PASS PerformanceEntry interface: existence and properties of interface object
-PASS PerformanceEntry interface object length
-PASS PerformanceEntry interface object name
-PASS PerformanceEntry interface: existence and properties of interface prototype object
-PASS PerformanceEntry interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceEntry interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceEntry interface: attribute name
-PASS PerformanceEntry interface: attribute entryType
-PASS PerformanceEntry interface: attribute startTime
-PASS PerformanceEntry interface: attribute duration
-PASS PerformanceEntry interface: operation toJSON()
-PASS PerformanceObserver interface: existence and properties of interface object
-PASS PerformanceObserver interface object length
-PASS PerformanceObserver interface object name
-PASS PerformanceObserver interface: existence and properties of interface prototype object
-PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceObserver interface: operation observe(PerformanceObserverInit)
-PASS PerformanceObserver interface: operation disconnect()
-PASS PerformanceObserver interface: operation takeRecords()
-PASS PerformanceObserver must be primary interface of observer
-PASS Stringification of observer
-PASS PerformanceObserver interface: observer must inherit property "observe(PerformanceObserverInit)" with the proper type
-PASS PerformanceObserver interface: calling observe(PerformanceObserverInit) on observer with too few arguments must throw TypeError
-PASS PerformanceObserver interface: observer must inherit property "disconnect()" with the proper type
-PASS PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type
-PASS PerformanceObserverEntryList interface: existence and properties of interface object
-PASS PerformanceObserverEntryList interface object length
-PASS PerformanceObserverEntryList interface object name
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceObserverEntryList interface: operation getEntries()
-PASS PerformanceObserverEntryList interface: operation getEntriesByType(DOMString)
-PASS PerformanceObserverEntryList interface: operation getEntriesByName(DOMString, DOMString)
-PASS PerformanceObserverEntryList must be primary interface of entryList
-PASS Stringification of entryList
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntries()" with the proper type
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByType(DOMString)" with the proper type
-PASS PerformanceObserverEntryList interface: calling getEntriesByType(DOMString) on entryList with too few arguments must throw TypeError
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
-PASS PerformanceObserverEntryList interface: calling getEntriesByName(DOMString, DOMString) on entryList with too few arguments must throw TypeError
-PASS Performance interface: operation getEntries()
-PASS Performance interface: operation getEntriesByType(DOMString)
-PASS Performance interface: operation getEntriesByName(DOMString, DOMString)
-PASS Performance interface: performance must inherit property "getEntries()" with the proper type
-PASS Performance interface: performance must inherit property "getEntriesByType(DOMString)" with the proper type
-PASS Performance interface: calling getEntriesByType(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
-PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
-FAIL PerformanceMark interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object length assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object name assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark must be primary interface of mark assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-PASS Stringification of mark
-PASS PerformanceEntry interface: mark must inherit property "name" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "entryType" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "startTime" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "duration" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "toJSON()" with the proper type
-PASS Test default toJSON operation of PerformanceMark
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
deleted file mode 100644
index 0e17d27..0000000
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-This is a testharness.js-based test.
-Found 67 tests; 60 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idlharness
-PASS idl_test setup
-PASS Partial interface Performance: original interface defined
-PASS PerformanceEntry interface: existence and properties of interface object
-PASS PerformanceEntry interface object length
-PASS PerformanceEntry interface object name
-PASS PerformanceEntry interface: existence and properties of interface prototype object
-PASS PerformanceEntry interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceEntry interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceEntry interface: attribute name
-PASS PerformanceEntry interface: attribute entryType
-PASS PerformanceEntry interface: attribute startTime
-PASS PerformanceEntry interface: attribute duration
-PASS PerformanceEntry interface: operation toJSON()
-PASS PerformanceObserver interface: existence and properties of interface object
-PASS PerformanceObserver interface object length
-PASS PerformanceObserver interface object name
-PASS PerformanceObserver interface: existence and properties of interface prototype object
-PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceObserver interface: operation observe(PerformanceObserverInit)
-PASS PerformanceObserver interface: operation disconnect()
-PASS PerformanceObserver interface: operation takeRecords()
-PASS PerformanceObserver must be primary interface of observer
-PASS Stringification of observer
-PASS PerformanceObserver interface: observer must inherit property "observe(PerformanceObserverInit)" with the proper type
-PASS PerformanceObserver interface: calling observe(PerformanceObserverInit) on observer with too few arguments must throw TypeError
-PASS PerformanceObserver interface: observer must inherit property "disconnect()" with the proper type
-PASS PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type
-PASS PerformanceObserverEntryList interface: existence and properties of interface object
-PASS PerformanceObserverEntryList interface object length
-PASS PerformanceObserverEntryList interface object name
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceObserverEntryList interface: operation getEntries()
-PASS PerformanceObserverEntryList interface: operation getEntriesByType(DOMString)
-PASS PerformanceObserverEntryList interface: operation getEntriesByName(DOMString, DOMString)
-PASS PerformanceObserverEntryList must be primary interface of entryList
-PASS Stringification of entryList
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntries()" with the proper type
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByType(DOMString)" with the proper type
-PASS PerformanceObserverEntryList interface: calling getEntriesByType(DOMString) on entryList with too few arguments must throw TypeError
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
-PASS PerformanceObserverEntryList interface: calling getEntriesByName(DOMString, DOMString) on entryList with too few arguments must throw TypeError
-PASS Performance interface: operation getEntries()
-PASS Performance interface: operation getEntriesByType(DOMString)
-PASS Performance interface: operation getEntriesByName(DOMString, DOMString)
-PASS Performance interface: performance must inherit property "getEntries()" with the proper type
-PASS Performance interface: performance must inherit property "getEntriesByType(DOMString)" with the proper type
-PASS Performance interface: calling getEntriesByType(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
-PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
-FAIL PerformanceMark interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object length assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object name assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark must be primary interface of mark assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-PASS Stringification of mark
-PASS PerformanceEntry interface: mark must inherit property "name" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "entryType" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "startTime" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "duration" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "toJSON()" with the proper type
-PASS Test default toJSON operation of PerformanceMark
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
deleted file mode 100644
index 0e17d27..0000000
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-This is a testharness.js-based test.
-Found 67 tests; 60 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idlharness
-PASS idl_test setup
-PASS Partial interface Performance: original interface defined
-PASS PerformanceEntry interface: existence and properties of interface object
-PASS PerformanceEntry interface object length
-PASS PerformanceEntry interface object name
-PASS PerformanceEntry interface: existence and properties of interface prototype object
-PASS PerformanceEntry interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceEntry interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceEntry interface: attribute name
-PASS PerformanceEntry interface: attribute entryType
-PASS PerformanceEntry interface: attribute startTime
-PASS PerformanceEntry interface: attribute duration
-PASS PerformanceEntry interface: operation toJSON()
-PASS PerformanceObserver interface: existence and properties of interface object
-PASS PerformanceObserver interface object length
-PASS PerformanceObserver interface object name
-PASS PerformanceObserver interface: existence and properties of interface prototype object
-PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceObserver interface: operation observe(PerformanceObserverInit)
-PASS PerformanceObserver interface: operation disconnect()
-PASS PerformanceObserver interface: operation takeRecords()
-PASS PerformanceObserver must be primary interface of observer
-PASS Stringification of observer
-PASS PerformanceObserver interface: observer must inherit property "observe(PerformanceObserverInit)" with the proper type
-PASS PerformanceObserver interface: calling observe(PerformanceObserverInit) on observer with too few arguments must throw TypeError
-PASS PerformanceObserver interface: observer must inherit property "disconnect()" with the proper type
-PASS PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type
-PASS PerformanceObserverEntryList interface: existence and properties of interface object
-PASS PerformanceObserverEntryList interface object length
-PASS PerformanceObserverEntryList interface object name
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's "constructor" property
-PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's @@unscopables property
-PASS PerformanceObserverEntryList interface: operation getEntries()
-PASS PerformanceObserverEntryList interface: operation getEntriesByType(DOMString)
-PASS PerformanceObserverEntryList interface: operation getEntriesByName(DOMString, DOMString)
-PASS PerformanceObserverEntryList must be primary interface of entryList
-PASS Stringification of entryList
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntries()" with the proper type
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByType(DOMString)" with the proper type
-PASS PerformanceObserverEntryList interface: calling getEntriesByType(DOMString) on entryList with too few arguments must throw TypeError
-PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
-PASS PerformanceObserverEntryList interface: calling getEntriesByName(DOMString, DOMString) on entryList with too few arguments must throw TypeError
-PASS Performance interface: operation getEntries()
-PASS Performance interface: operation getEntriesByType(DOMString)
-PASS Performance interface: operation getEntriesByName(DOMString, DOMString)
-PASS Performance interface: performance must inherit property "getEntries()" with the proper type
-PASS Performance interface: performance must inherit property "getEntriesByType(DOMString)" with the proper type
-PASS Performance interface: calling getEntriesByType(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
-PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
-FAIL PerformanceMark interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object length assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object name assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark must be primary interface of mark assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-PASS Stringification of mark
-PASS PerformanceEntry interface: mark must inherit property "name" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "entryType" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "startTime" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "duration" with the proper type
-PASS PerformanceEntry interface: mark must inherit property "toJSON()" with the proper type
-PASS Test default toJSON operation of PerformanceMark
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt
deleted file mode 100644
index a389976..0000000
--- a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS Partial interface Performance: original interface defined
-FAIL PerformanceMark interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object length assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object name assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark must be primary interface of mark assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-PASS Stringification of mark
-FAIL PerformanceMeasure interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface object length assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface object name assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure must be primary interface of measure assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-PASS Stringification of measure
-PASS Performance interface: operation mark(DOMString)
-PASS Performance interface: operation clearMarks(DOMString)
-PASS Performance interface: operation measure(DOMString, DOMString, DOMString)
-PASS Performance interface: operation clearMeasures(DOMString)
-PASS Performance interface: performance must inherit property "mark(DOMString)" with the proper type
-PASS Performance interface: calling mark(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "clearMarks(DOMString)" with the proper type
-PASS Performance interface: calling clearMarks(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "measure(DOMString, DOMString, DOMString)" with the proper type
-PASS Performance interface: calling measure(DOMString, DOMString, DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "clearMeasures(DOMString)" with the proper type
-PASS Performance interface: calling clearMeasures(DOMString) on performance with too few arguments must throw TypeError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt
deleted file mode 100644
index a389976..0000000
--- a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS Partial interface Performance: original interface defined
-FAIL PerformanceMark interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object length assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object name assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark must be primary interface of mark assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-PASS Stringification of mark
-FAIL PerformanceMeasure interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface object length assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface object name assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure must be primary interface of measure assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-PASS Stringification of measure
-PASS Performance interface: operation mark(DOMString)
-PASS Performance interface: operation clearMarks(DOMString)
-PASS Performance interface: operation measure(DOMString, DOMString, DOMString)
-PASS Performance interface: operation clearMeasures(DOMString)
-PASS Performance interface: performance must inherit property "mark(DOMString)" with the proper type
-PASS Performance interface: calling mark(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "clearMarks(DOMString)" with the proper type
-PASS Performance interface: calling clearMarks(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "measure(DOMString, DOMString, DOMString)" with the proper type
-PASS Performance interface: calling measure(DOMString, DOMString, DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "clearMeasures(DOMString)" with the proper type
-PASS Performance interface: calling clearMeasures(DOMString) on performance with too few arguments must throw TypeError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt
deleted file mode 100644
index a389976..0000000
--- a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS Partial interface Performance: original interface defined
-FAIL PerformanceMark interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object length assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface object name assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-FAIL PerformanceMark must be primary interface of mark assert_own_property: self does not have own property "PerformanceMark" expected property "PerformanceMark" missing
-PASS Stringification of mark
-FAIL PerformanceMeasure interface: existence and properties of interface object assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface object length assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface object name assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-FAIL PerformanceMeasure must be primary interface of measure assert_own_property: self does not have own property "PerformanceMeasure" expected property "PerformanceMeasure" missing
-PASS Stringification of measure
-PASS Performance interface: operation mark(DOMString)
-PASS Performance interface: operation clearMarks(DOMString)
-PASS Performance interface: operation measure(DOMString, DOMString, DOMString)
-PASS Performance interface: operation clearMeasures(DOMString)
-PASS Performance interface: performance must inherit property "mark(DOMString)" with the proper type
-PASS Performance interface: calling mark(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "clearMarks(DOMString)" with the proper type
-PASS Performance interface: calling clearMarks(DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "measure(DOMString, DOMString, DOMString)" with the proper type
-PASS Performance interface: calling measure(DOMString, DOMString, DOMString) on performance with too few arguments must throw TypeError
-PASS Performance interface: performance must inherit property "clearMeasures(DOMString)" with the proper type
-PASS Performance interface: calling clearMeasures(DOMString) on performance with too few arguments must throw TypeError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index d51f8401..f4b872a 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1000,6 +1000,12 @@
     getter startTime
     method constructor
     method toJSON
+interface PerformanceMark : PerformanceEntry
+    attribute @@toStringTag
+    method constructor
+interface PerformanceMeasure : PerformanceEntry
+    attribute @@toStringTag
+    method constructor
 interface PerformanceObserver
     attribute @@toStringTag
     method constructor
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index 721093c..ccb72df 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index f4466ab..57327e60 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index f4466ab..57327e60 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index f4466ab..57327e60 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index f4466ab..57327e60 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index f4466ab..57327e60 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index 721093c..ccb72df 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png b/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
index ca06a90..540a17bf 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-ellipse-circumference-fill-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index 721093c..ccb72df 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
index 7f9645e6..da9ddec5 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/gpu/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index 9f4d9b7..018698b 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -858,6 +858,12 @@
     getter startTime
     method constructor
     method toJSON
+interface PerformanceMark : PerformanceEntry
+    attribute @@toStringTag
+    method constructor
+interface PerformanceMeasure : PerformanceEntry
+    attribute @@toStringTag
+    method constructor
 interface PerformanceObserver
     attribute @@toStringTag
     method constructor
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 03cb022..98e617f 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -807,6 +807,12 @@
 [Worker]     getter startTime
 [Worker]     method constructor
 [Worker]     method toJSON
+[Worker] interface PerformanceMark : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker] interface PerformanceMeasure : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface PerformanceObserver
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index a2fd963..13ce35b 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -802,6 +802,12 @@
 [Worker]     getter startTime
 [Worker]     method constructor
 [Worker]     method toJSON
+[Worker] interface PerformanceMark : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker] interface PerformanceMeasure : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface PerformanceObserver
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 4f087bf0..a13e529 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -920,6 +920,12 @@
 [Worker]     getter startTime
 [Worker]     method constructor
 [Worker]     method toJSON
+[Worker] interface PerformanceMark : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker] interface PerformanceMeasure : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface PerformanceObserver
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index f228369..dc8118d4 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -915,6 +915,12 @@
 [Worker]     getter startTime
 [Worker]     method constructor
 [Worker]     method toJSON
+[Worker] interface PerformanceMark : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker] interface PerformanceMeasure : PerformanceEntry
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface PerformanceObserver
 [Worker]     attribute @@toStringTag
 [Worker]     method constructor
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 8b73798..6e44b0b 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1468,7 +1468,7 @@
       'libfuzzer', 'asan', 'debug_bot', 'chromeos_codecs', 'pdf_xfa', 'disable_nacl', 'optimize_for_fuzzing',
     ],
     'libfuzzer_chromeos_asan_release_bot': [
-      'libfuzzer', 'asan', 'chromeos_with_codecs', 'release_bot', 'pdf_xfa', 'disable_nacl', 'optimize_for_fuzzing',
+      'libfuzzer', 'asan', 'chromeos_with_codecs', 'shared_release_bot', 'pdf_xfa', 'disable_nacl', 'optimize_for_fuzzing',
     ],
     'libfuzzer_asan_release_bot': [
       'libfuzzer', 'asan', 'release_bot', 'chromeos_codecs', 'pdf_xfa', 'disable_nacl', 'optimize_for_fuzzing',
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 5051deb..504d0f0 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -20230,6 +20230,36 @@
   </description>
 </action>
 
+<action name="TrustedWebActivity.DisclosureAccepted">
+  <owner>peconn@chromium.org</owner>
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <description>
+    Recorded when a user accepts the &quot;Running in Chrome&quot; disclosure
+    seen in a Trusted Web Activity.
+  </description>
+</action>
+
+<action name="TrustedWebActivity.DisclosureShown">
+  <owner>peconn@chromium.org</owner>
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <description>
+    Recorded when a user sees the &quot;Running in Chrome&quot; disclosure in a
+    Trusted Web Activity.
+  </description>
+</action>
+
+<action name="TrustedWebActivity.OpenedSettingsViaManageSpace">
+  <owner>peconn@chromium.org</owner>
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <description>
+    Recorded when site settings are opened via &quot;Manage Space&quot; button
+    in TWA client app's settings.
+  </description>
+</action>
+
 <action name="UI_DevTools_Connect">
   <owner>weili@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ac69d28..ebd5bce 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -46022,6 +46022,12 @@
   </int>
 </enum>
 
+<enum name="SettingsNavigationSources">
+  <int value="0" label="Other"/>
+  <int value="1" label="From TWA clear data dialog"/>
+  <int value="2" label="From TWA manage space activity"/>
+</enum>
+
 <enum name="SettingsResetPromptConfigError">
   <int value="1" label="Config Ok"/>
   <int value="2" label="Missing domain_hashes param"/>
@@ -49499,6 +49505,7 @@
   <int value="10" label="Browser Actions"/>
   <int value="11" label="Webapp Actions"/>
   <int value="12" label="Offline Content Suggestion"/>
+  <int value="13" label="Sites shown in Trusted Web Activities"/>
 </enum>
 
 <enum name="TabBackgroundLoadStatus">
@@ -50726,6 +50733,13 @@
   <int value="80" label="TRIM_MEMORY_COMPLETE"/>
 </enum>
 
+<enum name="TrustedWebActivityDelegatedNotificationSmallIconFallback">
+  <int value="0" label="No fallback"/>
+  <int value="1" label="Fallback icon mot provided"/>
+  <int value="2" label="Fallback for Status Bar"/>
+  <int value="3" label="Fallback for Status Bar and content"/>
+</enum>
+
 <enum name="UIEventType">
   <int value="0" label="Unknown"/>
   <int value="1" label="Touch released"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e78e53c..7a342b6 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -104618,6 +104618,17 @@
   <summary>Number of retries until the final response was recorded.</summary>
 </histogram>
 
+<histogram name="SingleWebsitePreferences.NavigatedFromToReset"
+    enum="SettingsNavigationSources" expires_after="M78">
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Logs which way had the user navigated into settings screens when they press
+    &quot;Clear and Reset&quot; button in settings of a single website.
+  </summary>
+</histogram>
+
 <histogram name="SiteEngagementService.DaysSinceLastShortcutLaunch"
     units="days">
   <owner>calamity@chromium.org</owner>
@@ -115630,6 +115641,62 @@
   </summary>
 </histogram>
 
+<histogram name="TrustedWebActivity.ClearDataDialogOnClearAppDataAccepted"
+    enum="Boolean" expires_after="M78">
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Emits true if the user went to site settings from the dialog that is shown
+    after a Trusted Web Activity client app has had its data cleared.
+  </summary>
+</histogram>
+
+<histogram name="TrustedWebActivity.ClearDataDialogOnUninstallAccepted"
+    enum="Boolean" expires_after="M78">
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Emits true if the user went to site settings from the dialog that is shown
+    after a Trusted Web Activity client app has been uninstalled.
+  </summary>
+</histogram>
+
+<histogram name="TrustedWebActivity.DelegatedNotificationSmallIconFallback"
+    enum="TrustedWebActivityDelegatedNotificationSmallIconFallback"
+    expires_after="M78">
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Logs which kind of fallback for notification small icon was used for Trusted
+    Web Activity notification delegation.
+  </summary>
+</histogram>
+
+<histogram name="TrustedWebActivity.TimeInVerifiedOrigin" units="ms"
+    expires_after="M78">
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Time spent in a verified origin until navigating to an unverified one or
+    pausing the Trusted Web Activity.
+  </summary>
+</histogram>
+
+<histogram name="TrustedWebActivity.TimeOutOfVerifiedOrigin" units="ms"
+    expires_after="M78">
+  <owner>pshmakov@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Time spent out of verified origins until navigating back to a verified one
+    or pausing the Trusted Web Activity.
+  </summary>
+</histogram>
+
 <histogram name="TryScroll.SlowScroll" enum="ScrollThread">
   <obsolete>
     Deprecated 02/2016 in Issue 1741103002, and replaced by
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 0874499d..1120328 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -5677,6 +5677,13 @@
     <summary>
       True if there was a mismatch.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <enumeration/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
 </event>
 
diff --git a/tools/perf/conditionally_execute b/tools/perf/conditionally_execute
deleted file mode 100755
index 15e1c35b..0000000
--- a/tools/perf/conditionally_execute
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env vpython
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import argparse
-import os
-import subprocess
-import sys
-
-
-def main(args):
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-      '--gyp-condition', '-c', type=str, required=True,
-      help=('The gyp condition that acts as the switch. If the '
-            'condition is found in environment variable GYP_DEFINES, '
-            'this will execute the target script'))
-  options, script_args = parser.parse_known_args()
-
-  # Make sure that we always execute target script with python.
-  if 'python' not in script_args[0]:
-    script_args.insert(0, sys.executable)
-
-  # A typical GYP_DEFINES string looks s.t like "foo=a bar=1 baz=c". We
-  # tokenize the string before doing string matching.
-  gyp_defines = os.environ.get('GYP_DEFINES', '').split()
-  if options.gyp_condition in gyp_defines:
-    subprocess.check_call(script_args)
-  return 0
-
-
-if __name__ == '__main__':
-  main(sys.argv[1:])
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 4af7ceb..3eda9b29 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -956,7 +956,10 @@
     # supports swarming. It doesn't hurt.
     'can_use_on_swarming_builders': True,
     'expiration': 2 * 60 * 60, # 2 hours pending max
-    'hard_timeout': 7 * 60 * 60, # 7 hours timeout for full suite
+    # TODO(crbug.com/865538): once we have plenty of windows hardwares,
+    # to shards perf benchmarks on Win builders, reduce this hard timeout limit
+    # to ~2 hrs.
+    'hard_timeout': 10 * 60 * 60, # 10 hours timeout for full suite
     'ignore_task_failure': False,
     'io_timeout': 30 * 60, # 30 minutes
     'dimension_sets': [
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 89c9170..693ff68 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -121,7 +121,7 @@
           'can_use_on_swarming_builders': True,
           'expiration': 7200,
           'io_timeout': 1800,
-          'hard_timeout': 25200,
+          'hard_timeout': 36000,
           'upload_test_results': True,
           'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
           'shards': 1
@@ -171,7 +171,7 @@
           'can_use_on_swarming_builders': True,
           'expiration': 7200,
           'io_timeout': 1800,
-          'hard_timeout': 25200,
+          'hard_timeout': 36000,
           'upload_test_results': True,
           'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
           'shards': 26
@@ -220,7 +220,7 @@
           'can_use_on_swarming_builders': True,
           'expiration': 7200,
           'io_timeout': 1800,
-          'hard_timeout': 25200,
+          'hard_timeout': 36000,
           'upload_test_results': True,
           'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
           'shards': 26
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 64e36823..573a4a6 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -10,11 +10,16 @@
 
 # Benchmark: blink_perf.bindings
 crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
+crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-json-deserialize.html [ Skip ]
 crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
+crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-json-serialize.html [ Skip ]
 crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
+crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
 crbug.com/893209 [ Nexus5X_Webview ] blink_perf.bindings/structured-clone-long-string-deserialize.html [ Skip ]
 crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
+crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/structured-clone-long-string-serialize.html [ Skip ]
 crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/worker-structured-clone-json-roundtrip.html [ Skip ]
+crbug.com/910207 [ Nexus_5X ] blink_perf.bindings/worker-structured-clone-json-roundtrip.html [ Skip ]
 crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/worker-structured-clone-json-to-worker.html [ Skip ]
 crbug.com/882881 [ Nexus_5X ] blink_perf.bindings/worker-structured-clone-json-to-worker.html [ Skip ]
 crbug.com/882881 [ Nexus_5 ] blink_perf.bindings/worker-structured-clone-json-from-worker.html [ Skip ]
@@ -38,10 +43,12 @@
 # Benchmark: blink_perf.layout
 crbug.com/551950 [ Android_Svelte ] blink_perf.layout/* [ Skip ]
 crbug.com/832686 [ Nexus_5 ] blink_perf.layout/subtree-detaching.html [ Skip ]
+crbug.com/910207 [ Nexus_5X ] blink_perf.layout/subtree-detaching.html [ Skip ]
 
 # Benchmark: blink_perf.paint
 crbug.com/574483 [ Android_Svelte ] blink_perf.paint/* [ Skip ]
 crbug.com/799540 [ Nexus_5 ] blink_perf.paint/* [ Skip ]
+crbug.com/910207 [ Nexus_5X ] blink_perf.paint/* [ Skip ]
 crbug.com/859979 [ Android_Webview ] blink_perf.paint/paint-offset-changes.html [ Skip ]
 crbug.com/901493 [ Nexus6_Webview ] blink_perf.paint/* [ Skip ]
 
@@ -88,6 +95,7 @@
 # Benchmark: loading.mobile
 crbug.com/656861 [ All ] loading.mobile/G1 [ Skip ]
 crbug.com/857108 [ Nexus_5 ] loading.mobile/G1_3g [ Skip ]
+crbug.com/910207 [ Nexus_5X ] loading.mobile/G1_3g [ Skip ]
 [ Nexus_5X ] loading.mobile/Hongkiat [ Skip ]
 [ Nexus_5X ] loading.mobile/Dramaq [ Skip ]
 crbug.com/859597 [ All ] loading.mobile/Bradesco_3g [ Skip ]
@@ -105,8 +113,10 @@
 crbug.com/859597 [ All ] loading.mobile/YahooNews_3g [ Skip ]
 crbug.com/859597 [ Nexus_5 ] loading.mobile/FlipBoard_cold_3g [ Skip ]
 crbug.com/862663 [ Nexus_5 ] loading.mobile/GoogleBrazil_3g [ Skip ]
+crbug.com/910207 [ Nexus_5X ] loading.mobile/GoogleBrazil_3g [ Skip ]
 crbug.com/862663 [ Nexus_5 ] loading.mobile/GoogleIndonesia_3g [ Skip ]
 crbug.com/862663 [ Nexus_5 ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
+crbug.com/910207 [ Nexus_5X ] loading.mobile/GoogleRedirectToGoogleJapan_3g [ Skip ]
 crbug.com/859597 [ Nexus_5X ] loading.mobile/FlipBoard_cold_3g [ Skip ]
 crbug.com/859597 [ Nexus_5 ] loading.mobile/Hongkiat_3g [ Skip ]
 crbug.com/859597 [ Nexus_5X ] loading.mobile/Hongkiat_3g [ Skip ]
@@ -166,6 +176,7 @@
 crbug.com/785286 [ Android_Webview ] rendering.mobile/smash_cat [ Skip ]
 crbug.com/785286 [ Android_Webview ] rendering.mobile/effect_games [ Skip ]
 crbug.com/364248 [ Nexus_5 ] rendering.mobile/geo_apis [ Skip ]
+crbug.com/910207 [ Nexus_5X ] rendering.mobile/geo_apis [ Skip ]
 crbug.com/825234 [ Android_Webview ] rendering.mobile/bouncing_balls_shadow [ Skip ]
 crbug.com/350692 [ All ] rendering.mobile/microsoft_performance [ Skip ]
 crbug.com/755556 [ Android ] rendering.mobile/balls_css_key_frame_animations_composited_transform [ Skip ]
@@ -204,6 +215,7 @@
 crbug.com/893197 [ Nexus_5X ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
 crbug.com/865400 [ Pixel_2 ] rendering.mobile/yahoo_answers_mobile_2018 [ Skip ]
 crbug.com/874935 [ Nexus_5 ] rendering.mobile/yahoo_news_2018 [ Skip ]
+crbug.com/910207 [ Nexus_5X ] rendering.mobile/yahoo_news_2018 [ Skip ]
 crbug.com/901526 [ All ] rendering.mobile/microsoft_fireflies [ Skip ]
 
 # Benchmark: rasterize_and_record_micro.top_25
@@ -212,8 +224,9 @@
 crbug.com/873011 [ Android_Webview ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
 crbug.com/865400 [ Pixel_2 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
 crbug.com/865400 [ Nexus_5 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
-crbug.com/892223 [ Nexus_5 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
 crbug.com/865400 [ Nexus_5X ] rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html [ Skip ]
+crbug.com/892223 [ Nexus_5 ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
+crbug.com/910207 [ Nexus_5X ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
 crbug.com/875878 [ Nexus6_Webview ] rasterize_and_record_micro.top_25/file://static_top_25/yahoogames.html [ Skip ]
 
 # Benchmark: startup.mobile
@@ -271,10 +284,13 @@
 crbug.com/657433 [ Android ] system_health.memory_mobile/load:tools:gmail [ Skip ]
 crbug.com/708300 [ Android ] system_health.memory_mobile/browse:shopping:flipkart [ Skip ]
 crbug.com/784400 [ Nexus_5 ] system_health.memory_mobile/background:tools:gmail [ Skip ]
+crbug.com/910207 [ Nexus_5X ] system_health.memory_mobile/background:tools:gmail [ Skip ]
 crbug.com/780779 [ Nexus_5 ] system_health.memory_mobile/browse:social:facebook [ Skip ]
+crbug.com/910207 [ Nexus_5X ] system_health.memory_mobile/browse:social:facebook [ Skip ]
 crbug.com/738854 [ Nexus_5X ] system_health.memory_mobile/load:tools:drive [ Skip ]
 crbug.com/738854 [ Android_Webview ] system_health.memory_mobile/load:tools:drive [ Skip ]
 crbug.com/834905 [ Nexus_5 ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll [ Skip ]
+crbug.com/910207 [ Nexus_5X ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll [ Skip ]
 crbug.com/843547 [ Android_Go ] system_health.memory_mobile/background:news:nytimes [ Skip ]
 crbug.com/852888 [ Nexus_5X ] system_health.memory_mobile/background:news:nytimes [ Skip ]
 crbug.com/859500 [ Nexus_5X ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll [ Skip ]
@@ -334,6 +350,7 @@
 [ Android_Webview ] v8.browsing_mobile/browse:chrome:omnibox [ Skip ]
 [ Android_Webview ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
 crbug.com/815175 [ Nexus_5 ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
+crbug.com/910207 [ Nexus_5X ] v8.browsing_mobile/browse:chrome:newtab [ Skip ]
 crbug.com/853212 [ Android_Webview ] v8.browsing_mobile/browse:media:youtube [ Skip ]
 crbug.com/853212 [ Android_Webview ] v8.browsing_mobile/browse:news:cnn [ Skip ]
 crbug.com/877648 [ Android_Go ] v8.browsing_mobile/browse:news:toi [ Skip ]
@@ -347,6 +364,7 @@
 [ Android_Webview ] v8.browsing_mobile-future/browse:chrome:omnibox [ Skip ]
 [ Android_Webview ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
 crbug.com/803465 [ Nexus_5 ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
+crbug.com/910207 [ Nexus_5X ] v8.browsing_mobile-future/browse:chrome:newtab [ Skip ]
 crbug.com/799080 [ Nexus_5X Android_Webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
 crbug.com/799080 [ Nexus_5X Android_Webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
 crbug.com/875159 [ Win_10 ] v8.browsing_desktop-future/browse:social:facebook_infinite_scroll [ Skip ]
diff --git a/ui/base/clipboard/clipboard_win.cc b/ui/base/clipboard/clipboard_win.cc
index e49dd8c..f5d2e406 100644
--- a/ui/base/clipboard/clipboard_win.cc
+++ b/ui/base/clipboard/clipboard_win.cc
@@ -422,7 +422,7 @@
 
 // ClipboardWin implementation.
 ClipboardWin::ClipboardWin() {
-  if (base::MessageLoopForUI::IsCurrent())
+  if (base::MessageLoopCurrentForUI::IsSet())
     clipboard_owner_.reset(new base::win::MessageWindow());
 }
 
diff --git a/ui/base/ime/win/tsf_bridge.cc b/ui/base/ime/win/tsf_bridge.cc
index b5b1ba2..eab3ad63 100644
--- a/ui/base/ime/win/tsf_bridge.cc
+++ b/ui/base/ime/win/tsf_bridge.cc
@@ -131,7 +131,7 @@
 TSFBridgeImpl::TSFBridgeImpl() = default;
 
 TSFBridgeImpl::~TSFBridgeImpl() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   if (!IsInitialized())
     return;
   for (TSFDocumentMap::iterator it = tsf_document_map_.begin();
@@ -151,7 +151,7 @@
 }
 
 bool TSFBridgeImpl::Initialize() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   if (client_id_ != TF_CLIENTID_NULL) {
     DVLOG(1) << "Already initialized.";
     return false;
@@ -202,7 +202,7 @@
 }
 
 void TSFBridgeImpl::OnTextInputTypeChanged(const TextInputClient* client) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(IsInitialized());
 
   if (client != client_) {
@@ -229,7 +229,7 @@
 }
 
 bool TSFBridgeImpl::CancelComposition() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(IsInitialized());
 
   TSFDocument* document = GetAssociatedDocument();
@@ -242,7 +242,7 @@
 }
 
 bool TSFBridgeImpl::ConfirmComposition() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(IsInitialized());
 
   TSFDocument* document = GetAssociatedDocument();
@@ -256,7 +256,7 @@
 
 void TSFBridgeImpl::SetFocusedClient(HWND focused_window,
                                      TextInputClient* client) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(client);
   DCHECK(IsInitialized());
   if (attached_window_handle_ != focused_window)
@@ -276,7 +276,7 @@
 }
 
 void TSFBridgeImpl::RemoveFocusedClient(TextInputClient* client) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(IsInitialized());
   if (client_ != client)
     return;
@@ -296,7 +296,7 @@
 }
 
 Microsoft::WRL::ComPtr<ITfThreadMgr> TSFBridgeImpl::GetThreadManager() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(IsInitialized());
   return thread_manager_;
 }
@@ -492,7 +492,7 @@
 
 // static
 void TSFBridge::Initialize() {
-  if (!base::MessageLoopForUI::IsCurrent()) {
+  if (!base::MessageLoopCurrentForUI::IsSet()) {
     DVLOG(1) << "Do not use TSFBridge without UI thread.";
     return;
   }
@@ -509,7 +509,7 @@
 
 // static
 TSFBridge* TSFBridge::ReplaceForTesting(TSFBridge* bridge) {
-  if (!base::MessageLoopForUI::IsCurrent()) {
+  if (!base::MessageLoopCurrentForUI::IsSet()) {
     DVLOG(1) << "Do not use TSFBridge without UI thread.";
     return nullptr;
   }
@@ -520,7 +520,7 @@
 
 // static
 void TSFBridge::Shutdown() {
-  if (!base::MessageLoopForUI::IsCurrent()) {
+  if (!base::MessageLoopCurrentForUI::IsSet()) {
     DVLOG(1) << "Do not use TSFBridge without UI thread.";
   }
   TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get());
@@ -530,7 +530,7 @@
 
 // static
 TSFBridge* TSFBridge::GetInstance() {
-  if (!base::MessageLoopForUI::IsCurrent()) {
+  if (!base::MessageLoopCurrentForUI::IsSet()) {
     DVLOG(1) << "Do not use TSFBridge without UI thread.";
     return nullptr;
   }
diff --git a/ui/base/ime/win/tsf_input_scope.cc b/ui/base/ime/win/tsf_input_scope.cc
index d11c2073..2c0db97 100644
--- a/ui/base/ime/win/tsf_input_scope.cc
+++ b/ui/base/ime/win/tsf_input_scope.cc
@@ -158,7 +158,7 @@
 }  // namespace
 
 void InitializeTsfForInputScopes() {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   // Thread safety is not required because this function is under UI thread.
   if (!g_get_proc_done) {
     g_get_proc_done = true;
diff --git a/ui/base/material_design/material_design_controller.cc b/ui/base/material_design/material_design_controller.cc
index 2389ee9..e9700d21 100644
--- a/ui/base/material_design/material_design_controller.cc
+++ b/ui/base/material_design/material_design_controller.cc
@@ -75,7 +75,7 @@
       // Win 10+ uses dynamic mode by default and checks the current tablet mode
       // state to determine whether to start in touch mode.
       automatic_touch_ui_ = true;
-      if (base::MessageLoopForUI::IsCurrent() &&
+      if (base::MessageLoopCurrentForUI::IsSet() &&
           !GetInstance()->singleton_hwnd_observer_) {
         GetInstance()->singleton_hwnd_observer_ =
             std::make_unique<gfx::SingletonHwndObserver>(
diff --git a/ui/base/test/scoped_fake_nswindow_fullscreen.mm b/ui/base/test/scoped_fake_nswindow_fullscreen.mm
index 1b5fd3c1..977c011 100644
--- a/ui/base/test/scoped_fake_nswindow_fullscreen.mm
+++ b/ui/base/test/scoped_fake_nswindow_fullscreen.mm
@@ -106,7 +106,7 @@
     [[NSNotificationCenter defaultCenter]
         postNotificationName:NSWindowWillStartLiveResizeNotification
                       object:window];
-    DCHECK(base::MessageLoopForUI::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForUI::IsSet());
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::Bind(&Impl::FinishEnterFullscreen,
                               base::Unretained(this), fullscreen_content_size));
@@ -145,7 +145,7 @@
         postNotificationName:NSWindowWillExitFullScreenNotification
                       object:window_];
 
-    DCHECK(base::MessageLoopForUI::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForUI::IsSet());
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(&Impl::FinishExitFullscreen, base::Unretained(this)));
diff --git a/ui/base/test/ui_controls_mac.mm b/ui/base/test/ui_controls_mac.mm
index f498fbe..34f3ef9 100644
--- a/ui/base/test/ui_controls_mac.mm
+++ b/ui/base/test/ui_controls_mac.mm
@@ -265,7 +265,7 @@
                                 bool command,
                                 base::OnceClosure task) {
   CHECK(g_ui_controls_enabled);
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   std::vector<NSEvent*> events;
   SynthesizeKeyEventsSequence(window.GetNativeNSWindow(), key, control, shift,
diff --git a/ui/gfx/animation/animation_mac.mm b/ui/gfx/animation/animation_mac.mm
index e3f1b1f..99a841c 100644
--- a/ui/gfx/animation/animation_mac.mm
+++ b/ui/gfx/animation/animation_mac.mm
@@ -15,8 +15,8 @@
 bool Animation::ScrollAnimationsEnabledBySystem() {
   // Because of sandboxing, OS settings should only be queried from the browser
   // process.
-  DCHECK(base::MessageLoopForUI::IsCurrent() ||
-      base::MessageLoopForIO::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet() ||
+         base::MessageLoopCurrentForIO::IsSet());
 
   bool enabled = false;
   id value = nil;
diff --git a/ui/gfx/font_fallback_win.cc b/ui/gfx/font_fallback_win.cc
index 4d64f62c..3da6eee 100644
--- a/ui/gfx/font_fallback_win.cc
+++ b/ui/gfx/font_fallback_win.cc
@@ -306,7 +306,7 @@
   // browser process because we can use the shared system fallback, but in the
   // renderer this can cause hangs. Code that needs font fallback in the
   // renderer should instead use the font proxy.
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   // Check that we have at least as much text as was claimed. If we have less
   // text than expected then DirectWrite will become confused and crash. This
diff --git a/ui/gfx/win/singleton_hwnd.cc b/ui/gfx/win/singleton_hwnd.cc
index 7d3aa6e..d97adfa 100644
--- a/ui/gfx/win/singleton_hwnd.cc
+++ b/ui/gfx/win/singleton_hwnd.cc
@@ -27,7 +27,7 @@
 }
 
 SingletonHwnd::SingletonHwnd() {
-  if (!base::MessageLoopForUI::IsCurrent()) {
+  if (!base::MessageLoopCurrentForUI::IsSet()) {
     // Creating this window in (e.g.) a renderer inhibits shutdown on
     // Windows. See http://crbug.com/230122 and http://crbug.com/236039.
     return;
diff --git a/ui/ozone/platform/drm/gpu/drm_device.cc b/ui/ozone/platform/drm/gpu/drm_device.cc
index 8da7993..1ad207b 100644
--- a/ui/ozone/platform/drm/gpu/drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/drm_device.cc
@@ -205,19 +205,19 @@
 
  private:
   void Register() {
-    DCHECK(base::MessageLoopForIO::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForIO::IsSet());
     base::MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
         fd_, true, base::MessagePumpForIO::WATCH_READ, &controller_, this);
   }
 
   void Unregister() {
-    DCHECK(base::MessageLoopForIO::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForIO::IsSet());
     controller_.StopWatchingFileDescriptor();
   }
 
   // base::MessagePumpLibevent::FdWatcher overrides:
   void OnFileCanReadWithoutBlocking(int fd) override {
-    DCHECK(base::MessageLoopForIO::IsCurrent());
+    DCHECK(base::MessageLoopCurrentForIO::IsSet());
     TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd);
 
     if (!ProcessDrmEvent(
diff --git a/ui/ozone/platform/wayland/wayland_connection.cc b/ui/ozone/platform/wayland/wayland_connection.cc
index ead0cf3..72c8d8b 100644
--- a/ui/ozone/platform/wayland/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/wayland_connection.cc
@@ -98,7 +98,7 @@
   DCHECK(display_);
   wl_display_flush(display_.get());
 
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   if (!base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
           wl_display_get_fd(display_.get()), true,
           base::MessagePumpLibevent::WATCH_READ, &controller_, this))
@@ -111,7 +111,7 @@
 void WaylandConnection::ScheduleFlush() {
   if (scheduled_flush_ || !watching_)
     return;
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&WaylandConnection::Flush, base::Unretained(this)));
@@ -176,7 +176,7 @@
     const std::vector<uint64_t>& modifiers,
     uint32_t planes_count,
     uint32_t buffer_id) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   if (!buffer_manager_->CreateBuffer(std::move(file), width, height, strides,
                                      offsets, format, modifiers, planes_count,
                                      buffer_id)) {
@@ -185,7 +185,7 @@
 }
 
 void WaylandConnection::DestroyZwpLinuxDmabuf(uint32_t buffer_id) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   if (!buffer_manager_->DestroyBuffer(buffer_id)) {
     TerminateGpuProcess(buffer_manager_->error_message());
   }
@@ -196,7 +196,7 @@
     uint32_t buffer_id,
     const gfx::Rect& damage_region,
     ScheduleBufferSwapCallback callback) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   if (!buffer_manager_->ScheduleBufferSwap(widget, buffer_id, damage_region,
                                            std::move(callback))) {
     TerminateGpuProcess(buffer_manager_->error_message());
diff --git a/ui/snapshot/screenshot_grabber.cc b/ui/snapshot/screenshot_grabber.cc
index 73b56dd..285fd0c 100644
--- a/ui/snapshot/screenshot_grabber.cc
+++ b/ui/snapshot/screenshot_grabber.cc
@@ -73,7 +73,7 @@
 void ScreenshotGrabber::TakeScreenshot(gfx::NativeWindow window,
                                        const gfx::Rect& rect,
                                        ScreenshotCallback callback) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   last_screenshot_timestamp_ = base::TimeTicks::Now();
 
   bool is_partial = true;
@@ -106,7 +106,7 @@
     bool is_partial,
     ScreenshotCallback callback,
     scoped_refptr<base::RefCountedMemory> png_data) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
 #if defined(USE_AURA)
   cursor_hider_.reset();
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index 03d48a06..8cce8de 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -329,7 +329,7 @@
     set_views_delegate(std::move(views_delegate));
     ViewsTestBase::SetUp();
     Init();
-    ASSERT_TRUE(base::MessageLoopForUI::IsCurrent());
+    ASSERT_TRUE(base::MessageLoopCurrentForUI::IsSet());
   }
 
   void TearDown() override {
diff --git a/ui/views/controls/textfield/textfield_model.cc b/ui/views/controls/textfield/textfield_model.cc
index 1bfea071..6c63283 100644
--- a/ui/views/controls/textfield/textfield_model.cc
+++ b/ui/views/controls/textfield/textfield_model.cc
@@ -270,7 +270,7 @@
 // the default kill ring size of 1 (i.e. a single buffer) is assumed.
 base::string16* GetKillBuffer() {
   static base::NoDestructor<base::string16> kill_buffer;
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
   return kill_buffer.get();
 }
 
diff --git a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc
index d6b086c1..e65fba7 100644
--- a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc
+++ b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc
@@ -73,7 +73,7 @@
 }
 
 uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) {
-  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   // This method processes all events while the move loop is active.
   if (!in_move_loop_)